未验证 提交 8699eee3 编写于 作者: P pomian 提交者: GitHub

Merge pull request #722 from lepoco/development

Merge development branch
......@@ -111,8 +111,8 @@ dotnet_style_readonly_field = true:warning
# var preferences
csharp_style_var_elsewhere = false:warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_style_var_for_built_in_types = true:warning
csharp_style_var_when_type_is_apparent = true:warning
# Expression-bodied members
csharp_style_expression_bodied_accessors = false:silent
......@@ -361,6 +361,12 @@ dotnet_diagnostic.CA2240.severity = warning
dotnet_diagnostic.CA2241.severity = warning
dotnet_diagnostic.CA2242.severity = warning
# Stylecop Analyzers
dotnet_diagnostic.SA1111.severity = none
dotnet_diagnostic.SA1121.severity = none
dotnet_diagnostic.SA1208.severity = none
dotnet_diagnostic.SA1518.severity = none
# Require file header OR A source file contains a header that does not match the required text
dotnet_diagnostic.IDE0073.severity = error
......
......@@ -6,20 +6,19 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
target-branch: "development"
directory: "/"
schedule:
interval: "daily"
labels:
- "Actions"
# Maintain dependencies for nuget
- package-ecosystem: "nuget"
target-branch: "development"
directory: "src"
schedule:
interval: "daily"
labels:
- "NuGet"
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
target-branch: "development"
directory: "/"
schedule:
interval: "daily"
labels:
- "Actions"
# Maintain dependencies for nuget
- package-ecosystem: "nuget"
target-branch: "development"
directory: "src"
schedule:
interval: "daily"
labels:
- "NuGet"
name: wpfui-labeler
on:
- pull_request_target
jobs:
triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
name: wpfui-lock
on:
schedule:
- cron: "0 0 * * *"
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4
with:
# https://github.com/dessant/lock-threads
github-token: ${{ github.token }}
issue-inactive-days: "90"
exclude-issue-created-before: ""
exclude-any-issue-labels: "keep-unlocked, status:awaiting response"
add-issue-labels: "locked-due-to-inactivity"
issue-comment: ""
issue-lock-reason: "resolved"
name: wpf-ui-cd-docs
on:
push:
branches: [main]
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
deploy_docs:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
- name: Setup .NET Core SDK 7.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.x
- name: Install docfx
run: dotnet tool update -g docfx
- name: Install dependencies
run: dotnet restore
- name: Install template dependencies
run: npm Install
working-directory: docs/templates
- name: Build docfx template
run: npm run build
working-directory: docs/templates
- name: docfx Build
run: docfx docs/docfx.json
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: docs/_site/
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
name: wpf-ui-cd-extension
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy_extension:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: microsoft/setup-msbuild@v1.3
with:
msbuild-architecture: x64
- uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
- name: Restore dependencies
run: nuget restore Wpf.Ui.sln
- name: Build the solution
run: msbuild src\Wpf.Ui.Extension\Wpf.Ui.Extension.csproj /t:Rebuild -p:Configuration=Release -p:RestorePackages=false -p:Platform="x64" -p:GITHUB_ACTIONS=True
- uses: actions/upload-artifact@v3
with:
name: wpf-ui-vs22-extension
path: src\Wpf.Ui.Extension\bin\x64\Release\Wpf.Ui.Extension.vsix
name: wpf-ui-cd-nuget
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy_nuget:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: microsoft/setup-msbuild@v1.3
with:
msbuild-architecture: x64
- uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
- name: Setup .NET Core SDK 7.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.x
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build src\Wpf.Ui\Wpf.Ui.csproj --configuration Release --no-restore
- name: Build
run: dotnet build src\Wpf.Ui.Tray\Wpf.Ui.Tray.csproj --configuration Release --no-restore
- name: Publish the package to NuGet.org
run: nuget push **\*.nupkg -NonInteractive -SkipDuplicate -Source 'https://api.nuget.org/v3/index.json'
- name: Publish the symbols to NuGet.org
run: nuget push **\*.snupkg -NonInteractive -SkipDuplicate -Source 'https://api.nuget.org/v3/index.json'
name: wpfui-docs
on:
push:
branches: [main]
paths:
- "docs/"
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Pages
uses: actions/configure-pages@v3
- uses: nikeee/docfx-action@v1.0.0
name: Build Documentation
with:
args: docs/docfx.json
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload entire repository
path: docs/_site/
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
name: wpf-ui-labeler
on:
- pull_request_target
jobs:
triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
name: wpf-ui-lock
on:
schedule:
- cron: "0 0 * * *"
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4
with:
# https://github.com/dessant/lock-threads
github-token: ${{ github.token }}
issue-inactive-days: "90"
exclude-issue-created-before: ""
exclude-any-issue-labels: "keep-unlocked, status:awaiting response"
add-issue-labels: "locked-due-to-inactivity"
issue-comment: ""
issue-lock-reason: "resolved"
name: wpfui-pr-deploy
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: microsoft/setup-msbuild@v1.3
with:
msbuild-architecture: x64
- uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
- name: Restore dependencies
run: nuget restore Wpf.Ui.sln
- name: Build the solution
run: msbuild src\Wpf.Ui\Wpf.Ui.csproj -p:Configuration=Release -p:Platform="Any CPU" -p:GITHUB_ACTIONS=True -p:ContinuousIntegrationBuild=true -m -graph -isolate
- name: Publish the package to NuGet.org
run: nuget push **\*.nupkg -NonInteractive -SkipDuplicate -Source 'https://api.nuget.org/v3/index.json'
- name: Publish the symbols to NuGet.org
run: nuget push **\*.snupkg -NonInteractive -SkipDuplicate -Source 'https://api.nuget.org/v3/index.json'
name: wpfui-pr-validator
name: wpf-ui-pr-validator
on:
pull_request:
branches: [development]
push:
branches: [development]
pull_request:
branches: [development]
push:
branches: [development]
workflow_dispatch:
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: microsoft/setup-msbuild@v1.3
with:
msbuild-architecture: x64
- uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
matrix_build:
runs-on: windows-latest
strategy:
matrix:
project:
- Wpf.Ui
- Wpf.Ui.Tray
- Wpf.Ui.ToastNotifications
- Wpf.Ui.Gallery
steps:
- uses: actions/checkout@v3
- uses: microsoft/setup-msbuild@v1.3
with:
msbuild-architecture: x64
- uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
- name: Setup .NET Core SDK 7.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.x
- name: Restore dependencies
run: nuget restore Wpf.Ui.sln
- name: Install dependencies
run: dotnet restore
- name: Build the solution
run: msbuild src\Wpf.Ui.Gallery\Wpf.Ui.Gallery.csproj -p:Configuration=Release -p:Platform="Any CPU" -p:GITHUB_ACTIONS=True -p:ContinuousIntegrationBuild=true -m -graph -isolate
- name: Build ${{ matrix.project }}
run: dotnet build src\${{ matrix.project }}\${{ matrix.project }}.csproj --configuration Release --no-restore
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Version>3.0.0-preview.4</Version>
<Version>3.0.0-preview.5</Version>
<LangVersion>11.0</LangVersion>
<Deterministic>true</Deterministic>
</PropertyGroup>
......@@ -19,16 +19,20 @@
<Copyright>Copyright (C) 2021-2023 Leszek Pomianowski and WPF UI Contributors</Copyright>
</PropertyGroup>
<PropertyGroup>
<PackagesCommonFrameworks>net7.0-windows;net6.0-windows;net481;net472;net462</PackagesCommonFrameworks>
</PropertyGroup>
<PropertyGroup>
<PackageVersion>$(Version)</PackageVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageLicense>https://raw.githubusercontent.com/lepoco/wpfui/main/LICENSE</PackageLicense>
<PackageProjectUrl>https://github.com/lepoco/wpfui</PackageProjectUrl>
<RepositoryUrl>https://github.com/lepoco/wpfui</RepositoryUrl>
<RepositoryBranch>main</RepositoryBranch>
<RepositoryType>git</RepositoryType>
<Description>A simple way to make your application written in WPF keep up with modern design trends. Library changes the base elements like Page, ToggleButton or List, and also includes additional controls like Navigation, NumberBox, Dialog or Snackbar.</Description>
<Description>WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly.</Description>
<PackageTags>wpf ui wpfui fluent design winui windows controls custom metro modern xaml toolkit color dark theme lepo net6 net5 net</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
......@@ -37,6 +41,10 @@
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<SourceRoot Include="$(MSBuildThisFileDirectory)/"/>
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>$(MSBuildProjectName).UnitTests</_Parameter1>
......
<Project>
<ItemGroup>
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.0.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageVersion Include="coverlet.collector" Version="3.2.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
......@@ -13,9 +13,11 @@
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="NativeMethods" Version="0.0.3" />
<PackageVersion Include="PolySharp" Version="1.12.1" />
<PackageVersion Include="PolySharp" Version="1.13.2" />
<PackageVersion Include="WpfAnalyzers" Version="4.1.1" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
<PackageVersion Include="xunit" Version="2.4.2" />
</ItemGroup>
......
......@@ -54,6 +54,8 @@ https://marketplace.visualstudio.com/items?itemName=lepo.wpf-ui
![Demo App Sample](https://user-images.githubusercontent.com/13592821/166259110-0fb98120-fe34-4e6d-ab92-9f72ad7113c3.png)
![Monaco Editor](https://user-images.githubusercontent.com/13592821/258610583-7d71f69d-45b3-4be6-bcb8-8cf6cd60a2ff.png)
![Text Editor Sample](https://user-images.githubusercontent.com/13592821/165918838-a65cbb86-4fc4-4efb-adb7-e39027fb661f.png)
![Store App Sample](https://user-images.githubusercontent.com/13592821/165918914-6948fb42-1ee1-4c36-870e-65bb8ffe3c8a.png)
......
此差异已折叠。
......@@ -7,3 +7,5 @@
/**/bin/
/**/obj/
_site
public
/src/
......@@ -3,13 +3,13 @@
{
"src": [
{
"files": ["src/Wpf.Ui/*.csproj"],
"files": ["src/Wpf.Ui/*.csproj", "src/Wpf.Ui.Tray/*.csproj"],
"src": "../"
}
],
"dest": "api",
"properties": {
"TargetFramework": "net7.0"
"TargetFramework": "net472"
},
"disableGitFeatures": false,
"disableDefaultFilter": false
......@@ -43,7 +43,7 @@
"_appFooter": "<span>Made with <a href=\"https://dotnet.github.io/docfx\" rel=\"noreferrer\">docfx</a>, <a href=\"https://chat.openai.com/\" rel=\"noreferrer\">ChatGPT</a> and <a href=\"https://www.deepl.com/\" rel=\"noreferrer\">DeepL</a> | Copyright © 2023 <a href=\"https://dev.lepo.co/\">lepo.co</a></span>"
},
"dest": "_site",
"template": ["default", "modern", "template"],
"template": ["default", "templates/wpfui"],
"globalMetadataFiles": [],
"fileMetadataFiles": [],
"markdownEngineName": "markdig",
......
# Tutorial
# WPF UI Docs
**WPF UI** is a library built for [Windows Presentation Foundation (WPF)](https://docs.microsoft.com/en-us/visualstudio/designers/getting-started-with-wpf) and the [C#](https://docs.microsoft.com/en-us/dotnet/csharp/) language.
To be able to work with them comfortably, you will need:
......@@ -21,14 +21,14 @@ You can install **WPF UI**, the library for the Windows Presentation Foundation
- Use the **NuGet** package manager.
We recommend using the **NuGet** package manager, it allows you to easily install and update your application dependencies.
More information on how to install **WPF UI** using **NuGet** [can be found here](/tutorial/nuget.html).
More information on how to install **WPF UI** using **NuGet** [can be found here](/documentation/nuget.html).
## Extension for Visual Studio
Creators of **WPF UI** have prepared a special plugin that will automatically create a project based on **WPF UI**, Dependency Injection and MVVM, thanks to which you will quickly and easily start a new apps.
[Learn more about the WPF UI plug-in for Visual Studio 2022](/tutorial/extension.html)
[Learn more about the WPF UI plug-in for Visual Studio 2022](/documentation/extension.html)
## Getting started
Once you have chosen how to install **WPF UI**, you can move on to creating your first app, more on this in [Getting Started](/tutorial/getting-started.html).
Once you have chosen how to install **WPF UI**, you can move on to creating your first app, more on this in [Getting Started](/documentation/getting-started.html).
# Navigation
# Navigation View
**WPF UI** implements a variety of navigation related controls. You can use them to conveniently manage the pages of your application.
......@@ -27,89 +27,45 @@ You can also navigate using an index like an array.
RootNavigation.Navigate(2);
```
## NavigationStore
It is similar to the navigation from the Windows Store.
```xml
<ui:NavigationStore
x:Name="RootNavigation"
Frame="{Binding ElementName=RootFrame, Mode=OneWay}">
<ui:NavigationStore.Items>
<ui:NavigationItem
Content="Home"
Icon="Home24"
PageType="{x:Type pages:Dashboard}"
PageTag="dashboard" />
</ui:NavigationStore.Items>
<ui:NavigationStore.Footer>
<ui:NavigationItem
Content="Settings"
Icon="Diversity24"
PageType="{x:Type pages:Settings}" />
<!-- A navigation element that does not point to the page can be used as a button. -->
<ui:NavigationItem
Click="NavigationButtonTheme_OnClick"
Content="Theme"
Icon="DarkTheme24" />
</ui:NavigationStore.Footer>
</ui:NavigationStore>
```
## NavigationCompact
It is similar to the navigation from the Windows 11 Task Manager.
## NavigationView
```xml
<ui:NavigationCompact
x:Name="RootNavigation"
Frame="{Binding ElementName=RootFrame}">
<ui:NavigationCompact.Items>
<ui:NavigationItem
Content="Home"
Icon="Home24"
PageType="{x:Type pages:Dashboard}"
PageTag="dashboard" />
</ui:NavigationCompact.Items>
<ui:NavigationCompact.Footer>
<ui:NavigationItem
Content="Settings"
Icon="Diversity24"
PageType="{x:Type pages:Settings}" />
<!-- A navigation element that does not point to the page can be used as a button. -->
<ui:NavigationItem
Click="NavigationButtonTheme_OnClick"
Content="Theme"
Icon="DarkTheme24" />
</ui:NavigationCompact.Footer>
</ui:NavigationCompact>
<ui:NavigationView x:Name="RootNavigation" Grid.Row="1">
<ui:NavigationView.AutoSuggestBox>
<ui:AutoSuggestBox x:Name="AutoSuggestBox" PlaceholderText="Search">
<ui:AutoSuggestBox.Icon>
<ui:IconSourceElement>
<ui:SymbolIconSource Symbol="Search24" />
</ui:IconSourceElement>
</ui:AutoSuggestBox.Icon>
</ui:AutoSuggestBox>
</ui:NavigationView.AutoSuggestBox>
<ui:NavigationView.Header>
<ui:BreadcrumbBar
Margin="42,32,0,0"
FontSize="28"
FontWeight="DemiBold" />
</ui:NavigationView.Header>
<ui:NavigationView.MenuItems>
<ui:NavigationViewItem Content="Dashboard" TargetPageType="{x:Type pages:DashboardPage}">
<ui:NavigationViewItem.Icon>
<ui:SymbolIcon Symbol="Home24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="Data" TargetPageType="{x:Type pages:DataPage}">
<ui:NavigationViewItem.Icon>
<ui:SymbolIcon Symbol="DataHistogram24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
</ui:NavigationView.MenuItems>
<ui:NavigationView.FooterMenuItems>
<ui:NavigationViewItem Content="Settings" TargetPageType="{x:Type pages:SettingsPage}">
<ui:NavigationViewItem.Icon>
<ui:SymbolIcon Symbol="Settings24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
</ui:NavigationView.FooterMenuItems>
</ui:NavigationView>
```
## NavigationFluent
It is similar to the navigation from the Windows 11 Settings app.
```xml
<ui:NavigationFluent
x:Name="RootNavigation"
Frame="{Binding ElementName=RootFrame}">
<ui:NavigationFluent.Items>
<ui:NavigationItem
Content="Home"
Icon="Home24"
Page="{x:Type pages:Dashboard}"
PageTag="dashboard" />
</ui:NavigationFluent.Items>
<ui:NavigationFluent.Footer>
<ui:NavigationItem
Content="Settings"
Icon="Diversity24"
Page="{x:Type pages:Settings}" />
<!-- A navigation element that does not point to the page can be used as a button. -->
<ui:NavigationItem
Click="NavigationButtonTheme_OnClick"
Content="Theme"
Icon="DarkTheme24" />
</ui:NavigationFluent.Footer>
</ui:NavigationFluent>
```
## Pane display mode
<svg xmlns="http://www.w3.org/2000/svg" fill="#777777" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<polygon id="path-1" points="0 46.021103 0 3.7002935 84.6521577 3.7002935 84.6521577 88.3419125 0 88.3419125"></polygon>
</defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="nuget">
<g id="Global/Logo" transform="translate(0.000000, 6.000000)">
<path d="M374.424959,454.856991 C327.675805,454.856991 289.772801,416.950177 289.772801,370.196324 C289.772801,323.463635 327.675805,285.535656 374.424959,285.535656 C421.174113,285.535656 459.077116,323.463635 459.077116,370.196324 C459.077116,416.950177 421.174113,454.856991 374.424959,454.856991 M205.565067,260.814741 C176.33891,260.814741 152.657469,237.109754 152.657469,207.901824 C152.657469,178.672728 176.33891,154.988907 205.565067,154.988907 C234.791225,154.988907 258.472666,178.672728 258.472666,207.901824 C258.472666,237.109754 234.791225,260.814741 205.565067,260.814741 M378.170817,95.6417786 L236.886365,95.6417786 C164.889705,95.6417786 106.479717,154.057639 106.479717,226.082702 L106.479717,367.360191 C106.479717,439.40642 164.889705,497.77995 236.886365,497.77995 L378.170817,497.77995 C450.209803,497.77995 508.577466,439.40642 508.577466,367.360191 L508.577466,226.082702 C508.577466,154.057639 450.209803,95.6417786 378.170817,95.6417786" id="Fill-12" fill="#777777" fill-rule="evenodd"></path>
<mask id="mask-2" fill="transparent">
<use xlink:href="#path-1"></use>
</mask>
<g id="Clip-15"></g>
<path d="M84.6521577,46.0115787 C84.6521577,69.3990881 65.6900744,88.3419125 42.3260788,88.3419125 C18.9409203,88.3419125 0,69.3990881 0,46.0115787 C0,22.6452344 18.9409203,3.68124485 42.3260788,3.68124485 C65.6900744,3.68124485 84.6521577,22.6452344 84.6521577,46.0115787" id="Fill-14" fill="#777777" fill-rule="evenodd" mask="url(#mask-2)"></path>
</g>
</g>
</g>
</svg>
<svg width="193" height="193" viewBox="0 0 193 193" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<g clip-path="url(#clip1)">
<mask id="mask0" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="177" height="177">
<path d="M40.6211 157.524L11.2134 135.467C10.2965 134.786 9.55244 133.899 9.04142 132.877C8.5304 131.855 8.26668 130.728 8.27155 129.586V63.4144C8.26668 62.2721 8.5304 61.1447 9.04142 60.1232C9.55244 59.1016 10.2965 58.2145 11.2134 57.5334L40.6211 35.4762C39.8311 36.218 39.2341 37.1414 38.8819 38.1663C38.5297 39.1912 38.433 40.2865 38.6001 41.3572V151.643C38.433 152.714 38.5297 153.809 38.8819 154.834C39.2341 155.859 39.8311 156.782 40.6211 157.524Z" fill="white"/>
<path d="M181.456 38.8951C180.244 38.0992 178.825 37.6773 177.375 37.682C176.436 37.6744 175.504 37.8522 174.634 38.2052C173.763 38.5581 172.971 39.0793 172.302 39.7388L171.789 40.2544L113.117 96.5001L80.6548 127.637L50.6212 156.421L50.1057 156.937C49.4375 157.597 48.6453 158.119 47.7747 158.473C46.904 158.826 45.9721 159.004 45.0325 158.996C43.4419 158.993 41.8948 158.477 40.6211 157.524L11.2134 135.467C12.7075 136.589 14.5731 137.1 16.4305 136.895C18.2879 136.69 19.9973 135.785 21.2108 134.364L53.8554 96.4642L80.6548 65.3637L127.601 10.8439C128.282 10.0365 129.132 9.38792 130.091 8.94356C131.05 8.4992 132.094 8.26983 133.151 8.27152C134.601 8.26684 136.02 8.68874 137.231 9.48467L181.456 38.8951Z" fill="white"/>
<path d="M181.456 154.105L137.342 183.515C135.841 184.495 134.037 184.901 132.261 184.658C130.486 184.415 128.857 183.539 127.675 182.192L80.6548 127.637L53.8554 96.4642L21.2108 58.5975C19.9865 57.1909 18.2755 56.2994 16.4211 56.102C14.5668 55.9046 12.7064 56.416 11.2134 57.5334L40.6211 35.4762C41.8948 34.5234 43.4419 34.007 45.0325 34.0038C45.9721 33.9959 46.904 34.1738 47.7747 34.5272C48.6453 34.8807 49.4375 35.4028 50.1057 36.0634L50.6212 36.579L80.6548 65.3637L113.117 96.5001L171.789 152.746L172.302 153.261C172.971 153.921 173.763 154.442 174.634 154.795C175.504 155.148 176.436 155.326 177.375 155.318C178.825 155.323 180.244 154.901 181.456 154.105Z" fill="white"/>
<path d="M184.729 45.0325V147.968C184.73 149.181 184.432 150.376 183.861 151.446C183.29 152.517 182.464 153.43 181.456 154.105L137.342 183.515C138.35 182.84 139.176 181.926 139.747 180.855C140.317 179.784 140.615 178.589 140.614 177.375V15.6248C140.598 14.4022 140.281 13.2025 139.691 12.1316C139.101 11.0607 138.256 10.1516 137.231 9.48467L181.456 38.8951C182.461 39.5728 183.286 40.4866 183.856 41.5565C184.427 42.6265 184.726 43.8199 184.729 45.0325Z" fill="white"/>
</mask>
<g mask="url(#mask0)">
<path d="M40.6211 157.524L11.2134 135.467C10.2965 134.786 9.55244 133.898 9.04142 132.877C8.5304 131.855 8.26668 130.728 8.27155 129.586V63.4143C8.26668 62.272 8.5304 61.1446 9.04142 60.1231C9.55244 59.1015 10.2965 58.2144 11.2134 57.5333L40.6211 35.4761C39.8311 36.2179 39.2341 37.1413 38.8819 38.1662C38.5297 39.1911 38.433 40.2864 38.6001 41.3571V151.643C38.433 152.714 38.5297 153.809 38.8819 154.834C39.2341 155.859 39.8311 156.782 40.6211 157.524Z" fill="#5E438F"/>
<g filter="url(#filter0_dd)">
<path d="M181.455 38.8951C180.244 38.0992 178.825 37.6773 177.375 37.682C176.436 37.6744 175.504 37.8522 174.633 38.2052C173.763 38.5581 172.971 39.0793 172.302 39.7388L171.789 40.2544L140.539 70.2163L113.117 96.5001L80.6543 127.637L50.6207 156.421L50.1052 156.937C49.437 157.597 48.6448 158.119 47.7742 158.473C46.9035 158.826 45.9716 159.004 45.032 158.996C43.4414 158.993 41.8943 158.477 40.6206 157.524L11.2129 135.467C12.7069 136.589 14.5726 137.1 16.43 136.895C18.2874 136.69 19.9968 135.785 21.2103 134.364L37.6815 115.249L53.8549 96.4643L80.6543 65.3637L127.6 10.8439C128.282 10.0365 129.132 9.38792 130.091 8.94356C131.049 8.4992 132.094 8.26983 133.15 8.27152C134.6 8.26684 136.019 8.68874 137.231 9.48467L181.455 38.8951Z" fill="url(#paint0_linear)"/>
</g>
<g filter="url(#filter1_dd)">
<path d="M181.455 154.105L137.341 183.516C135.841 184.496 134.037 184.901 132.261 184.658C130.485 184.415 128.857 183.54 127.675 182.192L80.6543 127.637L53.8549 96.4644L37.6815 77.7158L21.2103 58.5978C19.986 57.1911 18.2749 56.2997 16.4206 56.1022C14.5663 55.9048 12.7059 56.4162 11.2129 57.5335L40.6206 35.4764C41.8943 34.5236 43.4414 34.0072 45.032 34.0041C45.9716 33.9961 46.9035 34.174 47.7742 34.5275C48.6448 34.8809 49.437 35.403 50.1052 36.0637L50.6207 36.5792L80.6543 65.3638L113.117 96.5002L140.578 122.823L171.789 152.746L172.302 153.262C172.971 153.921 173.763 154.442 174.633 154.795C175.504 155.148 176.436 155.326 177.375 155.318C178.825 155.323 180.244 154.901 181.455 154.105Z" fill="url(#paint1_linear)"/>
</g>
<g filter="url(#filter2_dd)">
<path d="M184.729 45.0321V147.967C184.73 149.181 184.432 150.375 183.861 151.446C183.29 152.516 182.464 153.43 181.456 154.105L137.341 183.515C138.35 182.84 139.176 181.926 139.746 180.855C140.317 179.784 140.615 178.589 140.614 177.375V15.6244C140.598 14.4019 140.281 13.2021 139.691 12.1312C139.101 11.0603 138.256 10.1512 137.231 9.48428L181.456 38.8947C182.461 39.5724 183.285 40.4862 183.856 41.5562C184.426 42.6261 184.726 43.8196 184.729 45.0321Z" fill="url(#paint2_linear)"/>
</g>
</g>
</g>
</g>
<defs>
<filter id="filter0_dd" x="6.03194" y="4.38577" width="180.604" height="161.087" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.246095"/>
<feGaussianBlur stdDeviation="0.246095"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1.29524"/>
<feGaussianBlur stdDeviation="2.59048"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.22 0"/>
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
</filter>
<filter id="filter1_dd" x="6.03194" y="30.1181" width="180.604" height="161.085" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.246095"/>
<feGaussianBlur stdDeviation="0.246095"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1.29524"/>
<feGaussianBlur stdDeviation="2.59048"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.22 0"/>
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
</filter>
<filter id="filter2_dd" x="121.688" y="-6.05857" width="78.583" height="205.117" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.129524"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="7.77143"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0"/>
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
</filter>
<linearGradient id="paint0_linear" x1="151.642" y1="8.27149" x2="20.6141" y2="140.148" gradientUnits="userSpaceOnUse">
<stop stop-color="#7252AA"/>
<stop offset="1" stop-color="#7252AA"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="24.8145" y1="40.4371" x2="147.966" y2="178.293" gradientUnits="userSpaceOnUse">
<stop stop-color="#AE7FE2"/>
<stop offset="1" stop-color="#9A70D4"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="160.457" y1="5.93579" x2="160.457" y2="183.367" gradientUnits="userSpaceOnUse">
<stop stop-color="#D59DFF"/>
<stop offset="1" stop-color="#C18EF1"/>
</linearGradient>
<clipPath id="clip0">
<rect width="176.457" height="176.457" fill="white" transform="translate(8.27148 8.27148)"/>
</clipPath>
<clipPath id="clip1">
<rect width="176.457" height="176.457" fill="white" transform="translate(8.27148 8.27148)"/>
</clipPath>
</defs>
</svg>
此差异由.gitattributes 抑制。
此差异由.gitattributes 抑制。
此差异已折叠。
......@@ -6,7 +6,7 @@
"start_url": "/",
"name": "WPF UI",
"short_name": "WPF UI",
"description": "A simple way to make your application written in WPF keep up with modern design trends.",
"description": "WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly.",
"icons": [
{
"src": "images/icon-192x192.png",
......@@ -29,4 +29,4 @@
"type": "image/png"
}
]
}
\ No newline at end of file
}
/* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. */
/* Checkout https://getbootstrap.com/docs/5.3/customize/color/ for more customization options */
body {
--bs-link-color-rgb: 66, 184, 131 !important;
--bs-link-hover-color-rgb: 64, 180, 128 !important;
}
.navbar-brand img {
height: 35px;
margin-right: 15px;
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
module.exports = {
env: {
browser: true
},
ignorePatterns: ['**/*.js'],
extends: ['standard', 'eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
root: true,
rules: {
'space-before-function-paren': ['warn', 'never'],
}
};
*.min.js
*.min.css
*.map
*.woff
*.woff2
glyphicons-*.*
dist
fonts
node_modules
{
"extends": "stylelint-config-standard-scss",
"ignoreFiles": [
"**/*.ts"
],
"rules": {
"selector-class-pattern": null,
"font-family-no-missing-generic-family-keyword": [ true, {
"ignoreFontFamilies": [
"bootstrap-icons"
]
}]
}
}
This folder contains the source code for website themes used by docfx.exe.
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
const esbuild = require('esbuild')
const { sassPlugin } = require('esbuild-sass-plugin')
const bs = require('browser-sync')
const { cpSync, rmSync } = require('fs')
const { join } = require('path')
const { spawnSync } = require('child_process')
const yargs = require('yargs/yargs')
const { hideBin } = require('yargs/helpers')
const argv = yargs(hideBin(process.argv)).argv
const watch = argv.watch
const project = argv.project || '../samples/seed'
const distdir = '../src/Docfx.App/templates'
const loader = {
'.eot': 'file',
'.svg': 'file',
'.ttf': 'file',
'.woff': 'file',
'.woff2': 'file'
}
build()
async function build() {
await buildWpfUiTemplate();
copyToDist()
if (watch) {
serve()
}
}
async function buildWpfUiTemplate() {
const config = {
bundle: true,
format: 'esm',
splitting: true,
minify: true,
sourcemap: true,
outExtension: {
'.css': '.min.css',
'.js': '.min.js'
},
outdir: 'wpfui/public',
entryPoints: [
'wpfui/src/docfx.ts',
'wpfui/src/search-worker.ts',
],
plugins: [
sassPlugin()
],
loader,
}
if (watch) {
const context = await esbuild.context(config)
await context.watch()
} else {
await esbuild.build(config)
}
}
function copyToDist() {
rmSync(distdir, { recursive: true, force: true })
cpSync('wpfui', join(distdir, 'wpfui'), { recursive: true, overwrite: true, filter })
function filter(src) {
const segments = src.split(/[/\\]/)
return !segments.includes('node_modules') && !segments.includes('package-lock.json') && !segments.includes('src')
}
function staticTocFilter(src) {
return filter(src) && !src.includes('toc.html')
}
}
此差异已折叠。
{
"name": "@docfx/template",
"version": "1.0.0",
"description": "Docfx static website templates",
"keywords": [
"docfx",
"template"
],
"author": "docfx",
"license": "MIT",
"browserslist": [
"defaults"
],
"scripts": {
"build": "node build.js",
"lint": "eslint wpfui/src && stylelint wpfui/src"
},
"dependencies": {
"@default/anchor-js": "npm:anchor-js@5.0.0",
"@default/bootstrap": "npm:bootstrap@3.4.1",
"@default/highlight.js": "npm:highlight.js@11.8.0",
"@default/lunr": "npm:lunr@2.3.9",
"@default/mark.js": "npm:mark.js@8.11.1",
"@default/twbs-pagination": "josecebe/twbs-pagination#1.3.1",
"@default/url": "npm:@websanova/url@2.6.3",
"@websanova/url": "^2.6.3",
"anchor-js": "^5.0.0",
"bootstrap": "^5.3.1",
"bootstrap-icons": "^1.10.5",
"highlight.js": "^11.8.0",
"jquery": "3.7.0",
"lit-html": "^2.8.0",
"lunr": "2.3.9",
"mathjax": "^3.2.2",
"mermaid": "^10.3.0"
},
"devDependencies": {
"@types/jest": "^29.5.3",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"browser-sync": "^2.29.3",
"esbuild": "~0.18.17",
"esbuild-sass-plugin": "~2.10.0",
"eslint": "^8.46.0",
"eslint-config-standard": "^17.1.0",
"jest": "^29.6.2",
"stylelint": "^15.10.2",
"stylelint-config-standard-scss": "^10.0.0",
"ts-jest": "^29.1.1",
"typescript": "^5.1.6",
"yargs": "^17.7.2"
}
}
{
"compilerOptions": {
"removeComments": true,
"resolveJsonModule": true,
"esModuleInterop": true
}
}
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
{{!include(/^public/.*/)}}
{{!include(favicon.ico)}}
{{!include(logo.svg)}}
{{!include(search-stopwords.json)}}
<!DOCTYPE html>
<html {{#_lang}}lang="{{_lang}}"{{/_lang}}>
<head>
<meta charset="utf-8">
{{#redirect_url}}
<meta http-equiv="refresh" content="0;URL='{{redirect_url}}'">
{{/redirect_url}}
{{^redirect_url}}
<title>{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}</title>
<link rel="dns-prefetch" href="//fonts.googleapis.com" />
<link rel="dns-prefetch" href="//fonts.gstatic.com" />
<meta property="og:locale" content="en_US" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="wpfui.lepo.co" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
<link rel="icon" href="{{_rel}}{{{_appFaviconPath}}}{{^_appFaviconPath}}favicon.ico{{/_appFaviconPath}}">
<link rel="stylesheet" href="{{_rel}}public/docfx.min.css">
<link rel="stylesheet" href="{{_rel}}public/main.css">
<meta name="docfx:navrel" content="{{_navRel}}">
<meta name="docfx:tocrel" content="{{_tocRel}}">
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}
{{#_enableSearch}}<meta name="docfx:rel" content="{{_rel}}">{{/_enableSearch}}
{{#_disableNewTab}}<meta name="docfx:disablenewtab" content="true">{{/_disableNewTab}}
{{#_disableTocFilter}}<meta name="docfx:disabletocfilter" content="true">{{/_disableTocFilter}}
{{#docurl}}<meta name="docfx:docurl" content="{{docurl}}">{{/docurl}}
{{/redirect_url}}
</head>
{{^redirect_url}}
<script type="module">
import options from './{{_rel}}public/main.js'
import { init } from './{{_rel}}public/docfx.min.js'
init(options)
</script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
{{#_googleAnalyticsTagId}}
<script async src="https://www.googletagmanager.com/gtag/js?id={{_googleAnalyticsTagId}}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', '{{_googleAnalyticsTagId}}');
</script>
{{/_googleAnalyticsTagId}}
<body class="tex2jax_ignore" data-layout="{{_layout}}{{layout}}" data-yaml-mime="{{yamlmime}}">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="{{_appLogoUrl}}{{^_appLogoUrl}}{{_rel}}index.html{{/_appLogoUrl}}">
<img id="logo" class="svg" src="{{_rel}}{{{_appLogoPath}}}{{^_appLogoPath}}logo.svg{{/_appLogoPath}}" alt="{{_appName}}" >
{{_appName}}
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
{{#_enableSearch}}
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="{{__global.search}}" autocomplete="off" aria-label="Search">
</form>
{{/_enableSearch}}
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" style="margin-top: -.65em; margin-left: -.8em"
type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas"
aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="{{uid}}">
{{!body}}
</article>
{{^_disableContribution}}
<div class="contribution d-print-none">
{{#sourceurl}}
<a href="{{sourceurl}}" class="edit-link">Edit this page</a>
{{/sourceurl}}
{{^sourceurl}}{{#docurl}}
<a href="{{docurl}}" class="edit-link">Edit this page</a>
{{/docurl}}{{/sourceurl}}
</div>
{{/_disableContribution}}
{{^_disableNextArticle}}
<div class="next-article d-print-none border-top" id="nextArticle"></div>
{{/_disableNextArticle}}
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
{{#_enableSearch}}
<div class="container-xxl search-results" id="search-results"></div>
{{/_enableSearch}}
<footer class="border-top">
<div class="container-xxl">
<div class="row"><div class="col-12">WPF UI<a target="_blank" rel="noopener nofollow noreferrer" href="https://github.com/lepoco/wpfui"><code> https://github.com/lepoco/wpfui </code></a></div><div class="col-12">Build with<a target="_blank" rel="noopener nofollow noreferrer" href="https://dotnet.github.io/docfx/"><code> docfx </code></a>using <a target="_blank" rel="noopener nofollow noreferrer" href="https://www.deepl.com/en/translator"><code> deepl</code></a><a target="_blank" rel="noopener nofollow noreferrer" href="https://highlightjs.org/"><code> highlight.js</code></a><a target="_blank" rel="noopener nofollow noreferrer" href="https://getbootstrap.com/"><code> bootstrap</code></a><a target="_blank" rel="noopener nofollow noreferrer" href="https://github.com/microsoft/fluentui-system-icons"><code> fluent-system-icons</code></a></div><div class="col-12">Copyright © 2023 lepo.co | Leszek Pomianowski</div></div>
</div>
</footer>
</body>
{{/redirect_url}}
</html>
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
<h1 id="{{id}}" data-uid="{{uid}}" class="text-break">
{{>partials/title}}
{{#sourceurl}}<a class="header-action link-secondary" title="View source" href="{{sourceurl}}"><i class="bi bi-code-slash"></i></a>{{/sourceurl}}
</h1>
<div class="facts text-secondary">
<dl><dt>{{__global.namespace}}</dt><dd>{{{namespace.specName.0.value}}}</dd></dl>
{{#assemblies.0}}<dl><dt>{{__global.assembly}}</dt><dd>{{assemblies.0}}.dll</dd></dl>{{/assemblies.0}}
</div>
<div class="markdown summary">{{{summary}}}</div>
<div class="markdown conceptual">{{{conceptual}}}</div>
{{#syntax.content.0.value}}
<div class="codewrapper">
<pre><code class="lang-csharp hljs">{{syntax.content.0.value}}</code></pre>
</div>
{{/syntax.content.0.value}}
{{#syntax.parameters.0}}
<h4 class="section">{{__global.parameters}}</h4>
<dl class="parameters">
{{/syntax.parameters.0}}
{{#syntax.parameters}}
<dt><code>{{{id}}}</code> {{{type.specName.0.value}}}</dt>
<dd>{{{description}}}</dd>
{{/syntax.parameters}}
{{#syntax.parameters.0}}
</dl>
{{/syntax.parameters.0}}
{{#syntax.return}}
<h4 class="section">{{__global.returns}}</h4>
<dl class="parameters">
<dt>{{{type.specName.0.value}}}</dt>
<dd>{{{description}}}</dd>
</dl>
{{/syntax.return}}
{{#syntax.typeParameters.0}}
<h4 class="section">{{__global.typeParameters}}</h4>
<dl class="parameters">
{{/syntax.typeParameters.0}}
{{#syntax.typeParameters}}
<dt><code>{{{id}}}</code></dt>
<dd>{{{description}}}</dd>
{{/syntax.typeParameters}}
{{#syntax.typeParameters.0}}
</dl>
{{/syntax.typeParameters.0}}
{{#inClass}}
{{#inheritance.0}}
<dl class="typelist inheritance">
<dt>{{__global.inheritance}}</dt>
<dd>
{{/inheritance.0}}
{{#inheritance}}
<div>{{{specName.0.value}}}</div>
{{/inheritance}}
<div><span class="xref">{{name.0.value}}</span></div>
{{#inheritance.0}}
</dd>
</dl>
{{/inheritance.0}}
{{/inClass}}
{{#implements.0}}
<dl class="typelist implements">
<dt>{{__global.implements}}</dt>
<dd>
{{/implements.0}}
{{#implements}}
<div>{{{specName.0.value}}}</div>
{{/implements}}
{{#implements.0}}
</dd>
</dl>
{{/implements.0}}
{{#inClass}}
{{#derivedClasses.0}}
<dl class="typelist derived">
<dt>{{__global.derived}}</dt>
<dd>
{{/derivedClasses.0}}
{{#derivedClasses}}
<div>{{{specName.0.value}}}</div>
{{/derivedClasses}}
{{#derivedClasses.0}}
</dd>
</dl>
{{/derivedClasses.0}}
{{/inClass}}
{{#inheritedMembers.0}}
<dl class="typelist derived">
<dt>{{__global.inheritedMembers}}</dt>
<dd>
{{/inheritedMembers.0}}
{{#inheritedMembers}}
<div>
{{#definition}}
<xref uid="{{definition}}" text="{{nameWithType.0.value}}" alt="{{fullName.0.value}}"/>
{{/definition}}
{{^definition}}
<xref uid="{{uid}}" text="{{nameWithType.0.value}}" alt="{{fullName.0.value}}"/>
{{/definition}}
</div>
{{/inheritedMembers}}
{{#inheritedMembers.0}}
</dl>
{{/inheritedMembers.0}}
{{#extensionMethods.0}}
<dl class="typelist extensionMethods">
<dt>{{__global.extensionMethods}}</dt>
<dd>
{{/extensionMethods.0}}
{{#extensionMethods}}
<div>
{{#definition}}
<xref uid="{{definition}}" altProperty="fullName" displayProperty="nameWithType"/>
{{/definition}}
{{^definition}}
<xref uid="{{uid}}" altProperty="fullName" displayProperty="nameWithType"/>
{{/definition}}
</div>
{{/extensionMethods}}
{{#extensionMethods.0}}
</dl>
{{/extensionMethods.0}}
{{#isEnum}}
{{#children}}
<h2 id="{{id}}">{{>partials/classSubtitle}}</h2>
<dl class="parameters">
{{#children}}
<dt id="{{id}}"><code>{{syntax.content.0.value}}</code></dt>
<dd>{{{summary}}}</dd>
{{/children}}
</dl>
{{/children}}
{{/isEnum}}
{{#example.0}}
<h2 id="{{id}}_examples">{{__global.examples}}</h2>
{{/example.0}}
{{#example}}
{{{.}}}
{{/example}}
{{#remarks}}
<h2 id="{{id}}_remarks">{{__global.remarks}}</h2>
<div class="markdown level0 remarks">{{{remarks}}}</div>
{{/remarks}}
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
{{>partials/class.header}}
{{#children}}
<h2 class="section" id="{{id}}">{{>partials/classSubtitle}}</h2>
{{#children}}
<dl class="jumplist">
<dt><xref uid="{{uid}}" altProperty="fullName" displayProperty="name"/></dt>
<dd>{{{summary}}}</dd>
</dl>
{{/children}}
{{/children}}
{{#seealso.0}}
<h2 id="seealso">{{__global.seealso}}</h2>
<div class="seealso">
{{/seealso.0}}
{{#seealso}}
{{#isCref}}
<div>{{{type.specName.0.value}}}</div>
{{/isCref}}
{{^isCref}}
<div>{{{url}}}</div>
{{/isCref}}
{{/seealso}}
{{#seealso.0}}
</div>
{{/seealso.0}}
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
{{>partials/class.header}}
{{#children}}
{{^_splitReference}}
<h2 class="section" id="{{id}}">{{>partials/classSubtitle}}</h2>
{{/_splitReference}}
{{#children}}
{{#overload}}
<a id="{{id}}" data-uid="{{uid}}"></a>
{{/overload}}
<h3 id="{{id}}" data-uid="{{uid}}">
{{name.0.value}}
{{#sourceurl}}<a class="header-action link-secondary" title="View source" href="{{sourceurl}}"><i class="bi bi-code-slash"></i></a>{{/sourceurl}}
</h3>
<div class="markdown level1 summary">{{{summary}}}</div>
<div class="markdown level1 conceptual">{{{conceptual}}}</div>
{{#syntax}}
<div class="codewrapper">
<pre><code class="lang-csharp hljs">{{syntax.content.0.value}}</code></pre>
</div>
{{#syntax.parameters.0}}
<h4 class="section">{{__global.parameters}}</h4>
<dl class="parameters">
{{/syntax.parameters.0}}
{{#syntax.parameters}}
<dt><code>{{{id}}}</code> {{{type.specName.0.value}}}</dt>
<dd>{{{description}}}</dd>
{{/syntax.parameters}}
{{#syntax.parameters.0}}
</dl>
{{/syntax.parameters.0}}
{{#syntax.return}}
<h4 class="section">{{__global.returns}}</h4>
<dl class="parameters">
<dt>{{{type.specName.0.value}}}</dt>
<dd>{{{description}}}</dd>
</dl>
{{/syntax.return}}
{{#syntax.typeParameters.0}}
<h4 class="section">{{__global.typeParameters}}</h4>
<dl class="parameters">
{{/syntax.typeParameters.0}}
{{#syntax.typeParameters}}
<dt><code>{{{id}}}</code></dt>
<dd>{{{description}}}</dd>
{{/syntax.typeParameters}}
{{#syntax.typeParameters.0}}
</dl>
{{/syntax.typeParameters.0}}
{{#fieldValue}}
<h4 class="section">{{__global.fieldValue}}</h4>
<dl class="parameters">
<dt>{{{type.specName.0.value}}}</dt>
<dd>{{{description}}}</dd>
</dl>
{{/fieldValue}}
{{#propertyValue}}
<h4 class="section">{{__global.propertyValue}}</h4>
<dl class="parameters">
<dt>{{{type.specName.0.value}}}</dt>
<dd>{{{description}}}</dd>
</dl>
{{/propertyValue}}
{{#eventType}}
<h4 class="section">{{__global.eventType}}</h4>
<dl class="parameters">
<dt>{{{type.specName.0.value}}}</dt>
<dd>{{{description}}}</dd>
</dl>
{{/eventType}}
{{/syntax}}
{{#example.0}}
<h4 class="section" id="{{id}}_examples">{{__global.examples}}</h4>
{{/example.0}}
{{#example}}
{{{.}}}
{{/example}}
{{#remarks}}
<h4 class="section" id="{{id}}_remarks">{{__global.remarks}}</h4>
<div class="markdown level1 remarks">{{{remarks}}}</div>
{{/remarks}}
{{#exceptions.0}}
<h4 class="section">{{__global.exceptions}}</h4>
<dl class="parameters">
{{/exceptions.0}}
{{#exceptions}}
<dt>{{{type.specName.0.value}}}</dt>
<dd>{{{description}}}</dd>
{{/exceptions}}
{{#exceptions.0}}
</dl>
{{/exceptions.0}}
{{#seealso.0}}
<dl class="typelist seealso">
<dt>{{__global.seealso}}</dt>
<dd>
{{/seealso.0}}
{{#seealso}}
{{#isCref}}
<div>{{{type.specName.0.value}}}</div>
{{/isCref}}
{{^isCref}}
<div>{{{url}}}</div>
{{/isCref}}
{{/seealso}}
{{#seealso.0}}
</dd>
</dl>
{{/seealso.0}}
{{/children}}
{{/children}}
{{#seealso.0}}
<h2 id="seealso">{{__global.seealso}}</h2>
<div class="seealso">
{{/seealso.0}}
{{#seealso}}
{{#isCref}}
<div>{{{type.specName.0.value}}}</div>
{{/isCref}}
{{^isCref}}
<div>{{{url}}}</div>
{{/isCref}}
{{/seealso}}
{{#seealso.0}}
</div>
{{/seealso.0}}
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
{{>partials/class}}
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
{{!Add your own custom template for the content for ManagedReference here}}
{{#_splitReference}}
{{#isCollection}}
{{>partials/collection}}
{{/isCollection}}
{{#isItem}}
{{>partials/item}}
{{/isItem}}
{{/_splitReference}}
\ No newline at end of file
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
{{>partials/class.header}}
{{#seealso.0}}
<h2 id="seealso">{{__global.seealso}}</h2>
<div class="seealso">
{{/seealso.0}}
{{#seealso}}
{{#isCref}}
<div>{{{type.specName.0.value}}}</div>
{{/isCref}}
{{^isCref}}
<div>{{{url}}}</div>
{{/isCref}}
{{/seealso}}
{{#seealso.0}}
</div>
{{/seealso.0}}
\ No newline at end of file
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
{{>partials/class.header}}
\ No newline at end of file
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
<h1 id="{{id}}" data-uid="{{uid}}" class="text-break">{{>partials/title}}</h1>
<div class="markdown level0 summary">{{{summary}}}</div>
<div class="markdown level0 conceptual">{{{conceptual}}}</div>
<div class="markdown level0 remarks">{{{remarks}}}</div>
{{#children}}
<h3 id="{{id}}">{{>partials/namespaceSubtitle}}</h3>
{{#children}}
<dl class="jumplist">
<dt><xref uid="{{uid}}" altProperty="fullName" displayProperty="name"/></dt>
<dd>{{{summary}}}</dd>
</dl>
{{/children}}
{{/children}}
@import "https://fonts.googleapis.com/css2?family=Montserrat:wght@100;300;500;700;900&family=Raleway:wght@300;500;700;900&display=swap";
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
$enable-important-utilities: false;
$container-max-widths: (
xxl: 1768px
) !default;
@import "mixins";
@import "bootstrap/scss/bootstrap";
@import "highlight";
@import "layout";
@import "nav";
@import "toc";
@import "markdown";
@import "search";
@import "dotnet";
@import "wpfui";
h1,
h2,
h3,
h4,
h5,
h6,
.xref,
.text-break {
word-wrap: break-word;
word-break: break-word;
}
.divider {
margin: 0 5px;
color: #ccc;
}
article {
// For REST API view source link
span.small.pull-right {
float: right;
}
img {
max-width: 100%;
height: auto;
}
}
.codewrapper {
position: relative;
}
.sample-response .response-content {
max-height: 200px;
}
@media (width <= 768px) {
#mobile-indicator {
display: block;
}
.mobile-hide {
display: none;
}
/* workaround for #hashtag url is no longer needed */
h1::before,
h2::before,
h3::before,
h4::before {
content: "";
display: none;
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import 'bootstrap'
import { DocfxOptions } from './options'
import { highlight } from './highlight'
import { renderMarkdown } from './markdown'
import { enableSearch } from './search'
import { renderToc } from './toc'
import { initTheme } from './theme'
import { renderBreadcrumb, renderInThisArticle, renderNavbar } from './nav'
import { renderIndexStats } from './wpfui-index-stats'
import 'bootstrap-icons/font/bootstrap-icons.scss'
import './docfx.scss'
declare global {
interface Window {
docfx: DocfxOptions & {
ready?: boolean,
searchReady?: boolean,
searchResultReady?: boolean,
}
}
}
export async function init(options: DocfxOptions) {
window.docfx = Object.assign({}, options)
initTheme()
enableSearch()
renderInThisArticle()
renderIndexStats()
await Promise.all([
renderMarkdown(),
renderNav(),
highlight()
])
window.docfx.ready = true
async function renderNav() {
const [navbar, toc] = await Promise.all([renderNavbar(), renderToc()])
renderBreadcrumb([...navbar, ...toc])
}
}
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
body[data-yaml-mime="ManagedReference"] article {
h1[data-uid] {
position: relative;
padding-right: 1.6rem;
}
h3[data-uid] {
position: relative;
font-weight: 400;
margin-top: 3rem;
padding-bottom: 5px;
padding-right: 1.6rem;
}
h2.section {
margin-top: 3rem;
+h3[data-uid], +a+h3[data-uid] {
margin-top: 1rem;
}
}
h4.section {
font-weight: 300;
margin-top: 1.6rem;
}
dl>dt {
font-weight: normal;
}
dl>dd {
margin-left: 1rem;
}
dl.typelist {
>dt {
font-weight: 600;
}
>dd {
margin-left: 0;
}
>dd>div {
display: inline-block;
&:not(:last-child)::after {
content: ', ';
}
}
&.inheritance>dd>div:not(:last-child)::after {
font-family: bootstrap-icons;
content: '\F12C';
position: relative;
top: .2em;
opacity: .8;
}
}
dl.parameters {
>dt>code {
margin-right: .2em;
}
}
div.facts {
font-size: 14px;
margin: 2rem 0 1rem;
>dl {
margin: 0;
>dd {
margin-left: .25rem;
display: inline-block;
}
>dt {
display: inline-block;
}
>dt::after {
content: ":";
}
}
}
.header-action {
position: absolute;
right: 0;
bottom: .2rem;
font-size: 1.2rem;
}
td.term {
font-weight: 600;
}
summary {
display: block;
cursor: inherit;
}
li>span.term {
font-weight: 600;
&::after {
content: '-';
margin: 0 .5em;
}
}
}
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { breakWord } from './helper'
test('break-text', () => {
expect(breakWord('Other APIs')).toEqual(['Other APIs'])
expect(breakWord('System.CodeDom')).toEqual(['System.', 'Code', 'Dom'])
expect(breakWord('System.Collections.Dictionary<string, object>')).toEqual(['System.', 'Collections.', 'Dictionary<', 'string,', ' object>'])
expect(breakWord('https://github.com/dotnet/docfx')).toEqual(['https://github.', 'com/', 'dotnet/', 'docfx'])
})
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { html, TemplateResult } from 'lit-html'
/**
* Get the value of an HTML meta tag.
*/
export function meta(name: string): string {
return (document.querySelector(`meta[name="${name}"]`) as HTMLMetaElement)?.content
}
/**
* Add <wbr> into long word.
*/
export function breakWord(text: string): string[] {
const regex = /([a-z0-9])([A-Z]+[a-z])|([a-zA-Z0-9][.,/<>_])/g
const result = []
let start = 0
while (true) {
const match = regex.exec(text)
if (!match) {
break
}
const index = match.index + (match[1] || match[3]).length
result.push(text.slice(start, index))
start = index
}
if (start < text.length) {
result.push(text.slice(start))
}
return result
}
/**
* Add <wbr> into long word.
*/
export function breakWordLit(text: string): TemplateResult {
const result = []
breakWord(text).forEach(word => {
if (result.length > 0) {
result.push(html`<wbr>`)
}
result.push(html`${word}`)
})
return html`${result}`
}
/**
* Check if the url is external.
* @param url The url to check.
* @returns True if the url is external.
*/
export function isExternalHref(url: URL): boolean {
return url.hostname !== window.location.hostname || url.protocol !== window.location.protocol
}
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
@import "highlight.js/scss/vs";
@include color-mode(dark) {
/* stylelint-disable-next-line no-invalid-position-at-import-rule */
@import "highlight.js/scss/vs2015";
}
.hljs {
background-color: #f5f5f5;
}
/* For code snippet line highlight */
pre > code .line-highlight {
background-color: yellow;
}
@include color-mode(dark) {
pre > code .line-highlight {
background-color: #4a4a00;
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
export async function highlight() {
const codeBlocks = document.querySelectorAll('pre code')
if (codeBlocks.length <= 0) {
return
}
const { default: hljs } = await import('highlight.js')
window.docfx.configureHljs?.(hljs)
document.querySelectorAll('pre code').forEach(block => {
hljs.highlightElement(block as HTMLElement)
})
document.querySelectorAll('pre code[highlight-lines]').forEach(block => {
if (block.innerHTML === '') {
return
}
const queryString = block.getAttribute('highlight-lines')
if (!queryString) {
return
}
const lines = block.innerHTML.split('\n')
const ranges = queryString.split(',')
for (const range of ranges) {
let start = 0
let end = 0
const found = range.match(/^(\d+)-(\d+)?$/)
if (found) {
// consider region as `{startlinenumber}-{endlinenumber}`, in which {endlinenumber} is optional
start = +found[1]
end = +found[2]
if (isNaN(end) || end > lines.length) {
end = lines.length
}
} else {
// consider region as a sigine line number
if (isNaN(Number(range))) {
continue
}
start = +range
end = start
}
if (start <= 0 || end <= 0 || start > end || start > lines.length) {
// skip current region if invalid
continue
}
lines[start - 1] = '<span class="line-highlight">' + lines[start - 1]
lines[end - 1] = lines[end - 1] + '</span>'
}
block.innerHTML = lines.join('\n')
})
}
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
$header-height: 80px;
$footer-height: 120px;
$main-padding-top: 1.6rem;
$main-padding-bottom: 4rem;
// Makes a div sticky to top
@mixin sticky-top {
@include media-breakpoint-up(md) {
position: sticky;
top: 0;
z-index: 1030;
}
}
@mixin stick-to-header {
@include media-breakpoint-up(md) {
position: sticky;
top: calc($header-height + $main-padding-top);
}
}
html {
width: calc(100vw - var(--scrollbar-width));
min-height: 100vh;
overflow-x: hidden;
}
body,
body[data-layout="landing"] {
width: calc(100vw - var(--scrollbar-width));
min-height: 100vh;
display: flex;
flex-direction: column;
>header {
display: flex;
align-items: stretch;
@include sticky-top;
@include media-breakpoint-up(md) {
height: $header-height;
}
>nav {
flex: 1;
}
}
>footer {
padding: 2rem 1rem;
height: $footer-height;
>div {
display: flex;
align-items: center;
}
}
>main {
display: flex;
flex: 1;
padding-top: $main-padding-top;
padding-bottom: $main-padding-bottom;
>.content {
>:not(article) {
display: none;
}
@include media-breakpoint-up(md) {
>article [id] {
scroll-margin-top: $header-height;
}
}
}
>:not(.content) {
display: none;
}
}
@media print {
>header, >footer {
display: none;
}
}
}
@media not print {
// Search layout
body[data-search] {
>main {
display: none;
}
>.search-results {
display: block;
flex: 1;
padding-top: $main-padding-top;
padding-bottom: $main-padding-bottom;
}
}
body:not([data-search]) {
>.search-results {
display: none;
}
// Default layout: with header, footer, actionbar, affix, and toc
&[data-layout=""],
&[data-layout="conceptual"] {
>main {
padding-bottom: 0;
>.toc-offcanvas {
flex: .35;
display: block;
overflow-x: hidden;
overflow-y: auto;
max-width: 360px;
max-height: calc(100vh - $header-height - $main-padding-top);
@include stick-to-header;
@include media-breakpoint-down(md) {
flex: 0;
}
}
>.content {
flex: 1;
min-width: 0;
margin: 0 3rem;
padding-bottom: $main-padding-bottom;
>.actionbar {
display: flex;
align-items: flex-start;
margin-top: .5rem;
min-height: 40px;
}
>.contribution,
>.next-article {
display: flex;
}
@include media-breakpoint-down(lg) {
margin: 0 1rem;
}
@include media-breakpoint-down(md) {
margin: 0;
}
}
>.affix {
display: block;
width: 230px;
max-height: calc(100vh - #{$header-height});
overflow-x: hidden;
overflow-y: auto;
@include stick-to-header;
@media only screen and (width <= 1140px) {
display: none;
}
}
}
}
// Chromeless layout: with no header, footer, actionbar, affix, and toc
&[data-layout="chromeless"] {
>header, >footer {
display: none;
}
}
}
}
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
/* External link icon */
a.external[href]::after {
font-family: bootstrap-icons;
content: "\F1C5";
font-size: .6rem;
margin: 0 .2em;
display: inline-block;
}
/* Alerts */
.alert h5 {
text-transform: uppercase;
font-weight: bold;
font-size: 1rem;
&::before {
@include adjust-icon;
}
}
.alert-info h5::before {
content: "\F431";
}
.alert-warning h5::before {
content: "\F333";
}
.alert-danger h5::before {
content: "\F623";
}
/* For Embedded Video */
div.embeddedvideo {
padding-top: 56.25%;
position: relative;
width: 100%;
margin-bottom: 1em;
}
div.embeddedvideo iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
/* For code actions */
pre {
position: relative;
>.code-action {
display: none;
position: absolute;
top: .25rem;
right: .2rem;
.bi-check-lg {
font-size: 1.2rem;
}
}
&:hover {
>.code-action {
display: block;
}
}
}
/* For tabbed content */
.tabGroup {
margin-bottom: 1rem;
>section {
margin: 0;
padding: 1rem;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { breakWord, meta } from './helper'
import AnchorJs from 'anchor-js'
import { html, render } from 'lit-html'
import { getTheme } from './theme'
/**
* Initialize markdown rendering.
*/
export async function renderMarkdown() {
renderWordBreaks()
renderTables()
renderAlerts()
renderLinks()
renderTabs()
renderAnchor()
renderCodeCopy()
renderClickableImage()
await Promise.all([
renderMath(),
renderMermaid()
])
}
async function renderMath() {
const math = document.querySelectorAll('.math')
if (math.length > 0) {
await import('mathjax/es5/tex-svg-full.js')
}
}
let mermaidRenderCount = 0
/**
* Render mermaid diagrams.
*/
async function renderMermaid() {
const diagrams = document.querySelectorAll<HTMLElement>('pre code.lang-mermaid')
if (diagrams.length <= 0) {
return
}
const { default: mermaid } = await import('mermaid')
const theme = getTheme() === 'dark' ? 'dark' : 'default'
// Turn off deterministic ids on re-render
const deterministicIds = mermaidRenderCount === 0
mermaid.initialize(Object.assign({ startOnLoad: false, deterministicIds, theme }, window.docfx.mermaid))
mermaidRenderCount++
const nodes = []
diagrams.forEach(e => {
// Rerender when elements becomes visible due to https://github.com/mermaid-js/mermaid/issues/1846
if (e.offsetParent) {
nodes.push(e.parentElement)
e.parentElement.classList.add('mermaid')
e.parentElement.innerHTML = e.innerHTML
}
})
await mermaid.run({ nodes })
}
/**
* Add <wbr> to break long text.
*/
function renderWordBreaks() {
document.querySelectorAll<HTMLElement>('article h1,h2,h3,h4,h5,h6,.xref,.text-break').forEach(e => {
if (e.innerHTML?.trim() === e.innerText?.trim()) {
const children: (string | Node)[] = []
for (const text of breakWord(e.innerText)) {
if (children.length > 0) {
children.push(document.createElement('wbr'))
}
children.push(text)
}
e.replaceChildren(...children)
}
})
}
/**
* Make images in articles clickable by wrapping the image in an anchor tag.
* The image is clickable only if its size is larger than 200x200 and it is not already been wrapped in an anchor tag.
*/
function renderClickableImage() {
const MIN_CLICKABLE_IMAGE_SIZE = 200
const imageLinks = Array.from(document.querySelectorAll<HTMLImageElement>('article a img[src]'))
document.querySelectorAll<HTMLImageElement>('article img[src]').forEach(img => {
if (shouldMakeClickable()) {
makeClickable()
} else {
img.addEventListener('load', () => {
if (shouldMakeClickable()) {
makeClickable()
}
})
}
function makeClickable() {
const a = document.createElement('a')
a.target = '_blank'
a.rel = 'noopener noreferrer nofollow'
a.href = img.src
img.replaceWith(a)
a.appendChild(img)
}
function shouldMakeClickable(): boolean {
return img.naturalWidth > MIN_CLICKABLE_IMAGE_SIZE &&
img.naturalHeight > MIN_CLICKABLE_IMAGE_SIZE &&
!imageLinks.includes(img)
}
})
}
/**
* Styling for tables in conceptual documents using Bootstrap.
* See http://getbootstrap.com/css/#tables
*/
function renderTables() {
document.querySelectorAll('table').forEach(table => {
table.classList.add('table', 'table-bordered', 'table-condensed')
const wrapper = document.createElement('div')
wrapper.className = 'table-responsive'
table.parentElement.insertBefore(wrapper, table)
wrapper.appendChild(table)
})
}
/**
* Styling for alerts.
*/
function renderAlerts() {
document.querySelectorAll('.NOTE, .TIP').forEach(e => e.classList.add('alert', 'alert-info'))
document.querySelectorAll('.WARNING').forEach(e => e.classList.add('alert', 'alert-warning'))
document.querySelectorAll('.IMPORTANT, .CAUTION').forEach(e => e.classList.add('alert', 'alert-danger'))
}
/**
* Open external links to different host in a new window.
*/
function renderLinks() {
if (meta('docfx:disablenewtab') === 'true') {
return
}
document.querySelectorAll<HTMLAnchorElement>('article a[href]').forEach(a => {
if (a.hostname !== window.location.hostname && a.innerText.trim() !== '') {
a.target = '_blank'
a.rel = 'noopener noreferrer nofollow'
a.classList.add('external')
}
})
}
/**
* Render anchor # for headings
*/
function renderAnchor() {
const anchors = new AnchorJs()
anchors.options = Object.assign({
visible: 'hover',
icon: '#'
}, window.docfx.anchors)
anchors.add('article h2:not(.no-anchor), article h3:not(.no-anchor), article h4:not(.no-anchor)')
}
/**
* Render code copy button.
*/
function renderCodeCopy() {
document.querySelectorAll<HTMLElement>('pre>code').forEach(code => {
if (code.innerText.trim().length === 0) {
return
}
let copied = false
renderCore()
function renderCore() {
const dom = copied
? html`<a class='btn border-0 link-success code-action'><i class='bi bi-check-lg'></i></a>`
: html`<a class='btn border-0 code-action' title='copy' href='#' @click=${copy}><i class='bi bi-clipboard'></i></a>`
render(dom, code.parentElement)
async function copy(e) {
e.preventDefault()
await navigator.clipboard.writeText(code.innerText)
copied = true
renderCore()
setTimeout(() => {
copied = false
renderCore()
}, 1000)
}
}
})
}
/**
* Render tabbed content.
*/
function renderTabs() {
updateTabStyle()
const contentAttrs = {
id: 'data-bi-id',
name: 'data-bi-name',
type: 'data-bi-type'
}
const Tab = (function() {
function Tab(li, a, section) {
this.li = li
this.a = a
this.section = section
}
Object.defineProperty(Tab.prototype, 'tabIds', {
get: function() { return this.a.getAttribute('data-tab').split(' ') },
enumerable: true,
configurable: true
})
Object.defineProperty(Tab.prototype, 'condition', {
get: function() { return this.a.getAttribute('data-condition') },
enumerable: true,
configurable: true
})
Object.defineProperty(Tab.prototype, 'visible', {
get: function() { return !this.li.hasAttribute('hidden') },
set: function(value) {
if (value) {
this.li.removeAttribute('hidden')
this.li.removeAttribute('aria-hidden')
} else {
this.li.setAttribute('hidden', 'hidden')
this.li.setAttribute('aria-hidden', 'true')
}
},
enumerable: true,
configurable: true
})
Object.defineProperty(Tab.prototype, 'selected', {
get: function() { return !this.section.hasAttribute('hidden') },
set: function(value) {
if (value) {
this.a.setAttribute('aria-selected', 'true')
this.a.classList.add('active')
this.a.tabIndex = 0
this.section.removeAttribute('hidden')
this.section.removeAttribute('aria-hidden')
} else {
this.a.setAttribute('aria-selected', 'false')
this.a.classList.remove('active')
this.a.tabIndex = -1
this.section.setAttribute('hidden', 'hidden')
this.section.setAttribute('aria-hidden', 'true')
}
},
enumerable: true,
configurable: true
})
Tab.prototype.focus = function() {
this.a.focus()
}
return Tab
}())
initTabs(document.body)
function initTabs(container) {
const queryStringTabs = readTabsQueryStringParam()
const elements = container.querySelectorAll('.tabGroup')
const state = { groups: [], selectedTabs: [] }
for (let i = 0; i < elements.length; i++) {
const group = initTabGroup(elements.item(i))
if (!group.independent) {
updateVisibilityAndSelection(group, state)
state.groups.push(group)
}
}
container.addEventListener('click', function(event) { return handleClick(event, state) })
if (state.groups.length === 0) {
return state
}
selectTabs(queryStringTabs)
updateTabsQueryStringParam(state)
return state
}
function initTabGroup(element) {
const group = {
independent: element.hasAttribute('data-tab-group-independent'),
tabs: []
}
let li = element.firstElementChild.firstElementChild
while (li) {
const a = li.firstElementChild
a.setAttribute(contentAttrs.name, 'tab')
const dataTab = a.getAttribute('data-tab').replace(/\+/g, ' ')
a.setAttribute('data-tab', dataTab)
const section = element.querySelector('[id="' + a.getAttribute('aria-controls') + '"]')
const tab = new Tab(li, a, section)
group.tabs.push(tab)
li = li.nextElementSibling
}
element.setAttribute(contentAttrs.name, 'tab-group')
element.tabGroup = group
return group
}
function updateVisibilityAndSelection(group, state) {
let anySelected = false
let firstVisibleTab
for (let _i = 0, _a = group.tabs; _i < _a.length; _i++) {
const tab = _a[_i]
tab.visible = tab.condition === null || state.selectedTabs.indexOf(tab.condition) !== -1
if (tab.visible) {
if (!firstVisibleTab) {
firstVisibleTab = tab
}
}
tab.selected = tab.visible && arraysIntersect(state.selectedTabs, tab.tabIds)
anySelected = anySelected || tab.selected
}
if (!anySelected) {
for (let _b = 0, _c = group.tabs; _b < _c.length; _b++) {
const tabIds = _c[_b].tabIds
for (let _d = 0, tabIds1 = tabIds; _d < tabIds1.length; _d++) {
const tabId = tabIds1[_d]
const index = state.selectedTabs.indexOf(tabId)
if (index === -1) {
continue
}
state.selectedTabs.splice(index, 1)
}
}
const tab = firstVisibleTab
tab.selected = true
state.selectedTabs.push(tab.tabIds[0])
}
}
function getTabInfoFromEvent(event) {
if (!(event.target instanceof HTMLElement)) {
return null
}
const anchor = event.target.closest('a[data-tab]')
if (anchor === null) {
return null
}
const tabIds = anchor.getAttribute('data-tab').split(' ')
const group = anchor.parentElement.parentElement.parentElement.tabGroup
if (group === undefined) {
return null
}
return { tabIds, group, anchor }
}
function handleClick(event, state) {
const info = getTabInfoFromEvent(event)
if (info === null) {
return
}
event.preventDefault()
info.anchor.href = 'javascript:'
setTimeout(function() {
info.anchor.href = '#' + info.anchor.getAttribute('aria-controls')
})
const tabIds = info.tabIds; const group = info.group
const originalTop = info.anchor.getBoundingClientRect().top
if (group.independent) {
for (let _i = 0, _a = group.tabs; _i < _a.length; _i++) {
const tab = _a[_i]
tab.selected = arraysIntersect(tab.tabIds, tabIds)
}
} else {
if (arraysIntersect(state.selectedTabs, tabIds)) {
return
}
const previousTabId = group.tabs.filter(function(t) { return t.selected })[0].tabIds[0]
state.selectedTabs.splice(state.selectedTabs.indexOf(previousTabId), 1, tabIds[0])
for (let _b = 0, _c = state.groups; _b < _c.length; _b++) {
const group1 = _c[_b]
updateVisibilityAndSelection(group1, state)
}
updateTabsQueryStringParam(state)
}
notifyContentUpdated()
const top = info.anchor.getBoundingClientRect().top
if (top !== originalTop && event instanceof MouseEvent) {
window.scrollTo(0, window.pageYOffset + top - originalTop)
}
}
function selectTabs(tabIds) {
for (let _i = 0, tabIds1 = tabIds; _i < tabIds1.length; _i++) {
const tabId = tabIds1[_i]
const a = document.querySelector('.tabGroup > ul > li > a[data-tab="' + tabId + '"]:not([hidden])')
if (a === null) {
return
}
a.dispatchEvent(new CustomEvent('click', { bubbles: true }))
}
}
function readTabsQueryStringParam() {
const qs = new URLSearchParams(window.location.search)
const t = qs.get('tabs')
if (!t) {
return []
}
return t.split(',')
}
function updateTabsQueryStringParam(state) {
const qs = new URLSearchParams(window.location.search)
qs.set('tabs', state.selectedTabs.join())
const url = location.protocol + '//' + location.host + location.pathname + '?' + qs.toString() + location.hash
if (location.href === url) {
return
}
history.replaceState({}, document.title, url)
}
function arraysIntersect(a, b) {
for (let _i = 0, a1 = a; _i < a1.length; _i++) {
const itemA = a1[_i]
for (let _a = 0, b1 = b; _a < b1.length; _a++) {
const itemB = b1[_a]
if (itemA === itemB) {
return true
}
}
}
return false
}
function updateTabStyle() {
document.querySelectorAll('div.tabGroup>ul').forEach(e => e.classList.add('nav', 'nav-tabs'))
document.querySelectorAll('div.tabGroup>ul>li').forEach(e => e.classList.add('nav-item'))
document.querySelectorAll('div.tabGroup>ul>li>a').forEach(e => e.classList.add('nav-link'))
document.querySelectorAll('div.tabGroup>section').forEach(e => e.classList.add('card'))
}
function notifyContentUpdated() {
renderMermaid()
}
}
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
@mixin adjust-icon {
font-family: bootstrap-icons;
position: relative;
margin-right: 0.5em;
top: 0.2em;
font-size: 1.25em;
font-weight: normal;
}
@mixin underline-on-hover {
text-decoration: none;
&:hover, &:focus {
text-decoration: underline;
}
}
\ No newline at end of file
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
.breadcrumb {
font-size: 14px;
a {
@include underline-on-hover;
}
}
.next-article {
display: flex;
&:not(:has(div)) {
border-top-width: 0;
}
&:has(div) {
margin-top: 3rem;
padding-top: 1rem;
}
&>div {
flex: 1;
&.next {
text-align: right;
}
&>span {
opacity: .66;
font-size: 14px;
}
&>a {
display: block;
}
}
}
.navbar {
padding: 2rem 1rem;
.navbar-brand {
display: flex;
align-items: center;
}
.navbar-nav {
display: flex;
flex-wrap: nowrap;
}
#navbar {
display: flex;
flex: 1;
justify-content: flex-end;
form {
display: flex;
position: relative;
align-items: center;
>i.bi {
position: absolute;
left: .8rem;
opacity: .5;
}
>input {
padding-left: 2.5rem;
}
&.search {
order: 50;
}
&.icons {
margin-left: auto;
}
}
}
@include media-breakpoint-down(md) {
#navbar {
flex-direction: column;
align-items: flex-start;
form {
margin: 1rem 0 0;
&.search {
align-self: stretch;
order: 30;
}
&.icons {
align-self: center;
order: 40;
margin: 1rem 0;
}
}
}
}
}
.affix {
font-size: 14px;
h5 {
display: inline-block;
font-weight: 300;
text-transform: uppercase;
padding: 1em 0 .5em;
font-size: 14px;
letter-spacing: 2px;
}
h6 {
font-size: 14px;
}
ul {
flex-direction: column;
list-style-type: none;
padding-left: 0;
margin-left: 0;
h6 {
margin-top: 1rem;
}
li {
margin: .4rem 0;
a {
@include underline-on-hover;
}
}
}
}
.contribution {
margin-top: 2rem;
a.edit-link {
@include underline-on-hover;
&::before {
content: "\F4CA";
display: inline-block;
@include adjust-icon;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { render, html, TemplateResult } from 'lit-html'
import { breakWordLit, meta, isExternalHref } from './helper'
import { themePicker } from './theme'
import { TocNode } from './toc'
export type NavItem = {
name: string
href: URL
}
export type NavItemContainer = {
name: string
items: NavItem[]
}
/**
* @returns active navbar items
*/
export async function renderNavbar(): Promise<NavItem[]> {
const navbar = document.getElementById('navbar')
if (!navbar) {
return
}
const navItems = await loadNavItems()
const activeItem = findActiveItem(navItems)
const menuItem = item => {
const current = (item === activeItem ? 'page' : false)
const active = (item === activeItem ? 'active' : null)
return html`<li class='nav-item'><a class='nav-link ${active}' aria-current=${current} href=${item.href}>${breakWordLit(item.name)}</a></li>`
}
const menu = html`
<ul class='navbar-nav'>${navItems.map(item => {
if ('items' in item) {
const active = item.items.some(i => i === activeItem) ? 'active' : null
return html`
<li class='nav-item dropdown'>
<a class='nav-link dropdown-toggle ${active}' href='#' role='button' data-bs-toggle='dropdown' aria-expanded='false'>
${breakWordLit(item.name)}
</a>
<ul class='dropdown-menu'>${item.items.map(menuItem)}</ul>
</li>`
} else {
return menuItem(item)
}
})
}</ul>`
function renderCore() {
const icons = html`
<form class="icons">
${window.docfx.iconLinks?.map(i => html`<a href="${i.href}" title="${i.title}" class="btn border-0"><i class="bi bi-${i.icon}"></i></a>`)}
${themePicker(renderCore)}
</form>`
render(html`${menu} ${icons}`, navbar)
}
renderCore()
return activeItem ? [activeItem] : []
async function loadNavItems(): Promise<(NavItem | NavItemContainer)[]> {
const navrel = meta('docfx:navrel')
if (!navrel) {
return []
}
const navUrl = new URL(navrel.replace(/.html$/gi, '.json'), window.location.href)
const { items } = await fetch(navUrl).then(res => res.json())
return items.map((a: NavItem | NavItemContainer) => {
if ('items' in a) {
return { name: a.name, items: a.items.map(i => ({ name: i.name, href: new URL(i.href, navUrl) })) }
}
return { name: a.name, href: new URL(a.href, navUrl) }
})
}
}
export function renderBreadcrumb(breadcrumb: (NavItem | TocNode)[]) {
const container = document.getElementById('breadcrumb')
if (container) {
render(
html`
<ol class="breadcrumb">
${breadcrumb.map(i => html`<li class="breadcrumb-item"><a href="${i.href}">${breakWordLit(i.name)}</a></li>`)}
</ol>`,
container)
}
}
export function renderInThisArticle() {
const affix = document.getElementById('affix')
const windowPathname = window.location.pathname
if (windowPathname === '' || windowPathname === '/' || windowPathname === '/index.html') {
return
}
if (affix) {
render(document.body.getAttribute('data-yaml-mime') === 'ManagedReference' ? inThisArticleForManagedReference() : inThisArticleForConceptual(), affix)
}
}
function inThisArticleForConceptual() {
const headings = document.querySelectorAll<HTMLHeadingElement>('article h2')
if (headings.length > 0) {
return html`
<h5 class="border-bottom">In this article</h5>
<ul>${Array.from(headings).map(h => html`<li><a class="link-secondary" href="#${h.id}">${breakWordLit(h.innerText)}</a></li>`)}</ul>`
}
}
function inThisArticleForManagedReference(): TemplateResult {
let headings = Array.from(document.querySelectorAll<HTMLHeadingElement>('article h2, article h3'))
headings = headings.filter((h, i) => h.tagName === 'H3' || headings[i + 1]?.tagName === 'H3')
if (headings.length > 0) {
return html`
<h5 class="border-bottom">In this article</h5>
<ul>${headings.map(h => {
return h.tagName === 'H2'
? html`<li><h6>${breakWordLit(h.innerText)}</h6></li>`
: html`<li><a class="link-secondary" href="#${h.id}">${breakWordLit(h.innerText)}</a></li>`
})}</ul>`
}
}
function findActiveItem(items: (NavItem | NavItemContainer)[]): NavItem {
const url = new URL(window.location.href)
let activeItem: NavItem
let maxPrefix = 0
for (const item of items.map(i => 'items' in i ? i.items : i).flat()) {
if (isExternalHref(item.href)) {
continue
}
const prefix = commonUrlPrefix(url, item.href)
if (prefix > maxPrefix) {
maxPrefix = prefix
activeItem = item
}
}
return activeItem
}
function commonUrlPrefix(url: URL, base: URL): number {
const urlSegments = url.pathname.split('/')
const baseSegments = base.pathname.split('/')
let i = 0
while (i < urlSegments.length && i < baseSegments.length && urlSegments[i] === baseSegments[i]) {
i++
}
return i
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import BootstrapIcons from 'bootstrap-icons/font/bootstrap-icons.json'
import { HLJSApi } from 'highlight.js'
import { AnchorJSOptions } from 'anchor-js'
import { MermaidConfig } from 'mermaid'
export type Theme = 'light' | 'dark' | 'auto'
export type IconLink = {
/** A [bootstrap-icons](https://icons.getbootstrap.com/) name */
icon: keyof typeof BootstrapIcons,
/** The URL of this icon link */
href: string,
/** The title of this icon link shown on mouse hover */
title?: string
}
/**
* Enables customization of the website through the global `window.docfx` object.
*/
export type DocfxOptions = {
/** Configures the default theme */
defaultTheme?: Theme,
/** A list of icons to show in the header next to the theme picker */
iconLinks?: IconLink[],
/** Configures [anchor-js](https://www.bryanbraun.com/anchorjs#options) options */
anchors?: AnchorJSOptions,
/** Configures mermaid diagram options */
mermaid?: MermaidConfig,
/** Configures [hightlight.js](https://highlightjs.org/) */
configureHljs?: (hljs: HLJSApi) => void,
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import lunr from 'lunr'
let lunrIndex
let stopWords = null
let searchData = {}
lunr.tokenizer.separator = /[\s\-.()]+/
const stopWordsRequest = new XMLHttpRequest()
stopWordsRequest.open('GET', '../search-stopwords.json')
stopWordsRequest.onload = function() {
if (this.status !== 200) {
return
}
stopWords = JSON.parse(this.responseText)
buildIndex()
}
stopWordsRequest.send()
const searchDataRequest = new XMLHttpRequest()
searchDataRequest.open('GET', '../index.json')
searchDataRequest.onload = function() {
if (this.status !== 200) {
return
}
searchData = JSON.parse(this.responseText)
buildIndex()
postMessage({ e: 'index-ready' })
}
searchDataRequest.send()
onmessage = function(oEvent) {
const q = oEvent.data.q
const results = []
if (lunrIndex) {
const hits = lunrIndex.search(q)
hits.forEach(function(hit) {
const item = searchData[hit.ref]
results.push({ href: item.href, title: item.title, keywords: item.keywords })
})
}
postMessage({ e: 'query-ready', q, d: results })
}
function buildIndex() {
if (stopWords !== null && !isEmpty(searchData)) {
lunrIndex = lunr(function() {
this.pipeline.remove(lunr.stopWordFilter)
this.ref('href')
this.field('title', { boost: 50 })
this.field('keywords', { boost: 20 })
for (const prop in searchData) {
if (Object.prototype.hasOwnProperty.call(searchData, prop)) {
this.add(searchData[prop])
}
}
const docfxStopWordFilter = lunr.generateStopWordFilter(stopWords)
lunr.Pipeline.registerFunction(docfxStopWordFilter, 'docfxStopWordFilter')
this.pipeline.add(docfxStopWordFilter)
this.searchPipeline.add(docfxStopWordFilter)
})
}
}
function isEmpty(obj) {
if (!obj) return true
for (const prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) { return false }
}
return true
}
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
#search-results {
line-height: 1.8;
>.search-list {
font-size: .9em;
color: $secondary;
}
>.sr-items {
flex: 1;
.sr-item {
margin-bottom: 1.5em;
>.item-title {
font-size: x-large;
}
>.item-href {
color: #093;
font-size: small;
}
>.item-brief {
font-size: small;
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { meta } from './helper'
import { html, render, TemplateResult } from 'lit-html'
import { classMap } from 'lit-html/directives/class-map.js'
type SearchHit = {
href: string
title: string
keywords: string
}
let query
/**
* Support full-text-search
*/
export function enableSearch() {
const searchQuery = document.getElementById('search-query') as HTMLInputElement
if (!searchQuery || !window.Worker) {
return
}
const relHref = meta('docfx:rel') || ''
const worker = new Worker(relHref + 'public/search-worker.min.js', { type: 'module' })
worker.onmessage = function(oEvent) {
switch (oEvent.data.e) {
case 'index-ready':
searchQuery.disabled = false
searchQuery.addEventListener('input', onSearchQueryInput)
window.docfx.searchReady = true
break
case 'query-ready':
document.body.setAttribute('data-search', 'true')
renderSearchResults(oEvent.data.d, 0)
window.docfx.searchResultReady = true
break
}
}
function onSearchQueryInput() {
query = searchQuery.value
if (query.length < 3) {
document.body.removeAttribute('data-search')
} else {
worker.postMessage({ q: query })
}
}
function relativeUrlToAbsoluteUrl(currentUrl, relativeUrl) {
const currentItems = currentUrl.split(/\/+/)
const relativeItems = relativeUrl.split(/\/+/)
let depth = currentItems.length - 1
const items = []
for (let i = 0; i < relativeItems.length; i++) {
if (relativeItems[i] === '..') {
depth--
} else if (relativeItems[i] !== '.') {
items.push(relativeItems[i])
}
}
return currentItems.slice(0, depth).concat(items).join('/')
}
function extractContentBrief(content) {
const briefOffset = 512
const words = query.split(/\s+/g)
const queryIndex = content.indexOf(words[0])
if (queryIndex > briefOffset) {
return '...' + content.slice(queryIndex - briefOffset, queryIndex + briefOffset) + '...'
} else if (queryIndex <= briefOffset) {
return content.slice(0, queryIndex + briefOffset) + '...'
}
}
function renderSearchResults(hits: SearchHit[], page: number) {
const numPerPage = 10
const totalPages = Math.ceil(hits.length / numPerPage)
render(
renderPage(page),
document.getElementById('search-results'))
function renderPage(page: number): TemplateResult {
if (hits.length === 0) {
return html`<div class="search-list">No results for "${query}"</div>`
}
const start = page * numPerPage
const curHits = hits.slice(start, start + numPerPage)
const items = html`
<div class="search-list">${hits.length} results for "${query}"</div>
<div class="sr-items">${curHits.map(hit => {
const currentUrl = window.location.href
const itemRawHref = relativeUrlToAbsoluteUrl(currentUrl, relHref + hit.href)
const itemHref = relHref + hit.href + '?q=' + query
const itemBrief = extractContentBrief(hit.keywords)
return html`
<div class="sr-item">
<div class="item-title"><a href="${itemHref}" target="_blank" rel="noopener noreferrer">${mark(hit.title, query)}</a></div>
<div class="item-href">${mark(itemRawHref, query)}</div>
<div class="item-brief">${mark(itemBrief, query)}</div>
</div>`
})}
</div>`
return html`${items} ${renderPagination()}`
}
function renderPagination() {
const maxVisiblePages = 5
const startPage = Math.max(0, Math.min(page - 2, totalPages - maxVisiblePages))
const endPage = Math.min(totalPages, startPage + maxVisiblePages)
const pages = Array.from(new Array(endPage - startPage).keys()).map(i => i + startPage)
if (pages.length <= 1) {
return null
}
return html`
<nav>
<ul class="pagination">
<li class="page-item">
<a class="page-link ${classMap({ disabled: page <= 0 })}" href="#" aria-label="Previous"
@click="${() => gotoPage(page - 1)}">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
${pages.map(i => html`
<li class="page-item">
<a class="page-link ${classMap({ active: page === i })}" href="#"
@click="${() => gotoPage(i)}">${i + 1}</a></li>`)}
<li class="page-item">
<a class="page-link ${classMap({ disabled: page >= totalPages - 1 })}" href="#" aria-label="Next"
@click="${() => gotoPage(page + 1)}">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>`
function gotoPage(page: number) {
if (page >= 0 && page < totalPages) {
renderSearchResults(hits, page)
}
}
}
}
}
function mark(text: string, query: string): TemplateResult {
const words = query.split(/\s+/g)
const wordsLower = words.map(w => w.toLowerCase())
const textLower = text.toLowerCase()
const result = []
let lastEnd = 0
for (let i = 0; i < wordsLower.length; i++) {
const word = wordsLower[i]
const index = textLower.indexOf(word, lastEnd)
if (index >= 0) {
result.push(html`${text.slice(lastEnd, index)}`)
result.push(html`<b>${text.slice(index, index + word.length)}</b>`)
lastEnd = index + word.length
}
}
result.push(html`${text.slice(lastEnd)}`)
return html`${result}`
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { html } from 'lit-html'
import { Theme } from './options'
function setTheme(theme: Theme) {
localStorage.setItem('theme', theme)
if (theme === 'auto') {
document.documentElement.setAttribute('data-bs-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
} else {
document.documentElement.setAttribute('data-bs-theme', theme)
}
}
function getDefaultTheme() {
return localStorage.getItem('theme') as Theme || window.docfx.defaultTheme || 'auto'
}
export function initTheme() {
setTheme(getDefaultTheme())
}
export function getTheme(): 'light' | 'dark' {
return document.documentElement.getAttribute('data-bs-theme') as 'light' | 'dark'
}
export function themePicker(refresh: () => void) {
const theme = getDefaultTheme()
const icon = theme === 'light' ? 'sun' : theme === 'dark' ? 'moon' : 'circle-half'
return html`
<div class='dropdown'>
<a title='Change theme' class='btn border-0 dropdown-toggle' data-bs-toggle='dropdown' aria-expanded='false'>
<i class='bi bi-${icon}'></i>
</a>
<ul class='dropdown-menu'>
<li><a class='dropdown-item' href='#' @click=${e => changeTheme(e, 'light')}><i class='bi bi-sun'></i> Light</a></li>
<li><a class='dropdown-item' href='#' @click=${e => changeTheme(e, 'dark')}><i class='bi bi-moon'></i> Dark</a></li>
<li><a class='dropdown-item' href='#' @click=${e => changeTheme(e, 'auto')}><i class='bi bi-circle-half'></i> Auto</a></li>
</ul>
</div>`
function changeTheme(e, theme: Theme) {
e.preventDefault()
setTheme(theme)
refresh()
}
}
/**
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*/
$expand-stub-width: .85rem;
.toc {
min-width: 0;
width: 100%;
ul {
font-size: 14px;
flex-direction: column;
list-style-type: none;
padding-left: 0;
overflow-wrap: break-word;
}
li {
font-weight: normal;
margin: .6em 0;
padding-left: $expand-stub-width;
position: relative;
}
li > a {
display: inline;
@include underline-on-hover;
}
li > ul {
display: none;
}
li.expanded > ul {
display: block;
}
.expand-stub::before {
display: inline-block;
width: $expand-stub-width;
cursor: pointer;
font-family: bootstrap-icons;
font-size: .8em;
content: "\F285";
position: absolute;
margin-top: .2em;
margin-left: -$expand-stub-width;
transition: transform 0.35s ease;
transform-origin: .5em 50%;
@media (prefers-reduced-motion) {
& {
transition: none;
}
}
}
li.expanded > .expand-stub::before {
transform: rotate(90deg);
}
span.name-only {
font-weight: 600;
display: inline-block;
margin: .4rem 0;
}
form.filter {
display: flex;
position: relative;
align-items: center;
margin-bottom: 1rem;
>i.bi {
position: absolute;
left: .6rem;
opacity: .5;
}
>input {
padding-left: 2rem;
}
}
>.no-result {
font-size: .9em;
color: $secondary;
}
}
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { TemplateResult, html, render } from 'lit-html'
import { classMap } from 'lit-html/directives/class-map.js'
import { breakWordLit, meta, isExternalHref } from './helper'
export type TocNode = {
name: string
href?: string
expanded?: boolean
items?: TocNode[]
}
/**
* @returns active TOC nodes
*/
export async function renderToc(): Promise<TocNode[]> {
const tocrel = meta('docfx:tocrel')
if (!tocrel) {
return []
}
const disableTocFilter = meta('docfx:disabletocfilter') === 'true'
let tocFilter = disableTocFilter ? '' : (localStorage?.getItem('tocFilter') || '')
const tocUrl = new URL(tocrel.replace(/.html$/gi, '.json'), window.location.href)
const { items } = await (await fetch(tocUrl)).json()
const activeNodes = []
const selectedNodes = []
items.forEach(initTocNodes)
const tocContainer = document.getElementById('toc')
if (tocContainer) {
renderToc()
const activeElements = tocContainer.querySelectorAll('li.active')
const lastActiveElement = activeElements[activeElements.length - 1]
if (lastActiveElement) {
lastActiveElement.scrollIntoView({ block: 'nearest' })
}
}
if (selectedNodes.length > 0) {
renderNextArticle(items, selectedNodes[0])
}
return activeNodes.slice(0, -1)
function initTocNodes(node: TocNode): boolean {
let active
if (node.href) {
const url = new URL(node.href, tocUrl)
node.href = url.href
active = isExternalHref(url) ? false : normalizeUrlPath(url) === normalizeUrlPath(window.location)
if (active) {
if (node.items) {
node.expanded = true
}
selectedNodes.push(node)
}
}
if (node.items) {
for (const child of node.items) {
if (initTocNodes(child)) {
active = true
node.expanded = true
}
}
}
if (active) {
activeNodes.unshift(node)
return true
}
return false
}
function renderToc() {
render(html`${renderTocFilter()} ${renderTocNodes(items) || renderNoFilterResult()}`, tocContainer)
}
function renderTocNodes(nodes: TocNode[]): TemplateResult {
const result = nodes.map(node => {
const { href, name, items, expanded } = node
const isLeaf = !items || items.length <= 0
const children = isLeaf ? null : renderTocNodes(items)
if (tocFilter !== '' && !children && !name.toLowerCase().includes(tocFilter.toLowerCase())) {
return null
}
const dom = href
? html`<a class='${classMap({ 'nav-link': !activeNodes.includes(node) })}' href=${href}>${breakWordLit(name)}</a>`
: (isLeaf
? html`<span class='text-body-tertiary name-only'>${breakWordLit(name)}</a>`
: html`<a class='${classMap({ 'nav-link': !activeNodes.includes(node) })}' href='#' @click=${toggleExpand}>${breakWordLit(name)}</a>`)
const isExpanded = (tocFilter !== '' && expanded !== false && children != null) || expanded === true
return html`
<li class=${classMap({ expanded: isExpanded, active: activeNodes.includes(node) })}>
${isLeaf ? null : html`<span class='expand-stub' @click=${toggleExpand}></span>`}
${dom}
${children}
</li>`
function toggleExpand(e) {
e.preventDefault()
node.expanded = !isExpanded
renderToc()
}
}).filter(node => node)
return result.length > 0 ? html`<ul>${result}</ul>` : null
}
function renderTocFilter(): TemplateResult {
return disableTocFilter
? null
: html`
<form class='filter'>
<i class='bi bi-filter'></i>
<input class='form-control' @input=${filterToc} value='${tocFilter}' type='search' placeholder='Filter by title' autocomplete='off' aria-label='Filter by title'>
</form>`
function filterToc(e: Event) {
tocFilter = (<HTMLInputElement>e.target).value.trim()
localStorage?.setItem('tocFilter', tocFilter)
renderToc()
}
}
function renderNoFilterResult(): TemplateResult {
return tocFilter === '' ? null : html`<div class='no-result'>No results for "${tocFilter}"</div>`
}
function normalizeUrlPath(url: { pathname: string }): string {
return url.pathname.replace(/\/index\.html$/gi, '/')
}
}
function renderNextArticle(items: TocNode[], node: TocNode) {
const nextArticle = document.getElementById('nextArticle')
if (!nextArticle) {
return
}
const tocNodes = flattenTocNodesWithHref(items)
const i = tocNodes.findIndex(n => n === node)
const prev = tocNodes[i - 1]
const next = tocNodes[i + 1]
if (!prev && !next) {
return
}
const prevButton = prev ? html`<div class="prev"><span><i class='bi bi-chevron-left'></i> Previous</span> <a href="${prev.href}" rel="prev">${breakWordLit(prev.name)}</a></div>` : null
const nextButton = next ? html`<div class="next"><span>Next <i class='bi bi-chevron-right'></i></span> <a href="${next.href}" rel="next">${breakWordLit(next.name)}</a></div>` : null
render(html`${prevButton} ${nextButton}`, nextArticle)
function flattenTocNodesWithHref(items: TocNode[]) {
const result = []
for (const item of items) {
if (item.href) {
result.push(item)
}
if (item.items) {
result.push(...flattenTocNodesWithHref(item.items))
}
}
return result
}
}
function updateBaseStats() {
console.debug('Index stats initialized')
}
export function renderIndexStats() {
const windowPathname = window.location.pathname
if (windowPathname === '' || windowPathname === '/' || windowPathname === '/index.html') {
updateBaseStats()
}
}
.h1, .h2, .h3, .h4, .h5, h1, h2, h3, h4, h5 {
font-family: Montserrat,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
}
.navbar-brand {
font-family: Montserrat,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
font-weight: 700;
}
img {
max-width: 100%;
}
h2 {
margin-top: 4rem;
font-weight: 700;
}
h3 {
margin-top: 2rem;
}
.navbar-brand {
#logo {
max-width: 30px;
margin-right: .7rem;
}
}
.spaced-page {
padding: 6rem 0;
}
.display-1, .display-4 {
font-weight: 700;
}
.spaced-page-separator {
padding: 1rem;
}
.colorfull {
background-color: #1fc8db;
background-image: linear-gradient(140deg,#55e2fd,#58b2dd 50%,#61cece 75%);
border-radius: 1rem;
padding: 1rem;
}
.mr-05 {
margin-right: .5rem;
}
.stats {
padding: 4rem 0;
text-align: center;
text-transform: uppercase;
strong {
font-family: Montserrat,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
font-weight: 700;
margin: 0;
width: 100%;
display: block;
}
}
footer {
// border-top: 1px solid #2c2c2c;
// color: #6b6b6b;
font-size: .8rem;
padding: 1rem;
a {
color: #71a1ff;
text-decoration: none;
}
}
.col-12 {
.card {
height: 100%;
}
}
.card {
.card-body {
img {
margin-bottom: 2rem;
max-width: 100%;
width: 100px;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
exports.transform = function (model) {
if (model.memberLayout === 'SeparatePages') {
model = transformMemberPage(model);
}
for (var key in model) {
if (key[0] === '_') {
delete model[key]
}
}
return {
content: JSON.stringify(model)
};
}
function transformMemberPage(model) {
var groupNames = {
"constructor": { key: "constructorsInSubtitle" },
"field": { key: "fieldsInSubtitle" },
"property": { key: "propertiesInSubtitle" },
"method": { key: "methodsInSubtitle" },
"event": { key: "eventsInSubtitle" },
"operator": { key: "operatorsInSubtitle" },
"eii": { key: "eiisInSubtitle" },
};
groupChildren(model);
transformItem(model, 1);
return model;
function groupChildren(item) {
if (!item || !item.items || item.items.length == 0) {
return;
}
var grouped = {};
var items = [];
item.items.forEach(function (element) {
groupChildren(element);
if (element.type) {
var type = element.isEii ? "eii" : element.type.toLowerCase();
if (!grouped.hasOwnProperty(type)) {
if (!groupNames.hasOwnProperty(type)) {
groupNames[type] = {
name: element.type
};
console.log(type + " is not predefined type, use its type name as display name.")
}
grouped[type] = [];
}
grouped[type].push(element);
} else {
items.push(element);
}
}, this);
// With order defined in groupNames
for (var key in groupNames) {
if (groupNames.hasOwnProperty(key) && grouped.hasOwnProperty(key)) {
items.push({
name: model.__global[groupNames[key].key] || groupNames[key].name,
items: grouped[key]
})
}
}
item.items = items;
}
function transformItem(item, level) {
// set to null in case mustache looks up
item.topicHref = item.topicHref || null;
item.tocHref = item.tocHref || null;
item.name = item.name || null;
item.level = level;
if (item.items && item.items.length > 0) {
item.leaf = false;
var length = item.items.length;
for (var i = 0; i < length; i++) {
transformItem(item.items[i], level + 1);
};
} else {
item.items = [];
item.leaf = true;
}
}
}
{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}}
{{{content}}}
\ No newline at end of file
items:
- name: Home
href: /
- name: Documentation
href: /documentation
items:
- name: Getting started
href: documentation/getting-started.md
- name: NuGet Package
href: documentation/nuget.md
- name: Visual Studio Extension
href: documentation/extension.md
- name: Themes
href: documentation/themes.md
- name: Icons
href: documentation/icons.md
- name: Theme Watcher
href: documentation/system-theme-watcher.md
- name: Theme Accent
href: documentation/accent.md
- name: Navigation
href: documentation/navigation-view.md
- name: Menu
href: documentation/menu.md
- name: Gallery
href: documentation/gallery.md
- name: Releases
href: documentation/releases.md
- name: API
href: api/
......@@ -9,7 +9,7 @@
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemesDictionary Theme="Dark" />
<ui:ThemesDictionary ApplicationTheme="Dark" />
<ui:ControlsDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
......
......@@ -15,10 +15,10 @@ public object Convert(object value, Type targetType, object parameter, CultureIn
if (parameter is not String enumString)
throw new ArgumentException("ExceptionEnumToBooleanConverterParameterMustBeAnEnumName");
if (!Enum.IsDefined(typeof(Wpf.Ui.Appearance.ThemeType), value))
if (!Enum.IsDefined(typeof(Wpf.Ui.Appearance.ApplicationTheme), value))
throw new ArgumentException("ExceptionEnumToBooleanConverterValueMustBeAnEnum");
var enumValue = Enum.Parse(typeof(Wpf.Ui.Appearance.ThemeType), enumString);
var enumValue = Enum.Parse(typeof(Wpf.Ui.Appearance.ApplicationTheme), enumString);
return enumValue.Equals(value);
}
......@@ -28,6 +28,6 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu
if (parameter is not String enumString)
throw new ArgumentException("ExceptionEnumToBooleanConverterParameterMustBeAnEnumName");
return Enum.Parse(typeof(Wpf.Ui.Appearance.ThemeType), enumString);
return Enum.Parse(typeof(Wpf.Ui.Appearance.ApplicationTheme), enumString);
}
}
// This Source Code Form is subject to the terms of the MIT License.
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
......@@ -15,7 +15,10 @@ public partial class SettingsViewModel : ObservableObject, INavigationAware
private string _appVersion = String.Empty;
[ObservableProperty]
private Wpf.Ui.Appearance.ThemeType _currentTheme = Wpf.Ui.Appearance.ThemeType.Unknown;
private Wpf.Ui.Appearance.ApplicationTheme _currentApplicationTheme = Wpf.Ui
.Appearance
.ApplicationTheme
.Unknown;
public void OnNavigatedTo()
{
......@@ -27,7 +30,7 @@ public void OnNavigatedTo()
private void InitializeViewModel()
{
CurrentTheme = Wpf.Ui.Appearance.Theme.GetAppTheme();
CurrentApplicationTheme = Wpf.Ui.Appearance.ApplicationThemeManager.GetAppTheme();
AppVersion = $"Wpf.Ui.Demo.Mvvm - {GetAssemblyVersion()}";
_isInitialized = true;
......@@ -45,20 +48,24 @@ private void OnChangeTheme(string parameter)
switch (parameter)
{
case "theme_light":
if (CurrentTheme == Wpf.Ui.Appearance.ThemeType.Light)
if (CurrentApplicationTheme == Wpf.Ui.Appearance.ApplicationTheme.Light)
break;
Wpf.Ui.Appearance.Theme.Apply(Wpf.Ui.Appearance.ThemeType.Light);
CurrentTheme = Wpf.Ui.Appearance.ThemeType.Light;
Wpf.Ui.Appearance.ApplicationThemeManager.Apply(
Wpf.Ui.Appearance.ApplicationTheme.Light
);
CurrentApplicationTheme = Wpf.Ui.Appearance.ApplicationTheme.Light;
break;
default:
if (CurrentTheme == Wpf.Ui.Appearance.ThemeType.Dark)
if (CurrentApplicationTheme == Wpf.Ui.Appearance.ApplicationTheme.Dark)
break;
Wpf.Ui.Appearance.Theme.Apply(Wpf.Ui.Appearance.ThemeType.Dark);
CurrentTheme = Wpf.Ui.Appearance.ThemeType.Dark;
Wpf.Ui.Appearance.ApplicationThemeManager.Apply(
Wpf.Ui.Appearance.ApplicationTheme.Dark
);
CurrentApplicationTheme = Wpf.Ui.Appearance.ApplicationTheme.Dark;
break;
}
......
<ui:FluentWindow
<ui:FluentWindow
x:Class="Wpf.Ui.Demo.Mvvm.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wpf.Ui.Demo.Mvvm.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tray="http://schemas.lepo.co/wpfui/2022/xaml/tray"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Title="{Binding ViewModel.ApplicationTitle, Mode=OneWay}"
Width="1100"
......@@ -60,18 +61,17 @@
<ui:TitleBar
Title="{Binding ViewModel.ApplicationTitle, Mode=OneWay}"
Grid.Row="0"
Icon="pack://application:,,,/Assets/applicationIcon-256.png">
<ui:TitleBar.Tray>
<ui:NotifyIcon
FocusOnLeftClick="True"
Icon="pack://application:,,,/Assets/applicationIcon-256.png"
MenuOnRightClick="True"
TooltipText="WPF UI - MVVM Demo">
<ui:NotifyIcon.Menu>
<ContextMenu ItemsSource="{Binding ViewModel.TrayMenuItems, Mode=OneWay}" />
</ui:NotifyIcon.Menu>
</ui:NotifyIcon>
</ui:TitleBar.Tray>
</ui:TitleBar>
Icon="pack://application:,,,/Assets/applicationIcon-256.png" />
<tray:NotifyIcon
Grid.Row="0"
FocusOnLeftClick="True"
Icon="pack://application:,,,/Assets/applicationIcon-256.png"
MenuOnRightClick="True"
TooltipText="WPF UI - MVVM Demo">
<tray:NotifyIcon.Menu>
<ContextMenu ItemsSource="{Binding ViewModel.TrayMenuItems, Mode=OneWay}" />
</tray:NotifyIcon.Menu>
</tray:NotifyIcon>
</Grid>
</ui:FluentWindow>
// This Source Code Form is subject to the terms of the MIT License.
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
......@@ -23,7 +23,7 @@ INavigationService navigationService
ViewModel = viewModel;
DataContext = this;
Appearance.Watcher.Watch(this);
Appearance.SystemThemeWatcher.Watch(this);
InitializeComponent();
SetPageService(pageService);
......
......@@ -32,14 +32,14 @@
CommandParameter="theme_light"
Content="Light"
GroupName="themeSelect"
IsChecked="{Binding ViewModel.CurrentTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Light, Mode=OneWay}" />
IsChecked="{Binding ViewModel.CurrentApplicationTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Light, Mode=OneWay}" />
<RadioButton
Margin="0,8,0,0"
Command="{Binding ViewModel.ChangeThemeCommand, Mode=OneWay}"
CommandParameter="theme_dark"
Content="Dark"
GroupName="themeSelect"
IsChecked="{Binding ViewModel.CurrentTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Dark, Mode=OneWay}" />
IsChecked="{Binding ViewModel.CurrentApplicationTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Dark, Mode=OneWay}" />
<TextBlock
Margin="0,24,0,0"
......
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>$(Version)</Version>
<AssemblyName>WPF UI - MVVM Demo</AssemblyName>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows10.0.22621.0</TargetFramework>
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>10.0</LangVersion>
<UseWPF>true</UseWPF>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>applicationIcon.ico</ApplicationIcon>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
......@@ -30,6 +27,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wpf.Ui.ToastNotifications\Wpf.Ui.ToastNotifications.csproj" />
<ProjectReference Include="..\Wpf.Ui.Tray\Wpf.Ui.Tray.csproj" />
<ProjectReference Include="..\Wpf.Ui\Wpf.Ui.csproj" />
</ItemGroup>
......@@ -38,4 +37,15 @@
<Resource Include="Assets\applicationIcon-256.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="WpfAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
......@@ -7,7 +7,7 @@
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemesDictionary Theme="Dark" />
<ui:ThemesDictionary ApplicationTheme="Dark" />
<ui:ControlsDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
......
<ui:FluentWindow
<ui:FluentWindow
x:Class="Wpf.Ui.Demo.Simple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
......@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Wpf.Ui.Demo.Simple"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="clr-namespace:Wpf.Ui.Demo.Simple.Views.Pages"
xmlns:tray="http://schemas.lepo.co/wpfui/2022/xaml/tray"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Title="WPF UI - Simple Demo"
Width="1100"
......@@ -74,18 +75,17 @@
<ui:TitleBar
Title="WPF UI - Simple Demo"
Grid.Row="0"
Icon="pack://application:,,,/Assets/applicationIcon-256.png">
<ui:TitleBar.Tray>
<ui:NotifyIcon
FocusOnLeftClick="True"
Icon="pack://application:,,,/Assets/applicationIcon-256.png"
MenuOnRightClick="True"
TooltipText="Wpf.Ui.Demo.Simple">
<ui:NotifyIcon.Menu>
<ContextMenu ItemsSource="{Binding ViewModel.TrayMenuItems, Mode=OneWay}" />
</ui:NotifyIcon.Menu>
</ui:NotifyIcon>
</ui:TitleBar.Tray>
</ui:TitleBar>
Icon="pack://application:,,,/Assets/applicationIcon-256.png" />
<tray:NotifyIcon
Grid.Row="0"
FocusOnLeftClick="True"
Icon="pack://application:,,,/Assets/applicationIcon-256.png"
MenuOnRightClick="True"
TooltipText="Wpf.Ui.Demo.Simple">
<tray:NotifyIcon.Menu>
<ContextMenu ItemsSource="{Binding ViewModel.TrayMenuItems, Mode=OneWay}" />
</tray:NotifyIcon.Menu>
</tray:NotifyIcon>
</Grid>
</ui:FluentWindow>
// This Source Code Form is subject to the terms of the MIT License.
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
......@@ -16,7 +16,7 @@ public MainWindow()
{
DataContext = this;
Appearance.Watcher.Watch(this);
Appearance.SystemThemeWatcher.Watch(this);
InitializeComponent();
......
// This Source Code Form is subject to the terms of the MIT License.
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
......@@ -20,7 +20,7 @@ public SettingsPage()
AppVersionTextBlock.Text = $"WPF UI - Simple Demo - {GetAssemblyVersion()}";
if (Appearance.Theme.GetAppTheme() == ThemeType.Dark)
if (Appearance.ApplicationThemeManager.GetAppTheme() == ApplicationTheme.Dark)
DarkThemeRadioButton.IsChecked = true;
else
LightThemeRadioButton.IsChecked = true;
......@@ -28,12 +28,12 @@ public SettingsPage()
private void OnLightThemeRadioButtonChecked(object sender, RoutedEventArgs e)
{
Appearance.Theme.Apply(ThemeType.Light);
Appearance.ApplicationThemeManager.Apply(ApplicationTheme.Light);
}
private void OnDarkThemeRadioButtonChecked(object sender, RoutedEventArgs e)
{
Appearance.Theme.Apply(ThemeType.Dark);
Appearance.ApplicationThemeManager.Apply(ApplicationTheme.Dark);
}
private string GetAssemblyVersion()
......
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>$(Version)</Version>
<AssemblyName>WPF UI - Simple Demo</AssemblyName>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows10.0.22621.0</TargetFramework>
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
<UseWPF>true</UseWPF>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>applicationIcon.ico</ApplicationIcon>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
......@@ -24,6 +21,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wpf.Ui.ToastNotifications\Wpf.Ui.ToastNotifications.csproj" />
<ProjectReference Include="..\Wpf.Ui.Tray\Wpf.Ui.Tray.csproj" />
<ProjectReference Include="..\Wpf.Ui\Wpf.Ui.csproj" />
</ItemGroup>
......@@ -32,4 +31,15 @@
<Resource Include="Assets\applicationIcon-256.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="WpfAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Threading;
using Wpf.Ui.Mvvm.Contracts;
using Wpf.Ui.Mvvm.Services;
using $safeprojectname$.Models;
using $safeprojectname$.Services;
namespace $safeprojectname$
{
......@@ -27,35 +27,7 @@ public partial class App
.ConfigureAppConfiguration(c => { c.SetBasePath(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)); })
.ConfigureServices((context, services) =>
{
// App Host
services.AddHostedService<ApplicationHostService>();
// Page resolver service
services.AddSingleton<IPageService, PageService>();
// Theme manipulation
services.AddSingleton<IThemeService, ThemeService>();
// TaskBar manipulation
services.AddSingleton<ITaskBarService, TaskBarService>();
// Service containing navigation, same as INavigationWindow... but without window
services.AddSingleton<INavigationService, NavigationService>();
// Main window with navigation
services.AddScoped<INavigationWindow, Views.Windows.MainWindow>();
services.AddScoped<ViewModels.MainWindowViewModel>();
// Views and ViewModels
services.AddScoped<Views.Pages.DashboardPage>();
services.AddScoped<ViewModels.DashboardViewModel>();
services.AddScoped<Views.Pages.DataPage>();
services.AddScoped<ViewModels.DataViewModel>();
services.AddScoped<Views.Pages.SettingsPage>();
services.AddScoped<ViewModels.SettingsViewModel>();
// Configuration
services.Configure<AppConfig>(context.Configuration.GetSection(nameof(AppConfig)));
throw new NotImplementedException("No service or window was registered.");
}).Build();
/// <summary>
......@@ -72,9 +44,9 @@ public static T GetService<T>()
/// <summary>
/// Occurs when the application is loading.
/// </summary>
private async void OnStartup(object sender, StartupEventArgs e)
private void OnStartup(object sender, StartupEventArgs e)
{
await _host.StartAsync();
_host.Start();
}
/// <summary>
......@@ -95,4 +67,4 @@ private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledEx
// For more info see https://docs.microsoft.com/en-us/dotnet/api/system.windows.application.dispatcherunhandledexception?view=windowsdesktop-6.0
}
}
}
\ No newline at end of file
}
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
using System.Windows;
[assembly: ThemeInfo(
......
......@@ -6,6 +6,7 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>wpfui-icon.ico</ApplicationIcon>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
</PropertyGroup>
......@@ -14,9 +15,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="WPF-UI" Version="3.0.0-preview.3" />
<PackageReference Include="WPF-UI" Version="3.0.0-preview.4" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1 " />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0 "/>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1 "/>
</ItemGroup>
<ItemGroup>
......
<VSTemplate Version="3.0.0"
xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
<TemplateData>
<Name>WPF UI - Fluent Navigation (MVVM | DI)</Name>
<Description>Template for creating WPF UI project with MVVM pattern, Dependency Injection, Fluent navigation and Mica background.</Description>
<Name>WPF UI - Blank project</Name>
<Description>Empty WPF UI project with generic .NET host, dependency injection and initialized resources.</Description>
<ProjectType>CSharp</ProjectType>
<ProjectSubType></ProjectSubType>
<SortOrder>1000</SortOrder>
......@@ -12,57 +12,26 @@
<LocationField>Enabled</LocationField>
<EnableLocationBrowseButton>true</EnableLocationBrowseButton>
<CreateInPlace>true</CreateInPlace>
<Icon>__TemplateIcon.png</Icon>
<LanguageTag>csharp</LanguageTag>
<LanguageTag>XAML</LanguageTag>
<PlatformTag>Windows</PlatformTag>
<ProjectTypeTag>Desktop</ProjectTypeTag>
<ProjectTypeTag>WPF UI</ProjectTypeTag>
<ProjectTypeTag>MVVM</ProjectTypeTag>
<Icon>__TemplateIcon.png</Icon>
<PreviewImage>__PreviewImage.png</PreviewImage>
</TemplateData>
<TemplateContent>
<Project TargetFileName="Wpf.Ui.Mvvm.FluentNavigation.csproj" File="Wpf.Ui.Mvvm.FluentNavigation.csproj" ReplaceParameters="true">
<Project TargetFileName="Wpf.Ui.Blank.csproj" File="Wpf.Ui.Blank.csproj" ReplaceParameters="true">
<Folder Name="Assets" TargetFolderName="Assets">
<ProjectItem ReplaceParameters="false" TargetFileName="applicationIcon-256.png">applicationIcon-256.png</ProjectItem>
<ProjectItem ReplaceParameters="false" TargetFileName="applicationIcon-1024.png">applicationIcon-1024.png</ProjectItem>
</Folder>
<Folder Name="Helpers" TargetFolderName="Helpers">
<ProjectItem ReplaceParameters="true" TargetFileName="EnumToBooleanConverter.cs">EnumToBooleanConverter.cs</ProjectItem>
</Folder>
<Folder Name="Models" TargetFolderName="Models">
<ProjectItem ReplaceParameters="true" TargetFileName="AppConfig.cs">AppConfig.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="DataColor.cs">DataColor.cs</ProjectItem>
</Folder>
<Folder Name="Services" TargetFolderName="Services">
<ProjectItem ReplaceParameters="true" TargetFileName="ApplicationHostService.cs">ApplicationHostService.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="PageService.cs">PageService.cs</ProjectItem>
</Folder>
<Folder Name="ViewModels" TargetFolderName="ViewModels">
<ProjectItem ReplaceParameters="true" TargetFileName="MainWindowViewModel.cs">MainWindowViewModel.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="DashboardViewModel.cs">DashboardViewModel.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="DataViewModel.cs">DataViewModel.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="SettingsViewModel.cs">SettingsViewModel.cs</ProjectItem>
</Folder>
<Folder Name="Views" TargetFolderName="Views">
<Folder Name="Pages" TargetFolderName="Pages">
<ProjectItem ReplaceParameters="true" TargetFileName="DashboardPage.xaml">DashboardPage.xaml</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="DashboardPage.xaml.cs">DashboardPage.xaml.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="DataPage.xaml">DataPage.xaml</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="DataPage.xaml.cs">DataPage.xaml.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="SettingsPage.xaml">SettingsPage.xaml</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="SettingsPage.xaml.cs">SettingsPage.xaml.cs</ProjectItem>
</Folder>
<Folder Name="Windows" TargetFolderName="Windows">
<ProjectItem ReplaceParameters="true" TargetFileName="MainWindow.xaml">MainWindow.xaml</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="MainWindow.xaml.cs">MainWindow.xaml.cs</ProjectItem>
</Folder>
<ProjectItem ReplaceParameters="false" TargetFileName="wpfui-icon-256.png">wpfui-icon-256.png</ProjectItem>
<ProjectItem ReplaceParameters="false" TargetFileName="wpfui-icon-1024.png">wpfui-icon-1024.png</ProjectItem>
</Folder>
<ProjectItem ReplaceParameters="true" TargetFileName="app.manifest">app.manifest</ProjectItem>
<ProjectItem ReplaceParameters="false" TargetFileName="applicationIcon.ico">applicationIcon.ico</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="App.xaml">App.xaml</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="App.xaml.cs">App.xaml.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
<ProjectItem ReplaceParameters="false" TargetFileName="wpfui-icon.ico">wpfui-icon.ico</ProjectItem>
</Project>
</TemplateContent>
</VSTemplate>
\ No newline at end of file
</VSTemplate>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="15.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MinimumVisualStudioVersion>17.0</MinimumVisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
......@@ -33,11 +34,11 @@
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<ProjectGuid>{08BE0726-8E3D-4624-AC9E-07816A14E906}</ProjectGuid>
<ProjectGuid>{ab3d44b5-9491-487e-a134-9ac5bed2b981}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Wpf.Ui.Mvvm.FluentNavigation</RootNamespace>
<AssemblyName>Wpf.Ui.Mvvm.FluentNavigation</AssemblyName>
<RootNamespace>Wpf.Ui.Extension.Template.Blank</RootNamespace>
<AssemblyName>Wpf.Ui.Extension.Template.Blank</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<GeneratePkgDefFile>false</GeneratePkgDefFile>
<IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer>
......@@ -68,11 +69,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Content Include="Wpf.Ui.Mvvm.FluentNavigation.csproj" />
<Content Include="__PreviewImage.png" />
<Content Include="__TemplateIcon.png" />
<VSTemplate Include="Wpf.Ui.Mvvm.FluentNavigation.vstemplate">
<OutputSubPath>Wpf.Ui.Mvvm.FluentNavigation</OutputSubPath>
<Content Include="Wpf.Ui.Blank.csproj" />
<VSTemplate Include="Wpf.Ui.Blank.vstemplate">
<OutputSubPath>Wpf.Ui.Blank</OutputSubPath>
</VSTemplate>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
......@@ -84,4 +83,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
</Project>
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
using System.Windows;
[assembly: ThemeInfo(
......
using System;
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
using System.Globalization;
using System.Windows.Data;
......@@ -6,17 +10,17 @@ namespace $safeprojectname$.Helpers
{
internal class EnumToBooleanConverter : IValueConverter
{
public EnumToBooleanConverter()
{
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is not String enumString)
{
throw new ArgumentException("ExceptionEnumToBooleanConverterParameterMustBeAnEnumName");
}
if (!Enum.IsDefined(typeof(Wpf.Ui.Appearance.ThemeType), value))
{
throw new ArgumentException("ExceptionEnumToBooleanConverterValueMustBeAnEnum");
}
var enumValue = Enum.Parse(typeof(Wpf.Ui.Appearance.ThemeType), enumString);
......@@ -26,7 +30,9 @@ public object Convert(object value, Type targetType, object parameter, CultureIn
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is not String enumString)
{
throw new ArgumentException("ExceptionEnumToBooleanConverterParameterMustBeAnEnumName");
}
return Enum.Parse(typeof(Wpf.Ui.Appearance.ThemeType), enumString);
}
......
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
namespace $safeprojectname$.Models
{
public class AppConfig
{
public string ConfigurationsFolder { get; set; }
public string AppPropertiesFileName { get; set; }
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册