├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── config.yml │ └── feature_request.yaml └── workflows │ └── library.nuget.yml ├── .gitignore ├── LICENSE ├── README.md ├── Settings.XamlStyler ├── Wpf.Ui.Violeta.sln ├── branding ├── geometric_splash.psd ├── microsoft-fluent-resources.psd ├── wpfui.ico ├── wpfui.png ├── wpfui.psd └── wpfui_full.png └── src ├── Wpf.Ui.Test ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── GlobalUsing.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Resources │ ├── Images │ │ ├── ProfilePicture.ico │ │ └── Tiara.png │ └── Strings │ │ └── Sample.md ├── TrayIconManager.cs └── Wpf.Ui.Test.csproj └── Wpf.Ui.Violeta ├── Appearance ├── ResourceDictionaryManager.cs ├── SystemMenuThemeManager.cs └── ThemeManager.cs ├── AssemblyInfo.cs ├── Controls ├── BitmapIcon │ └── BitmapIcon.cs ├── Button │ └── Button.xaml ├── CachedImage │ ├── CachedImage.cs │ └── FileCacheForImage.cs ├── ContentDialog │ ├── ContentDialog.cs │ ├── ContentDialog.xaml │ ├── ContentDialogButton.cs │ ├── ContentDialogButtonClickDeferral.cs │ ├── ContentDialogButtonClickEventArgs.cs │ ├── ContentDialogClosedEventArgs.cs │ ├── ContentDialogClosingDeferral.cs │ ├── ContentDialogClosingEventArgs.cs │ ├── ContentDialogHostService.cs │ ├── ContentDialogOpenedEventArgs.cs │ ├── ContentDialogPlacement.cs │ ├── ContentDialogResult.cs │ └── CornerRadiusFilterConverter.cs ├── DataGrid │ ├── DataGrid.xaml │ └── LegacyDataGrid.xaml ├── ExceptionReport │ ├── ExceptionReport.cs │ ├── ExceptionWindow.xaml │ └── ExceptionWindow.xaml.cs ├── Expander │ └── Expander.xaml ├── Flyout │ └── FlyoutService.cs ├── FontIcon │ ├── CustomFontSymbols.cs │ ├── FontIconFallback.cs │ ├── FontIconFallback.xaml │ └── FontSymbols.cs ├── ImageView │ ├── AnimatedImage.cs │ ├── AnimationProvider.cs │ ├── BitmapImageProvider.cs │ ├── ImageView.xaml │ └── ImageView.xaml.cs ├── Layout │ ├── AutoGrid.cs │ ├── Border.cs │ ├── Grid.cs │ ├── StackPanel.cs │ └── WrapPanel.cs ├── MessageBox │ ├── MessageBox.Image.cs │ ├── MessageBox.cs │ ├── MessageBoxButtonClickDeferral.cs │ ├── MessageBoxButtonClickEventArgs.cs │ ├── MessageBoxClosedEventArgs.cs │ ├── MessageBoxClosingDeferral.cs │ ├── MessageBoxClosingEventArgs.cs │ ├── MessageBoxContentTemplateSelector.cs │ ├── MessageBoxDialog.cs │ ├── MessageBoxDialog.xaml │ ├── MessageBoxIcon.cs │ ├── MessageBoxIconConverter.cs │ ├── MessageBoxIconExtensions.cs │ ├── MessageBoxIconForegroundConverter.cs │ ├── MessageBoxImageExtensions.cs │ ├── MessageBoxOpenedEventArgs.cs │ ├── MessageBoxSymbolGlyph.cs │ └── MessageBoxTemplateSettings.cs ├── PendingBox │ ├── IPendingHandler.cs │ ├── Loading.cs │ ├── Loading.xaml │ ├── PendingBox.cs │ ├── PendingBoxDialog.cs │ ├── PendingBoxDialog.xaml │ └── PendingHandler.cs ├── PersonPicture │ ├── InitialsGenerator.cs │ ├── PersonPicture.cs │ ├── PersonPicture.properties.cs │ ├── PersonPicture.xaml │ ├── PersonPictureAutomationPeer.cs │ └── PersonPictureTemplateSettings.cs ├── Primitives │ ├── ContentPresenterEx.cs │ ├── CustomPopupPlacementHelper.cs │ ├── IconElementEx.cs │ ├── MenuItemGroup.cs │ ├── PopupPositioner.cs │ ├── RadioButtonGroup.cs │ ├── SimpleVisualStateManager.cs │ ├── ThemeShadowChrome.cs │ ├── ToggleButtonGroup.cs │ ├── VisualStateGroupListener.cs │ └── VisualTree.cs ├── SmoothScrollViewer │ ├── SmoothScrollViewer.cs │ └── SmoothScrollViewer.xaml ├── Splash │ ├── Splash.cs │ ├── SplashConfig.cs │ ├── SplashWindow.xaml │ └── SplashWindow.xaml.cs ├── TextBox │ └── TextBox.xaml ├── Toast │ ├── Toast.cs │ ├── ToastConfig.cs │ ├── ToastControl.xaml │ ├── ToastControl.xaml.cs │ ├── ToastIcon.cs │ ├── ToastIconConverter.cs │ ├── ToastIconForegroundConverter.cs │ ├── ToastIconVisibilityConverter.cs │ └── ToastLocation.cs ├── TreeListView │ ├── TreeLevelToIndentConverter.cs │ ├── TreeListView.cs │ ├── TreeListView.xaml │ ├── TreeListViewItem.cs │ └── TreeRowExpander.cs └── TreeModelListView │ ├── ITreeModel.cs │ ├── TreeModelCanExpandConverter.cs │ ├── TreeModelCollection{T}.cs │ ├── TreeModelLevelToIndentConverter.cs │ ├── TreeModelListView.cs │ ├── TreeModelListView.xaml │ ├── TreeModelListViewItem.cs │ ├── TreeModelNode.cs │ ├── TreeModelObject{T}.cs │ ├── TreeModelRowCollection{T}.cs │ └── TreeModelRowExpander.cs ├── Converters ├── FileSizeStringConverter.cs └── PathToIconConverter.cs ├── GlobalSuppressions.cs ├── Markup ├── ControlsDictionary.cs ├── StaticResourceExtension.cs └── ThemesDictionary.cs ├── Resources ├── Fonts │ ├── Segoe Fluent Icons.ttf │ └── Violeta Fluent Icons.ttf ├── Images │ ├── background-b.png │ └── background.png ├── Localization │ ├── SH.Designer.cs │ ├── SH.en.resx │ ├── SH.fr.resx │ ├── SH.id.resx │ ├── SH.ja.resx │ ├── SH.ko.resx │ ├── SH.pt.resx │ ├── SH.resx │ ├── SH.ru.resx │ ├── SH.vi.resx │ └── SH.zh-Hant.resx ├── ManifestResourceProvider.cs ├── ResourcesProvider.cs ├── Theme │ ├── Dark.xaml │ └── Light.xaml ├── Variables.xaml └── Wpf.Ui.xaml ├── Threading ├── ApplicationDispatcher.cs └── STAThread.cs ├── Win32 ├── DpiAware.cs ├── DpiHelper.cs ├── Gdi32.cs ├── IconManager.cs ├── NTdll.cs ├── SHCore.cs ├── Shell32.cs ├── Structs │ ├── POINT.cs │ └── RECT.cs ├── TrayIconHost.cs ├── User32.cs └── UxTheme.cs └── Wpf.Ui.Violeta.csproj /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Create a report to help us improve 3 | title: "Bug title" 4 | labels: [bug] 5 | body: 6 | - type: textarea 7 | validations: 8 | required: true 9 | attributes: 10 | label: Describe the bug 11 | description: A clear and concise description of what the bug is 12 | - type: textarea 13 | validations: 14 | required: true 15 | attributes: 16 | label: To Reproduce 17 | description: Steps to reproduce the behavior 18 | - type: textarea 19 | validations: 20 | required: true 21 | attributes: 22 | label: Expected behavior 23 | description: A clear and concise description of what you expected to happen 24 | - type: textarea 25 | attributes: 26 | label: Screenshots 27 | description: If applicable, add screenshots to help explain your problem 28 | - type: textarea 29 | validations: 30 | required: true 31 | attributes: 32 | label: OS version 33 | description: Which OS versions did you see the issue on? 34 | - type: textarea 35 | validations: 36 | required: true 37 | attributes: 38 | label: .NET version 39 | description: Which .NET versions did you see the issue on? 40 | - type: textarea 41 | validations: 42 | required: true 43 | attributes: 44 | label: WPF-UI NuGet version 45 | description: Which WPF-UI NuGet versions did you see the issue on? 46 | - type: textarea 47 | attributes: 48 | label: Additional context 49 | description: Add any other context about the problem here -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Documentation 4 | url: https://wpfui.lepo.co/documentation/ 5 | about: Find out how WPF UI works. 6 | - name: Tutorial 7 | url: https://wpfui.lepo.co/documentation/tutorial 8 | about: Having trouble with the basics? Check out the tutorial! 9 | - name: lepo.co contact 10 | url: https://lepo.co/contact 11 | about: Do you want to establish business contact? Let us know. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for the wpfui 3 | title: "Feature request title" 4 | labels: [enhancement] 5 | body: 6 | - type: textarea 7 | validations: 8 | required: true 9 | attributes: 10 | label: Is your feature request related to a problem? Please describe 11 | description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | - type: textarea 13 | validations: 14 | required: true 15 | attributes: 16 | label: Describe the solution you'd like 17 | description: A clear and concise description of what you want to happen 18 | - type: textarea 19 | attributes: 20 | label: Describe alternatives you've considered 21 | description: A clear and concise description of any alternative solutions or features you've considered 22 | - type: textarea 23 | attributes: 24 | label: Additional context 25 | description: Add any other context or screenshots about the feature request here -------------------------------------------------------------------------------- /.github/workflows/library.nuget.yml: -------------------------------------------------------------------------------- 1 | name: wpfui.violeta 2 | 3 | on: 4 | release: 5 | types: [created] 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v4 19 | with: 20 | dotnet-version: 9.0.x 21 | 22 | - name: Setup Windows SDK 23 | uses: GuillaumeFalourd/setup-windows10-sdk-action@v1.5 24 | 25 | - name: Install dependencies 26 | run: dotnet restore 27 | 28 | - name: Build 29 | run: dotnet build --configuration Release --no-restore 30 | 31 | - name: Pack 32 | run: dotnet pack src\Wpf.Ui.Violeta\Wpf.Ui.Violeta.csproj -c Release --no-build -o out 33 | 34 | - name: Push NuGet package 35 | # run: dotnet nuget push "out\*.nupkg" -k ${{secrets.NUGET_AUTH_TOKEN}} --source https://api.nuget.org/v3/index.json 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: nuget-packages 39 | path: out/*.nupkg 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 ema 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Settings.XamlStyler: -------------------------------------------------------------------------------- 1 | { 2 | "AttributesTolerance": 2, 3 | "KeepFirstAttributeOnSameLine": false, 4 | "MaxAttributeCharactersPerLine": 0, 5 | "MaxAttributesPerLine": 1, 6 | "NewlineExemptionElements": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransform, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter", 7 | "SeparateByGroups": false, 8 | "AttributeIndentation": 0, 9 | "AttributeIndentationStyle": 1, 10 | "RemoveDesignTimeReferences": false, 11 | "IgnoreDesignTimeReferencePrefix": false, 12 | "EnableAttributeReordering": true, 13 | "AttributeOrderingRuleGroups": [ 14 | "x:Class", 15 | "xmlns, xmlns:x", 16 | "xmlns:*", 17 | "x:Key, Key, x:Name, Name, x:Uid, Uid, Title", 18 | "Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom", 19 | "Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight", 20 | "Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex", 21 | "*:*, *", 22 | "PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint", 23 | "mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText", 24 | "Storyboard.*, From, To, Duration" 25 | ], 26 | "FirstLineAttributes": "", 27 | "OrderAttributesByName": true, 28 | "PutEndingBracketOnNewLine": false, 29 | "RemoveEndingTagOfEmptyElement": true, 30 | "SpaceBeforeClosingSlash": true, 31 | "RootElementLineBreakRule": 0, 32 | "ReorderVSM": 2, 33 | "ReorderGridChildren": false, 34 | "ReorderCanvasChildren": false, 35 | "ReorderSetters": 0, 36 | "FormatMarkupExtension": true, 37 | "NoNewLineMarkupExtensions": "x:Bind, Binding", 38 | "ThicknessSeparator": 2, 39 | "ThicknessAttributes": "Margin, Padding, BorderThickness, ThumbnailClipMargin", 40 | "FormatOnSave": true, 41 | "CommentPadding": 2, 42 | "IndentSize": 4, 43 | "IndentWithTabs": false 44 | } -------------------------------------------------------------------------------- /Wpf.Ui.Violeta.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34902.65 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wpf.Ui.Test", "src\Wpf.Ui.Test\Wpf.Ui.Test.csproj", "{DB963C29-774D-4E63-A6C0-BEE52A23E40B}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wpf.Ui.Violeta", "src\Wpf.Ui.Violeta\Wpf.Ui.Violeta.csproj", "{2DBA3326-F339-48CB-BCCC-F2C4FBF2E612}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {DB963C29-774D-4E63-A6C0-BEE52A23E40B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {DB963C29-774D-4E63-A6C0-BEE52A23E40B}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {DB963C29-774D-4E63-A6C0-BEE52A23E40B}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {DB963C29-774D-4E63-A6C0-BEE52A23E40B}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {2DBA3326-F339-48CB-BCCC-F2C4FBF2E612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {2DBA3326-F339-48CB-BCCC-F2C4FBF2E612}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {2DBA3326-F339-48CB-BCCC-F2C4FBF2E612}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {2DBA3326-F339-48CB-BCCC-F2C4FBF2E612}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {0BEEA15F-4DFC-4E8B-B5B6-2E2DECE1E579} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /branding/geometric_splash.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/branding/geometric_splash.psd -------------------------------------------------------------------------------- /branding/microsoft-fluent-resources.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/branding/microsoft-fluent-resources.psd -------------------------------------------------------------------------------- /branding/wpfui.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/branding/wpfui.ico -------------------------------------------------------------------------------- /branding/wpfui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/branding/wpfui.png -------------------------------------------------------------------------------- /branding/wpfui.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/branding/wpfui.psd -------------------------------------------------------------------------------- /branding/wpfui_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/branding/wpfui_full.png -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/App.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | using System.Windows.Threading; 4 | using Wpf.Ui.Violeta.Appearance; 5 | using Wpf.Ui.Violeta.Controls; 6 | using Wpf.Ui.Violeta.Resources; 7 | 8 | namespace Wpf.Ui.Test; 9 | 10 | public partial class App : Application 11 | { 12 | public App() 13 | { 14 | SystemMenuThemeManager.Apply(); 15 | TrayIconManager.Start(); 16 | Splash.ShowAsync("pack://application:,,,/Wpf.Ui.Test;component/wpfui.png", 0.98d); 17 | InitializeComponent(); 18 | 19 | DispatcherUnhandledException += (object s, DispatcherUnhandledExceptionEventArgs e) => 20 | { 21 | Debug.WriteLine("Application.DispatcherUnhandledException " + e.Exception?.ToString() ?? string.Empty); 22 | ExceptionReport.Show(e.Exception!); 23 | e.Handled = true; 24 | }; 25 | 26 | string sampleMd = ResourcesProvider.GetString(@"pack://application:,,,/Resources/Strings/Sample.md"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/GlobalUsing.cs: -------------------------------------------------------------------------------- 1 | global using MessageBox = Wpf.Ui.Violeta.Controls.MessageBox; 2 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/Resources/Images/ProfilePicture.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/src/Wpf.Ui.Test/Resources/Images/ProfilePicture.ico -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/Resources/Images/Tiara.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/src/Wpf.Ui.Test/Resources/Images/Tiara.png -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/Resources/Strings/Sample.md: -------------------------------------------------------------------------------- 1 | # Sample 2 | 3 | ## H2 -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/TrayIconManager.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using System; 4 | using System.ComponentModel; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Reflection; 8 | using System.Runtime.InteropServices; 9 | using System.Windows; 10 | using Wpf.Ui.Violeta.Resources; 11 | using Wpf.Ui.Violeta.Win32; 12 | 13 | namespace Wpf.Ui.Test; 14 | 15 | internal partial class TrayIconManager 16 | { 17 | private static TrayIconManager _instance = null!; 18 | 19 | private readonly TrayIconHost? _iconHost = null; 20 | 21 | private TrayIconManager() 22 | { 23 | using Win32Icon icon = new(ResourcesProvider.GetStream(new Uri("pack://application:,,,/Wpf.Ui.Test;component/Resources/Images/ProfilePicture.ico"))); 24 | 25 | _iconHost = new TrayIconHost() 26 | { 27 | ToolTipText = "Wpf.Ui.Test", 28 | Icon = icon.Handle, 29 | Menu = 30 | [ 31 | new TrayMenuItem() 32 | { 33 | Header = Version, 34 | IsEnabled = false, 35 | }, 36 | new TraySeparator(), 37 | new TrayMenuItem() 38 | { 39 | Header = "Restart", 40 | Command = RestartCommand, 41 | }, 42 | new TrayMenuItem() 43 | { 44 | Header = "Exit", 45 | Command = ExitCommand, 46 | } 47 | ], 48 | }; 49 | 50 | _iconHost.LeftDoubleClick += (_, _) => ActivateOrRestoreMainWindow(); 51 | } 52 | 53 | public static TrayIconManager GetInstance() 54 | { 55 | return _instance ??= new TrayIconManager(); 56 | } 57 | 58 | public static void Start() 59 | { 60 | _ = GetInstance(); 61 | } 62 | } 63 | 64 | internal partial class TrayIconManager : ObservableObject 65 | { 66 | [ObservableProperty] 67 | private string version = $"v{Assembly.GetExecutingAssembly().GetName().Version!.ToString(4)}"; 68 | 69 | [RelayCommand] 70 | private void ActivateOrRestoreMainWindow() 71 | { 72 | if (Application.Current.MainWindow is not null) 73 | { 74 | if (Application.Current.MainWindow.IsVisible) 75 | { 76 | Application.Current.MainWindow.Hide(); 77 | } 78 | else 79 | { 80 | Application.Current.MainWindow.Show(); 81 | Application.Current.MainWindow.Activate(); 82 | } 83 | } 84 | } 85 | 86 | [RelayCommand] 87 | private void Restart() 88 | { 89 | try 90 | { 91 | using Process process = new() 92 | { 93 | StartInfo = new ProcessStartInfo() 94 | { 95 | FileName = GetExecutablePath(), 96 | WorkingDirectory = Environment.CurrentDirectory, 97 | UseShellExecute = true, 98 | }, 99 | }; 100 | process.Start(); 101 | } 102 | catch (Win32Exception) 103 | { 104 | return; 105 | } 106 | 107 | Process.GetCurrentProcess().Kill(); 108 | 109 | static string GetExecutablePath() 110 | { 111 | string fileName = AppDomain.CurrentDomain.FriendlyName; 112 | 113 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 114 | fileName += ".exe"; 115 | 116 | return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); 117 | } 118 | } 119 | 120 | [RelayCommand] 121 | private void Exit() 122 | { 123 | Application.Current.Shutdown(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Test/Wpf.Ui.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net9.0-windows 6 | enable 7 | true 8 | latest 9 | 4.0.2.3 10 | 4.0.2.3 11 | $(VersionPrefix)4.0.2.3 12 | Resources\Images\ProfilePicture.ico 13 | embedded 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Appearance/ResourceDictionaryManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Windows; 4 | 5 | namespace Wpf.Ui.Violeta.Appearance; 6 | 7 | internal class ResourceDictionaryManager 8 | { 9 | public string SearchNamespace { get; } 10 | 11 | public ResourceDictionaryManager(string searchNamespace) 12 | { 13 | SearchNamespace = searchNamespace; 14 | } 15 | 16 | public bool HasDictionary(string resourceLookup) 17 | { 18 | return GetDictionary(resourceLookup) != null; 19 | } 20 | 21 | public ResourceDictionary? GetDictionary(string resourceLookup) 22 | { 23 | Collection applicationDictionaries = GetApplicationMergedDictionaries(); 24 | 25 | if (applicationDictionaries.Count == 0) 26 | { 27 | return null; 28 | } 29 | 30 | resourceLookup = resourceLookup.ToLower().Trim(); 31 | 32 | foreach (ResourceDictionary t in applicationDictionaries) 33 | { 34 | string resourceDictionaryUri; 35 | 36 | if (t?.Source != null) 37 | { 38 | resourceDictionaryUri = t.Source.ToString().ToLower().Trim(); 39 | 40 | if ( 41 | resourceDictionaryUri.Contains(SearchNamespace) 42 | && resourceDictionaryUri.Contains(resourceLookup) 43 | ) 44 | { 45 | return t; 46 | } 47 | } 48 | 49 | foreach (ResourceDictionary? t1 in t!.MergedDictionaries) 50 | { 51 | if (t1?.Source == null) 52 | { 53 | continue; 54 | } 55 | 56 | resourceDictionaryUri = t1.Source.ToString().ToLower().Trim(); 57 | 58 | if ( 59 | !resourceDictionaryUri.Contains(SearchNamespace) 60 | || !resourceDictionaryUri.Contains(resourceLookup) 61 | ) 62 | { 63 | continue; 64 | } 65 | 66 | return t1; 67 | } 68 | } 69 | 70 | return null; 71 | } 72 | 73 | public bool UpdateDictionary(string resourceLookup, Uri? newResourceUri) 74 | { 75 | Collection applicationDictionaries = UiApplication 76 | .Current 77 | .Resources 78 | .MergedDictionaries; 79 | 80 | if (applicationDictionaries.Count == 0 || newResourceUri is null) 81 | { 82 | return false; 83 | } 84 | 85 | resourceLookup = resourceLookup.ToLower().Trim(); 86 | 87 | for (var i = 0; i < applicationDictionaries.Count; i++) 88 | { 89 | string sourceUri; 90 | 91 | if (applicationDictionaries[i]?.Source != null) 92 | { 93 | sourceUri = applicationDictionaries[i].Source.ToString().ToLower().Trim(); 94 | 95 | if (sourceUri.Contains(SearchNamespace) && sourceUri.Contains(resourceLookup)) 96 | { 97 | applicationDictionaries[i] = new() { Source = newResourceUri }; 98 | 99 | return true; 100 | } 101 | } 102 | 103 | for (var j = 0; j < applicationDictionaries[i].MergedDictionaries.Count; j++) 104 | { 105 | if (applicationDictionaries[i].MergedDictionaries[j]?.Source == null) 106 | { 107 | continue; 108 | } 109 | 110 | sourceUri = applicationDictionaries[i] 111 | .MergedDictionaries[j] 112 | .Source.ToString() 113 | .ToLower() 114 | .Trim(); 115 | 116 | if (!sourceUri.Contains(SearchNamespace) || !sourceUri.Contains(resourceLookup)) 117 | { 118 | continue; 119 | } 120 | 121 | applicationDictionaries[i].MergedDictionaries[j] = new() { Source = newResourceUri }; 122 | 123 | return true; 124 | } 125 | } 126 | 127 | return false; 128 | } 129 | 130 | private Collection GetApplicationMergedDictionaries() 131 | { 132 | return UiApplication.Current.Resources.MergedDictionaries; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Appearance/SystemMenuThemeManager.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using Wpf.Ui.Appearance; 4 | using Wpf.Ui.Violeta.Win32; 5 | 6 | namespace Wpf.Ui.Violeta.Appearance; 7 | 8 | public static class SystemMenuThemeManager 9 | { 10 | public static void Apply(SystemMenuTheme theme = SystemMenuTheme.Auto) 11 | { 12 | // Enable dark mode for context menus if using dark theme 13 | if (Environment.OSVersion.Version.Build >= 18362) // Windows 10 1903 14 | { 15 | if (theme == SystemMenuTheme.Auto) 16 | { 17 | // UxTheme methods will apply all of menus. 18 | // However, the Windows style system prefers that 19 | // Windows System Menu is based on `Apps Theme`, 20 | // and Tray Context Menu is based on `System Theme` when using a custom theme. 21 | // But actually we can't have our cake and eat it too. 22 | // Finally, we synchronize the theme styles of tray with higher usage rates. 23 | if (ThemeManager.GetSystemTheme() == SystemTheme.Dark) 24 | { 25 | _ = UxTheme.SetPreferredAppMode(UxTheme.PreferredAppMode.ForceDark); 26 | UxTheme.FlushMenuThemes(); 27 | } 28 | 29 | // Synchronize the theme with system settings 30 | SystemEvents.UserPreferenceChanged -= OnUserPreferenceChangedEventHandler; 31 | SystemEvents.UserPreferenceChanged += OnUserPreferenceChangedEventHandler; 32 | } 33 | else if (theme == SystemMenuTheme.Dark) 34 | { 35 | SystemEvents.UserPreferenceChanged -= OnUserPreferenceChangedEventHandler; 36 | _ = UxTheme.SetPreferredAppMode(UxTheme.PreferredAppMode.ForceDark); 37 | UxTheme.FlushMenuThemes(); 38 | } 39 | else if (theme == SystemMenuTheme.Light) 40 | { 41 | SystemEvents.UserPreferenceChanged -= OnUserPreferenceChangedEventHandler; 42 | _ = UxTheme.SetPreferredAppMode(UxTheme.PreferredAppMode.ForceLight); 43 | UxTheme.FlushMenuThemes(); 44 | } 45 | } 46 | } 47 | 48 | private static void OnUserPreferenceChangedEventHandler(object sender, UserPreferenceChangedEventArgs e) 49 | { 50 | if (ThemeManager.GetSystemTheme() == SystemTheme.Dark) 51 | { 52 | _ = UxTheme.SetPreferredAppMode(UxTheme.PreferredAppMode.ForceDark); 53 | UxTheme.FlushMenuThemes(); 54 | } 55 | else 56 | { 57 | _ = UxTheme.SetPreferredAppMode(UxTheme.PreferredAppMode.ForceLight); 58 | UxTheme.FlushMenuThemes(); 59 | } 60 | } 61 | } 62 | 63 | /// 64 | /// Theme in which an system menu is displayed. 65 | /// 66 | public enum SystemMenuTheme 67 | { 68 | /// 69 | /// Auto system theme. 70 | /// 71 | Auto = ApplicationTheme.Unknown, 72 | 73 | /// 74 | /// Dark system theme. 75 | /// 76 | Dark = ApplicationTheme.Dark, 77 | 78 | /// 79 | /// Light system theme. 80 | /// 81 | Light = ApplicationTheme.Light, 82 | } 83 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Windows; 3 | using System.Windows.Markup; 4 | 5 | [assembly: ComVisible(false)] 6 | [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] 7 | [assembly: Guid("617d6360-3976-4871-85cb-36bdb4f7ab62")] 8 | [assembly: XmlnsPrefix("http://schemas.lepo.co/wpfui/2022/xaml", "ui")] 9 | [assembly: XmlnsDefinition("http://schemas.lepo.co/wpfui/2022/xaml", "Wpf.Ui.Controls")] 10 | [assembly: XmlnsDefinition("http://schemas.lepo.co/wpfui/2022/xaml", "Wpf.Ui.Controls.Primitives")] 11 | [assembly: XmlnsDefinition("http://schemas.lepo.co/wpfui/2022/xaml", "Wpf.Ui")] 12 | [assembly: XmlnsPrefix("http://schemas.lepo.co/wpfui/2022/xaml/violeta", "vio")] 13 | [assembly: XmlnsDefinition("http://schemas.lepo.co/wpfui/2022/xaml/violeta", "Wpf.Ui.Violeta.Controls")] 14 | [assembly: XmlnsDefinition("http://schemas.lepo.co/wpfui/2022/xaml/violeta", "Wpf.Ui.Violeta.Converters")] 15 | [assembly: XmlnsDefinition("http://schemas.lepo.co/wpfui/2022/xaml/violeta", "Wpf.Ui.Violeta.Controls.Primitives")] 16 | [assembly: XmlnsDefinition("http://schemas.lepo.co/wpfui/2022/xaml/violeta", "Wpf.Ui.Violeta.Markup")] 17 | [assembly: XmlnsDefinition("http://schemas.lepo.co/wpfui/2022/xaml/violeta", "Wpf.Ui.Violeta")] 18 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/BitmapIcon/BitmapIcon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Media; 4 | using System.Windows.Media.Imaging; 5 | using System.Windows.Shapes; 6 | using Wpf.Ui.Violeta.Controls.Primitives; 7 | 8 | namespace Wpf.Ui.Controls; 9 | 10 | /// 11 | /// Represents an icon that uses a bitmap as its content. 12 | /// 13 | public class BitmapIcon : IconElementEx 14 | { 15 | static BitmapIcon() 16 | { 17 | ForegroundProperty.OverrideMetadata(typeof(BitmapIcon), new FrameworkPropertyMetadata(OnForegroundChanged)); 18 | } 19 | 20 | /// 21 | /// Initializes a new instance of the BitmapIcon class. 22 | /// 23 | public BitmapIcon() 24 | { 25 | } 26 | 27 | /// 28 | /// Identifies the UriSource dependency property. 29 | /// 30 | public static readonly DependencyProperty UriSourceProperty = 31 | BitmapImage.UriSourceProperty.AddOwner( 32 | typeof(BitmapIcon), 33 | new FrameworkPropertyMetadata(OnUriSourceChanged)); 34 | 35 | /// 36 | /// Gets or sets the Uniform Resource Identifier (URI) of the bitmap to use as the 37 | /// icon content. 38 | /// 39 | /// The Uri of the bitmap to use as the icon content. The default is **null**. 40 | public Uri UriSource 41 | { 42 | get => (Uri)GetValue(UriSourceProperty); 43 | set => SetValue(UriSourceProperty, value); 44 | } 45 | 46 | private static void OnUriSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 47 | { 48 | ((BitmapIcon)d).ApplyUriSource(); 49 | } 50 | 51 | /// 52 | /// Identifies the ShowAsMonochrome dependency property. 53 | /// 54 | public static readonly DependencyProperty ShowAsMonochromeProperty = 55 | DependencyProperty.Register( 56 | nameof(ShowAsMonochrome), 57 | typeof(bool), 58 | typeof(BitmapIcon), 59 | new PropertyMetadata(true, OnShowAsMonochromeChanged)); 60 | 61 | /// 62 | /// Gets or sets a value that indicates whether the bitmap is shown in a single color. 63 | /// 64 | /// 65 | /// **true** to show the bitmap in a single color; **false** to show the bitmap in 66 | /// full color. The default is **true.** 67 | /// 68 | public bool ShowAsMonochrome 69 | { 70 | get => (bool)GetValue(ShowAsMonochromeProperty); 71 | set => SetValue(ShowAsMonochromeProperty, value); 72 | } 73 | 74 | private static void OnShowAsMonochromeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 75 | { 76 | ((BitmapIcon)d).ApplyShowAsMonochrome(); 77 | } 78 | 79 | protected override UIElement InitializeChildren() 80 | { 81 | _image = new Image 82 | { 83 | Visibility = Visibility.Hidden 84 | }; 85 | 86 | _opacityMask = new ImageBrush(); 87 | _foreground = new Rectangle 88 | { 89 | OpacityMask = _opacityMask 90 | }; 91 | 92 | ApplyForeground(); 93 | ApplyUriSource(); 94 | 95 | Children.Add(_image); 96 | 97 | ApplyShowAsMonochrome(); 98 | 99 | return _image; 100 | } 101 | 102 | private protected override void OnShouldInheritForegroundFromVisualParentChanged() 103 | { 104 | ApplyForeground(); 105 | } 106 | 107 | private protected override void OnVisualParentForegroundPropertyChanged(DependencyPropertyChangedEventArgs args) 108 | { 109 | if (ShouldInheritForegroundFromVisualParent) 110 | { 111 | ApplyForeground(); 112 | } 113 | } 114 | 115 | private static void OnForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 116 | { 117 | ((BitmapIcon)d).ApplyForeground(); 118 | } 119 | 120 | private void ApplyForeground() 121 | { 122 | if (_foreground != null) 123 | { 124 | _foreground.Fill = ShouldInheritForegroundFromVisualParent ? VisualParentForeground : Foreground; 125 | } 126 | } 127 | 128 | private void ApplyUriSource() 129 | { 130 | if (_image != null && _opacityMask != null) 131 | { 132 | var uriSource = UriSource; 133 | if (uriSource != null) 134 | { 135 | var imageSource = new BitmapImage(uriSource); 136 | _image.Source = imageSource; 137 | _opacityMask.ImageSource = imageSource; 138 | } 139 | else 140 | { 141 | _image.ClearValue(Image.SourceProperty); 142 | _opacityMask.ClearValue(ImageBrush.ImageSourceProperty); 143 | } 144 | } 145 | } 146 | 147 | private void ApplyShowAsMonochrome() 148 | { 149 | bool showAsMonochrome = ShowAsMonochrome; 150 | 151 | if (_image != null) 152 | { 153 | _image.Visibility = showAsMonochrome ? Visibility.Hidden : Visibility.Visible; 154 | } 155 | 156 | if (_foreground != null) 157 | { 158 | if (showAsMonochrome) 159 | { 160 | if (!Children.Contains(_foreground)) 161 | { 162 | Children.Add(_foreground); 163 | } 164 | } 165 | else 166 | { 167 | Children.Remove(_foreground); 168 | } 169 | } 170 | } 171 | 172 | private Image _image = null!; 173 | private Rectangle _foreground = null!; 174 | private ImageBrush _opacityMask = null!; 175 | } 176 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Button/Button.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 48 | 49 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/CachedImage/CachedImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Cache; 3 | using System.Windows; 4 | using System.Windows.Media.Imaging; 5 | 6 | namespace Wpf.Ui.Violeta.Controls; 7 | 8 | /// 9 | /// Represents a control that is a wrapper on System.Windows.Controls.Image for enabling filesystem-based caching 10 | /// 11 | public class CachedImage : System.Windows.Controls.Image 12 | { 13 | static CachedImage() 14 | { 15 | DefaultStyleKeyProperty.OverrideMetadata(typeof(CachedImage), new FrameworkPropertyMetadata(typeof(CachedImage))); 16 | } 17 | 18 | public string ImageUrl 19 | { 20 | get => (string)GetValue(ImageUrlProperty); 21 | set => SetValue(ImageUrlProperty, value); 22 | } 23 | 24 | public static readonly DependencyProperty ImageUrlProperty = 25 | DependencyProperty.Register(nameof(ImageUrl), typeof(string), typeof(CachedImage), new(string.Empty, ImageUrlPropertyChanged)); 26 | 27 | public BitmapCreateOptions CreateOptions 28 | { 29 | get => (BitmapCreateOptions)GetValue(CreateOptionsProperty); 30 | set => SetValue(CreateOptionsProperty, value); 31 | } 32 | 33 | public static readonly DependencyProperty CreateOptionsProperty = 34 | DependencyProperty.Register(nameof(CreateOptions), typeof(BitmapCreateOptions), typeof(CachedImage)); 35 | 36 | private static async void ImageUrlPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 37 | { 38 | var url = e.NewValue as string; 39 | 40 | if (string.IsNullOrEmpty(url)) 41 | return; 42 | 43 | var cachedImage = (CachedImage)obj; 44 | var bitmapImage = new BitmapImage(); 45 | 46 | switch (FileCacheForImage.AppCacheMode) 47 | { 48 | case FileCacheForImage.CacheMode.WinINet: 49 | bitmapImage.BeginInit(); 50 | bitmapImage.CreateOptions = cachedImage.CreateOptions; 51 | bitmapImage.UriSource = new Uri(url); 52 | // Enable IE-like cache policy. 53 | bitmapImage.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.Default); 54 | bitmapImage.EndInit(); 55 | cachedImage.Source = bitmapImage; 56 | break; 57 | 58 | case FileCacheForImage.CacheMode.Dedicated: 59 | try 60 | { 61 | var memoryStream = await FileCacheForImage.HitAsync(url!); 62 | if (memoryStream == null) 63 | return; 64 | 65 | bitmapImage.BeginInit(); 66 | bitmapImage.CreateOptions = cachedImage.CreateOptions; 67 | bitmapImage.StreamSource = memoryStream; 68 | bitmapImage.EndInit(); 69 | cachedImage.Source = bitmapImage; 70 | } 71 | catch (Exception) 72 | { 73 | // ignored, in case the downloaded file is a broken or not an image. 74 | } 75 | break; 76 | 77 | default: 78 | throw new ArgumentOutOfRangeException(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/CachedImage/FileCacheForImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Security.Cryptography; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Wpf.Ui.Violeta.Controls; 12 | 13 | public static class FileCacheForImage 14 | { 15 | public enum CacheMode 16 | { 17 | WinINet, 18 | Dedicated 19 | } 20 | 21 | // Record whether a file is being written. 22 | private static readonly Dictionary IsWritingFile = []; 23 | 24 | // Timeout for performing the file download request. 25 | private static readonly TimeSpan RequestTimeout = TimeSpan.FromSeconds(5); 26 | 27 | // HttpClient is intended to be instantiated once per application, rather than per-use. 28 | private static readonly Lazy LazyHttpClient = new(() => new HttpClient()); 29 | 30 | static FileCacheForImage() 31 | { 32 | // default cache directory - can be changed if needed from App.xaml 33 | AppCacheDirectory = string.Format("{0}\\{1}\\Cache\\", 34 | Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), 35 | Process.GetCurrentProcess().ProcessName); 36 | AppCacheMode = CacheMode.WinINet; 37 | } 38 | 39 | /// 40 | /// Gets or sets the path to the folder that stores the cache file. Only works when AppCacheMode is 41 | /// CacheMode.Dedicated. 42 | /// 43 | public static string AppCacheDirectory { get; set; } 44 | 45 | /// 46 | /// Gets or sets the cache mode. WinINet is recommended, it's provided by .Net Framework and uses the Temporary Files 47 | /// of IE and the same cache policy of IE. 48 | /// 49 | public static CacheMode AppCacheMode { get; set; } 50 | 51 | public static async Task HitAsync(string url) 52 | { 53 | if (!Directory.Exists(AppCacheDirectory)) 54 | { 55 | Directory.CreateDirectory(AppCacheDirectory); 56 | } 57 | var uri = new Uri(url); 58 | var fileNameBuilder = new StringBuilder(); 59 | using (SHA1 sha1 = SHA1.Create()) 60 | { 61 | var canonicalUrl = uri.ToString(); 62 | var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(canonicalUrl)); 63 | fileNameBuilder.Append(BitConverter.ToString(hash).Replace("-", "").ToLower()); 64 | if (Path.HasExtension(canonicalUrl)) 65 | { 66 | fileNameBuilder.Append(Path.GetExtension(canonicalUrl).Split('?')[0]); 67 | } 68 | } 69 | 70 | var fileName = fileNameBuilder.ToString(); 71 | var localFile = string.Format("{0}\\{1}", AppCacheDirectory, fileName); 72 | var memoryStream = new MemoryStream(); 73 | 74 | FileStream fileStream = null!; 75 | if (!IsWritingFile.ContainsKey(fileName) && File.Exists(localFile)) 76 | { 77 | using (fileStream = new FileStream(localFile, FileMode.Open, FileAccess.Read)) 78 | { 79 | await fileStream.CopyToAsync(memoryStream); 80 | } 81 | memoryStream.Seek(0, SeekOrigin.Begin); 82 | return memoryStream; 83 | } 84 | 85 | var client = LazyHttpClient.Value; 86 | client.Timeout = RequestTimeout; 87 | try 88 | { 89 | var response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); 90 | 91 | if (response.IsSuccessStatusCode is false) 92 | { 93 | return null!; 94 | } 95 | 96 | var responseStream = await response.Content.ReadAsStreamAsync(); 97 | 98 | if (!IsWritingFile.ContainsKey(fileName)) 99 | { 100 | IsWritingFile[fileName] = true; 101 | fileStream = new FileStream(localFile, FileMode.Create, FileAccess.Write); 102 | } 103 | 104 | using (responseStream) 105 | { 106 | var bytebuffer = new byte[100]; 107 | int bytesRead; 108 | do 109 | { 110 | bytesRead = await responseStream.ReadAsync(bytebuffer, 0, 100); 111 | if (fileStream != null) 112 | { 113 | await fileStream.WriteAsync(bytebuffer, 0, bytesRead); 114 | } 115 | 116 | await memoryStream.WriteAsync(bytebuffer, 0, bytesRead); 117 | } while (bytesRead > 0); 118 | if (fileStream != null) 119 | { 120 | await fileStream.FlushAsync(); 121 | fileStream.Dispose(); 122 | IsWritingFile.Remove(fileName); 123 | } 124 | } 125 | memoryStream.Seek(0, SeekOrigin.Begin); 126 | return memoryStream; 127 | } 128 | catch (WebException) 129 | { 130 | return null!; 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogButton.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Controls; 2 | 3 | public enum ContentDialogButton 4 | { 5 | None = 0, 6 | Primary = 1, 7 | Secondary = 2, 8 | Close = 3, 9 | } 10 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogButtonClickDeferral.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public sealed class ContentDialogButtonClickDeferral 6 | { 7 | private readonly Action _handler; 8 | 9 | internal ContentDialogButtonClickDeferral(Action handler) 10 | { 11 | _handler = handler ?? throw new ArgumentNullException(nameof(handler)); 12 | } 13 | 14 | public void Complete() 15 | { 16 | _handler(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogButtonClickEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public class ContentDialogButtonClickEventArgs : RoutedEventArgs 7 | { 8 | private ContentDialogButtonClickDeferral _deferral = null!; 9 | private int _deferralCount; 10 | 11 | internal ContentDialogButtonClickEventArgs() 12 | { 13 | } 14 | 15 | public bool Cancel { get; set; } 16 | 17 | public ContentDialogButtonClickDeferral GetDeferral() 18 | { 19 | _deferralCount++; 20 | 21 | return new ContentDialogButtonClickDeferral(() => 22 | { 23 | DecrementDeferralCount(); 24 | }); 25 | } 26 | 27 | internal void SetDeferral(ContentDialogButtonClickDeferral deferral) 28 | { 29 | _deferral = deferral; 30 | } 31 | 32 | internal void DecrementDeferralCount() 33 | { 34 | Debug.Assert(_deferralCount > 0); 35 | _deferralCount--; 36 | if (_deferralCount == 0) 37 | { 38 | _deferral.Complete(); 39 | } 40 | } 41 | 42 | internal void IncrementDeferralCount() 43 | { 44 | _deferralCount++; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogClosedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public class ContentDialogClosedEventArgs : RoutedEventArgs 6 | { 7 | internal ContentDialogClosedEventArgs(ContentDialogResult result) 8 | { 9 | Result = result; 10 | } 11 | 12 | public ContentDialogResult Result { get; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogClosingDeferral.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public sealed class ContentDialogClosingDeferral 6 | { 7 | private readonly Action _handler; 8 | 9 | internal ContentDialogClosingDeferral(Action handler) 10 | { 11 | _handler = handler ?? throw new ArgumentNullException(nameof(handler)); 12 | } 13 | 14 | public void Complete() 15 | { 16 | _handler(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogClosingEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public sealed class ContentDialogClosingEventArgs : RoutedEventArgs 7 | { 8 | private ContentDialogClosingDeferral _deferral = null!; 9 | private int _deferralCount; 10 | 11 | internal ContentDialogClosingEventArgs(ContentDialogResult result) 12 | { 13 | Result = result; 14 | } 15 | 16 | public bool Cancel { get; set; } 17 | 18 | public ContentDialogResult Result { get; } 19 | 20 | public ContentDialogClosingDeferral GetDeferral() 21 | { 22 | _deferralCount++; 23 | 24 | return new ContentDialogClosingDeferral(() => 25 | { 26 | DecrementDeferralCount(); 27 | }); 28 | } 29 | 30 | internal void SetDeferral(ContentDialogClosingDeferral deferral) 31 | { 32 | _deferral = deferral; 33 | } 34 | 35 | internal void DecrementDeferralCount() 36 | { 37 | Debug.Assert(_deferralCount > 0); 38 | _deferralCount--; 39 | if (_deferralCount == 0) 40 | { 41 | _deferral.Complete(); 42 | } 43 | } 44 | 45 | internal void IncrementDeferralCount() 46 | { 47 | _deferralCount++; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogHostService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | 6 | namespace Wpf.Ui.Controls; 7 | 8 | public static class ContentDialogHostService 9 | { 10 | public const string HostName = "PART_ContentPresenterForDialogs"; 11 | 12 | public static bool IsPreferMainWindow { get; set; } = true; 13 | 14 | public static ContentPresenter? ContentPresenterForDialogs 15 | { 16 | get 17 | { 18 | if (IsPreferMainWindow && Application.Current.MainWindow != null) 19 | { 20 | return Application.Current.MainWindow.Dispatcher.Invoke(() => AttachContentDialogHost(Application.Current.MainWindow)); 21 | } 22 | else 23 | { 24 | Window? window = Application.Current.Windows.OfType() 25 | .Where(win => win.IsActive) 26 | .FirstOrDefault(); 27 | 28 | if (window != null) 29 | { 30 | return window.Dispatcher.Invoke(() => AttachContentDialogHost(window)); 31 | } 32 | } 33 | 34 | // Throw InvalidOperationException if ContentDialog::ShowAsync is called with a null DialogHost. 35 | return null; 36 | 37 | static ContentPresenter? AttachContentDialogHost(Window window) 38 | { 39 | if (window.Content is System.Windows.Controls.Grid rootGrid) 40 | { 41 | if (rootGrid.ColumnDefinitions.Count != 0) 42 | { 43 | throw new InvalidOperationException("The root Grid's ColumnDefinitions in the Window should be empty."); 44 | } 45 | if (rootGrid.RowDefinitions.Count != 0) 46 | { 47 | throw new InvalidOperationException("The root Grid's RowDefinitions in the Window should be empty."); 48 | } 49 | 50 | if (window.FindName(HostName) is ContentPresenter _host) 51 | { 52 | return _host; 53 | } 54 | else 55 | { 56 | ContentPresenter host = new() { Name = HostName }; 57 | rootGrid.Children.Add(host); 58 | window.RegisterName(HostName, host); 59 | return host; 60 | } 61 | } 62 | else 63 | { 64 | throw new InvalidOperationException("The root element of the Window must be of type Grid."); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogOpenedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public class ContentDialogOpenedEventArgs : RoutedEventArgs; 6 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogPlacement.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Controls; 2 | 3 | public enum ContentDialogPlacement 4 | { 5 | Popup = 0, 6 | InPlace = 1, 7 | } 8 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/ContentDialogResult.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Controls; 2 | 3 | public enum ContentDialogResult 4 | { 5 | None = 0, 6 | Primary = 1, 7 | Secondary = 2, 8 | } 9 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ContentDialog/CornerRadiusFilterConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace Wpf.Ui.Violeta.Controls; 7 | 8 | public sealed class CornerRadiusFilterConverter : DependencyObject, IValueConverter 9 | { 10 | public CornerRadiusFilterKind Filter { get; set; } 11 | 12 | public double Scale { get; set; } = 1d; 13 | 14 | public static CornerRadius Convert(CornerRadius radius, CornerRadiusFilterKind filterKind) 15 | { 16 | CornerRadius result = radius; 17 | switch (filterKind) 18 | { 19 | case CornerRadiusFilterKind.Top: 20 | result.BottomLeft = 0.0; 21 | result.BottomRight = 0.0; 22 | break; 23 | 24 | case CornerRadiusFilterKind.Right: 25 | result.TopLeft = 0.0; 26 | result.BottomLeft = 0.0; 27 | break; 28 | 29 | case CornerRadiusFilterKind.Bottom: 30 | result.TopLeft = 0.0; 31 | result.TopRight = 0.0; 32 | break; 33 | 34 | case CornerRadiusFilterKind.Left: 35 | result.TopRight = 0.0; 36 | result.BottomRight = 0.0; 37 | break; 38 | } 39 | 40 | return result; 41 | } 42 | 43 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 44 | { 45 | if (value is CornerRadius radius) 46 | { 47 | double scale = Scale; 48 | if (!double.IsNaN(scale)) 49 | { 50 | radius.TopLeft *= scale; 51 | radius.TopRight *= scale; 52 | radius.BottomRight *= scale; 53 | radius.BottomLeft *= scale; 54 | } 55 | 56 | CornerRadiusFilterKind filter = Filter; 57 | if (filter == CornerRadiusFilterKind.TopLeftValue || filter == CornerRadiusFilterKind.BottomRightValue) 58 | { 59 | return GetDoubleValue(radius, filter); 60 | } 61 | 62 | return Convert(radius, filter); 63 | } 64 | 65 | return new CornerRadius(0.0); 66 | } 67 | 68 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 69 | { 70 | return new CornerRadius(0.0); 71 | } 72 | 73 | private double GetDoubleValue(CornerRadius radius, CornerRadiusFilterKind filterKind) 74 | { 75 | return filterKind switch 76 | { 77 | CornerRadiusFilterKind.TopLeftValue => radius.TopLeft, 78 | CornerRadiusFilterKind.BottomRightValue => radius.BottomRight, 79 | _ => 0.0, 80 | }; 81 | } 82 | } 83 | 84 | public enum CornerRadiusFilterKind 85 | { 86 | None, 87 | Top, 88 | Right, 89 | Bottom, 90 | Left, 91 | TopLeftValue, 92 | BottomRightValue 93 | } 94 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/DataGrid/DataGrid.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 29 | 30 | 31 | 46 | 47 | 48 | 54 | 55 | 56 | 64 | 65 | 66 | 90 | 91 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ExceptionReport/ExceptionReport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows; 6 | using System.Windows.Threading; 7 | 8 | namespace Wpf.Ui.Violeta.Controls; 9 | 10 | public static class ExceptionReport 11 | { 12 | /// 13 | /// For safety only use `Application.Current.Dispatcher` for show dialog. 14 | /// It means the temporary STA thread will be skipped. 15 | /// 16 | public static bool IsOnlyApplicationDispatcher { get; set; } = true; 17 | 18 | public static void HandleOnUnhandledException(this Application app) 19 | { 20 | app.DispatcherUnhandledException -= OnApplicationUnhandledException; 21 | app.DispatcherUnhandledException += OnApplicationUnhandledException; 22 | } 23 | 24 | private static void OnApplicationUnhandledException(object? sender, DispatcherUnhandledExceptionEventArgs e) 25 | { 26 | e.Handled = true; 27 | 28 | Debug.WriteLine("[ExceptionReport] Application.DispatcherUnhandledException " + e.Exception.ToString()); 29 | Show(e.Exception); 30 | } 31 | 32 | public static void Show(Exception e, string? appName = null, string? appVersion = null) 33 | { 34 | Window? owner = Application.Current.Windows.OfType().FirstOrDefault(window => window.IsActive) 35 | ?? Application.Current.MainWindow; 36 | 37 | Dispatcher dispatcher = Application.Current.Dispatcher; 38 | 39 | if (!IsOnlyApplicationDispatcher) 40 | { 41 | dispatcher = owner?.Dispatcher ?? Application.Current.Dispatcher; 42 | } 43 | 44 | _ = dispatcher.Invoke(() => _ = new ExceptionWindow(e, appName, appVersion) { Owner = owner }.ShowDialog()); 45 | } 46 | 47 | public static async Task ShowAsync(Exception e, string? appName = null, string? appVersion = null) 48 | { 49 | Window? owner = Application.Current.Windows.OfType().FirstOrDefault(window => window.IsActive) 50 | ?? Application.Current.MainWindow; 51 | 52 | Dispatcher dispatcher = Application.Current.Dispatcher; 53 | 54 | if (!IsOnlyApplicationDispatcher) 55 | { 56 | dispatcher = owner?.Dispatcher ?? Application.Current.Dispatcher; 57 | } 58 | 59 | await dispatcher.BeginInvoke(() => _ = new ExceptionWindow(e, appName, appVersion) { Owner = owner }.ShowDialog()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ExceptionReport/ExceptionWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Windows; 4 | using System.Windows.Input; 5 | using System.Windows.Media; 6 | using Wpf.Ui.Controls; 7 | using Wpf.Ui.Violeta.Resources.Localization; 8 | using Wpf.Ui.Violeta.Win32; 9 | 10 | namespace Wpf.Ui.Violeta.Controls; 11 | 12 | public partial class ExceptionWindow : FluentWindow 13 | { 14 | public Window? owner = null; 15 | 16 | public new Window? Owner 17 | { 18 | get => owner; 19 | set 20 | { 21 | owner = value; 22 | 23 | if (owner != null) 24 | { 25 | try 26 | { 27 | // Try get title but be care the owner had been disposed. 28 | AppName ??= owner.Dispatcher.Invoke(() => owner.Title); 29 | } 30 | catch 31 | { 32 | /// 33 | } 34 | 35 | try 36 | { 37 | // Try get assembly version but be care the owner class had been unmounted. 38 | AppVersion ??= "v" + owner.GetType().Assembly.GetName().Version?.ToString(); 39 | } 40 | catch 41 | { 42 | /// 43 | } 44 | } 45 | } 46 | } 47 | 48 | public Exception? ExceptionObject { get; set; } 49 | public string? ExceptionType => ExceptionObject?.GetType().ToString(); 50 | 51 | public string? AppName 52 | { 53 | get => (string)GetValue(AppNameProperty); 54 | set => SetValue(AppNameProperty, value); 55 | } 56 | 57 | public static readonly DependencyProperty AppNameProperty = 58 | DependencyProperty.Register(nameof(AppName), typeof(string), typeof(ExceptionWindow), new PropertyMetadata(null)); 59 | 60 | public string? AppVersion 61 | { 62 | get => (string)GetValue(AppVersionProperty); 63 | set => SetValue(AppVersionProperty, value); 64 | } 65 | 66 | public static readonly DependencyProperty AppVersionProperty = 67 | DependencyProperty.Register(nameof(AppVersion), typeof(string), typeof(ExceptionWindow), new PropertyMetadata(null)); 68 | 69 | public string ErrorTime { get; } = DateTime.Now.ToString(); 70 | 71 | public string OSVersion { get; } = "Windows " + NTdll.GetOSVersion().ToString(); 72 | 73 | public ExceptionWindow(Exception e, string? appName = null, string? appVersion = null) 74 | { 75 | ExceptionObject = e ?? new Exception(""); 76 | AppName = appName; 77 | AppVersion = appVersion; 78 | 79 | DataContext = this; 80 | InitializeComponent(); 81 | 82 | Title = SH.ExceptionWindowTitle; 83 | Hint1TextBlock.Text = SH.ExceptionWindowHint1; 84 | Hint2TextBlock.Text = SH.ExceptionWindowHint2; 85 | CopyTextBlock.Text = SH.ButtonCopy; 86 | IgnoreTextBlock.Text = SH.ButtonIgnore; 87 | ExitTextBlock.Text = SH.ButtonExit; 88 | 89 | CopyButton.Click += (_, _) => CopyInfo(); 90 | TryIgnoreButton.Click += (_, _) => IgnoreAndTry(); 91 | ExitButton.Click += (_, _) => ExitApp(); 92 | 93 | KeyDown += (_, e) => 94 | { 95 | if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control && e.Key == Key.C) 96 | { 97 | CopyInfo(); 98 | } 99 | }; 100 | } 101 | 102 | protected override void OnSourceInitialized(EventArgs e) 103 | { 104 | base.OnSourceInitialized(e); 105 | 106 | if (WindowBackdrop.IsSupported(WindowBackdropType.Mica)) 107 | { 108 | Background = new SolidColorBrush(Colors.Transparent); 109 | WindowBackdrop.ApplyBackdrop(this, WindowBackdropType.Mica); 110 | } 111 | } 112 | 113 | public string GetInfo() 114 | { 115 | StringBuilder sb = new(); 116 | 117 | sb.AppendLine($"{AppName} {AppVersion}") 118 | .AppendLine($"{ErrorTime} {OSVersion}") 119 | .AppendLine("```") 120 | .AppendLine(ExceptionObject?.ToString()) 121 | .AppendLine("```"); 122 | 123 | return sb.ToString(); 124 | } 125 | 126 | public void CopyInfo() 127 | { 128 | try 129 | { 130 | Clipboard.SetText(GetInfo()); 131 | } 132 | catch 133 | { 134 | /// 135 | } 136 | } 137 | 138 | public void IgnoreAndTry() 139 | { 140 | Close(); 141 | } 142 | 143 | public void ExitApp() 144 | { 145 | IgnoreAndTry(); 146 | Environment.Exit('e' + 'x' + 'c' + 'e' + 'p' + 't' + 'i' + 'o' + 'n'); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/FontIcon/CustomFontSymbols.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Controls; 2 | 3 | /// 4 | /// Violeta Fluent Icons 5 | /// https://github.com/emako/fluentui-violeta-icons 6 | /// 7 | public sealed class CustomFontSymbols 8 | { 9 | public const string Edit = "\xe900"; 10 | public const string Empty = "\xe901"; 11 | public const string Extension = "\xe902"; 12 | public const string File = "\xe903"; 13 | } 14 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/FontIcon/FontIconFallback.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Media; 5 | 6 | namespace Wpf.Ui.Violeta.Controls; 7 | 8 | // TODO: Use font icon if available 9 | [EditorBrowsable(EditorBrowsableState.Never)] 10 | public sealed class FontIconFallback : Control 11 | { 12 | static FontIconFallback() 13 | { 14 | DefaultStyleKeyProperty.OverrideMetadata(typeof(FontIconFallback), new FrameworkPropertyMetadata(typeof(FontIconFallback))); 15 | FocusableProperty.OverrideMetadata(typeof(FontIconFallback), new FrameworkPropertyMetadata(false)); 16 | } 17 | 18 | public static readonly DependencyProperty DataProperty = 19 | DependencyProperty.Register(nameof(Data), typeof(Geometry), typeof(FontIconFallback), null); 20 | 21 | public Geometry Data 22 | { 23 | get => (Geometry)GetValue(DataProperty); 24 | set => SetValue(DataProperty, value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/FontIcon/FontIconFallback.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 28 | 29 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ImageView/AnimatedImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | 8 | namespace Wpf.Ui.Violeta.Controls; 9 | 10 | public class AnimatedImage : Image, IDisposable 11 | { 12 | public static List> Providers { get; } = []; 13 | 14 | private AnimationProvider _animation = null!; 15 | private bool _disposing; 16 | 17 | static AnimatedImage() 18 | { 19 | Providers.Add( 20 | new KeyValuePair( 21 | [".apng", ".png", ".jpg", ".jpeg", ".bmp"], 22 | typeof(BitmapImageProvider))); 23 | } 24 | 25 | public void Dispose() 26 | { 27 | _disposing = true; 28 | 29 | BeginAnimation(AnimationFrameIndexProperty, null); 30 | Source = null; 31 | 32 | _animation?.Dispose(); 33 | _animation = null!; 34 | } 35 | 36 | public event EventHandler? ImageLoaded; 37 | 38 | public event EventHandler? DoZoomToFit; 39 | 40 | private static AnimationProvider InitAnimationProvider(Uri path) 41 | { 42 | var ext = Path.GetExtension(path.LocalPath).ToLower(); 43 | var type = Providers.First(p => p.Key.Contains(ext) || p.Key.Contains("*")).Value; 44 | 45 | var provider = type.CreateInstance(path); 46 | 47 | return provider; 48 | } 49 | 50 | public static readonly DependencyProperty AnimationFrameIndexProperty = 51 | DependencyProperty.Register(nameof(AnimationFrameIndex), typeof(int), typeof(AnimatedImage), new UIPropertyMetadata(-1, AnimationFrameIndexChanged)); 52 | 53 | public static readonly DependencyProperty AnimationUriProperty = 54 | DependencyProperty.Register(nameof(AnimationUri), typeof(Uri), typeof(AnimatedImage), new UIPropertyMetadata(null, AnimationUriChanged)); 55 | 56 | public int AnimationFrameIndex 57 | { 58 | get => (int)GetValue(AnimationFrameIndexProperty); 59 | set => SetValue(AnimationFrameIndexProperty, value); 60 | } 61 | 62 | public Uri AnimationUri 63 | { 64 | get => (Uri)GetValue(AnimationUriProperty); 65 | set => SetValue(AnimationUriProperty, value); 66 | } 67 | 68 | private static void AnimationUriChanged(DependencyObject obj, DependencyPropertyChangedEventArgs ev) 69 | { 70 | if (obj is not AnimatedImage instance) 71 | { 72 | return; 73 | } 74 | 75 | instance._animation = InitAnimationProvider((Uri)ev.NewValue); 76 | ShowThumbnailAndStartAnimation(instance); 77 | } 78 | 79 | private static void ShowThumbnailAndStartAnimation(AnimatedImage instance) 80 | { 81 | instance.Dispatcher.Invoke(() => 82 | { 83 | if (instance._disposing) 84 | { 85 | return; 86 | } 87 | 88 | instance.DoZoomToFit?.Invoke(instance, new EventArgs()); 89 | instance.ImageLoaded?.Invoke(instance, new EventArgs()); 90 | 91 | instance.BeginAnimation(AnimationFrameIndexProperty, instance._animation?.Animator); 92 | }); 93 | } 94 | 95 | private static void AnimationFrameIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs ev) 96 | { 97 | if (obj is not AnimatedImage instance) 98 | { 99 | return; 100 | } 101 | 102 | if (instance._disposing) 103 | { 104 | return; 105 | } 106 | 107 | var task = instance._animation.GetRenderedFrame((int)ev.NewValue); 108 | 109 | task.ContinueWith(_ => instance.Dispatcher.Invoke(() => 110 | { 111 | if (instance._disposing) 112 | return; 113 | 114 | var firstLoad = instance.Source == null; 115 | 116 | instance.Source = _.Result; 117 | 118 | if (firstLoad) 119 | { 120 | instance.DoZoomToFit?.Invoke(instance, new EventArgs()); 121 | instance.ImageLoaded?.Invoke(instance, new EventArgs()); 122 | } 123 | })); 124 | } 125 | } 126 | 127 | file static class TypeExtensions 128 | { 129 | public static T CreateInstance(this Type t, params object[] paramArray) 130 | { 131 | return (T)Activator.CreateInstance(t, paramArray)!; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ImageView/AnimationProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Windows.Media.Animation; 4 | using System.Windows.Media.Imaging; 5 | 6 | namespace Wpf.Ui.Violeta.Controls; 7 | 8 | internal abstract class AnimationProvider : IDisposable 9 | { 10 | protected AnimationProvider(Uri path) 11 | { 12 | Path = path; 13 | Animator = new(); 14 | Animator.KeyFrames.Add(new DiscreteInt32KeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.Zero))); 15 | } 16 | 17 | public Uri Path { get; } 18 | 19 | public Int32AnimationUsingKeyFrames Animator { get; protected set; } 20 | 21 | public abstract void Dispose(); 22 | 23 | public abstract Task GetRenderedFrame(int index); 24 | } 25 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ImageView/BitmapImageProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Windows.Media.Imaging; 4 | 5 | namespace Wpf.Ui.Violeta.Controls; 6 | 7 | internal class BitmapImageProvider(Uri path) : AnimationProvider(path) 8 | { 9 | public override void Dispose() 10 | { 11 | /// 12 | } 13 | 14 | public override Task GetRenderedFrame(int index) 15 | { 16 | return Task.FromResult(new BitmapImage(Path)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/ImageView/ImageView.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 23 | 24 | 25 | 32 | 37 | 38 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Layout/StackPanel.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Markup; 4 | 5 | namespace Wpf.Ui.Controls; 6 | 7 | [ContentProperty(nameof(Children))] 8 | public class StackPanel : Panel 9 | { 10 | public static readonly DependencyProperty OrientationProperty = 11 | DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(StackPanel), new FrameworkPropertyMetadata(Orientation.Vertical, FrameworkPropertyMetadataOptions.AffectsMeasure)); 12 | 13 | public Orientation Orientation 14 | { 15 | get => (Orientation)GetValue(OrientationProperty); 16 | set => SetValue(OrientationProperty, value); 17 | } 18 | 19 | public static readonly DependencyProperty SpacingProperty = 20 | DependencyProperty.Register(nameof(Spacing), typeof(double), typeof(StackPanel), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure)); 21 | 22 | public double Spacing 23 | { 24 | get => (double)GetValue(SpacingProperty); 25 | set => SetValue(SpacingProperty, value); 26 | } 27 | 28 | protected override Size MeasureOverride(Size availableSize) 29 | { 30 | var spacing = Spacing; 31 | var panelDesiredSize = new Size(); 32 | 33 | if (Orientation == Orientation.Vertical) 34 | { 35 | availableSize.Height = double.PositiveInfinity; 36 | foreach (UIElement child in InternalChildren) 37 | { 38 | child.Measure(availableSize); 39 | var childDesiredSize = child.DesiredSize; 40 | 41 | panelDesiredSize.Height += childDesiredSize.Height; 42 | 43 | if (child.Visibility != Visibility.Collapsed) 44 | { 45 | panelDesiredSize.Height += spacing; 46 | } 47 | 48 | if (childDesiredSize.Width > panelDesiredSize.Width) 49 | { 50 | panelDesiredSize.Width = childDesiredSize.Width; 51 | } 52 | } 53 | 54 | if (InternalChildren.Count != 0) 55 | { 56 | panelDesiredSize.Height -= spacing; 57 | } 58 | } 59 | else 60 | { 61 | availableSize.Width = double.PositiveInfinity; 62 | foreach (UIElement child in InternalChildren) 63 | { 64 | child.Measure(availableSize); 65 | var childDesiredSize = child.DesiredSize; 66 | 67 | panelDesiredSize.Width += childDesiredSize.Width; 68 | 69 | if (child.Visibility != Visibility.Collapsed) 70 | { 71 | panelDesiredSize.Width += spacing; 72 | } 73 | 74 | if (childDesiredSize.Height > panelDesiredSize.Height) 75 | { 76 | panelDesiredSize.Height = childDesiredSize.Height; 77 | } 78 | } 79 | 80 | if (InternalChildren.Count != 0) 81 | { 82 | panelDesiredSize.Width -= spacing; 83 | } 84 | } 85 | 86 | return panelDesiredSize; 87 | } 88 | 89 | protected override Size ArrangeOverride(Size finalSize) 90 | { 91 | var offset = 0d; 92 | var spacing = Spacing; 93 | 94 | if (Orientation == Orientation.Vertical) 95 | { 96 | foreach (UIElement child in InternalChildren) 97 | { 98 | var childDesiredSize = child.DesiredSize; 99 | 100 | child.Arrange(new Rect(0, offset, finalSize.Width, childDesiredSize.Height)); 101 | 102 | offset += childDesiredSize.Height; 103 | 104 | if (child.Visibility != Visibility.Collapsed) 105 | { 106 | offset += spacing; 107 | } 108 | } 109 | } 110 | else 111 | { 112 | foreach (UIElement child in InternalChildren) 113 | { 114 | var childDesiredSize = child.DesiredSize; 115 | 116 | child.Arrange(new Rect(offset, 0, childDesiredSize.Width, finalSize.Height)); 117 | 118 | offset += childDesiredSize.Width; 119 | if (child.Visibility != Visibility.Collapsed) 120 | { 121 | offset += spacing; 122 | } 123 | } 124 | } 125 | 126 | return finalSize; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxButtonClickDeferral.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public sealed class MessageBoxButtonClickDeferral 6 | { 7 | private readonly Action _handler; 8 | 9 | internal MessageBoxButtonClickDeferral(Action handler) 10 | { 11 | _handler = handler ?? throw new ArgumentNullException(nameof(handler)); 12 | } 13 | 14 | public void Complete() 15 | { 16 | _handler(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxButtonClickEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public class MessageBoxButtonClickEventArgs : RoutedEventArgs 7 | { 8 | private MessageBoxButtonClickDeferral _deferral = null!; 9 | private int _deferralCount; 10 | 11 | internal MessageBoxButtonClickEventArgs() 12 | { 13 | } 14 | 15 | public bool Cancel { get; set; } 16 | 17 | public MessageBoxButtonClickDeferral GetDeferral() 18 | { 19 | _deferralCount++; 20 | 21 | return new MessageBoxButtonClickDeferral(() => 22 | { 23 | DecrementDeferralCount(); 24 | }); 25 | } 26 | 27 | internal void SetDeferral(MessageBoxButtonClickDeferral deferral) 28 | { 29 | _deferral = deferral; 30 | } 31 | 32 | internal void DecrementDeferralCount() 33 | { 34 | Debug.Assert(_deferralCount > 0); 35 | _deferralCount--; 36 | if (_deferralCount == 0) 37 | { 38 | _deferral.Complete(); 39 | } 40 | } 41 | 42 | internal void IncrementDeferralCount() 43 | { 44 | _deferralCount++; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxClosedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public class MessageBoxClosedEventArgs : RoutedEventArgs 6 | { 7 | internal MessageBoxClosedEventArgs(MessageBoxResult result) 8 | { 9 | Result = result; 10 | } 11 | 12 | public MessageBoxResult Result { get; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxClosingDeferral.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public sealed class MessageBoxClosingDeferral 6 | { 7 | private readonly Action _handler; 8 | 9 | internal MessageBoxClosingDeferral(Action handler) 10 | { 11 | _handler = handler ?? throw new ArgumentNullException(nameof(handler)); 12 | } 13 | 14 | public void Complete() 15 | { 16 | _handler(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxClosingEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Windows; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public sealed class MessageBoxClosingEventArgs : RoutedEventArgs 7 | { 8 | private MessageBoxClosingDeferral _deferral = null!; 9 | private int _deferralCount; 10 | 11 | internal MessageBoxClosingEventArgs(MessageBoxResult result) 12 | { 13 | Result = result; 14 | } 15 | 16 | public bool Cancel { get; set; } 17 | 18 | public MessageBoxResult Result { get; } 19 | 20 | public MessageBoxClosingDeferral GetDeferral() 21 | { 22 | _deferralCount++; 23 | 24 | return new MessageBoxClosingDeferral(() => 25 | { 26 | DecrementDeferralCount(); 27 | }); 28 | } 29 | 30 | internal void SetDeferral(MessageBoxClosingDeferral deferral) 31 | { 32 | _deferral = deferral; 33 | } 34 | 35 | internal void DecrementDeferralCount() 36 | { 37 | Debug.Assert(_deferralCount > 0); 38 | _deferralCount--; 39 | if (_deferralCount == 0) 40 | { 41 | _deferral.Complete(); 42 | } 43 | } 44 | 45 | internal void IncrementDeferralCount() 46 | { 47 | _deferralCount++; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxContentTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace Wpf.Ui.Violeta.Controls; 6 | 7 | internal sealed class MessageBoxContentTemplateSelector : DataTemplateSelector 8 | { 9 | public DataTemplate? StringTemplate { get; set; } 10 | public DataTemplate? DefaultTemplate { get; set; } 11 | 12 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 13 | { 14 | if (item is string) 15 | { 16 | return StringTemplate ?? throw new InvalidOperationException(nameof(StringTemplate)); 17 | } 18 | 19 | return DefaultTemplate ?? throw new InvalidOperationException(nameof(DefaultTemplate)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxIcon.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Controls; 2 | 3 | public enum MessageBoxIcon 4 | { 5 | None, 6 | Information, 7 | Success, 8 | Error, 9 | Warning, 10 | Question, 11 | } 12 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxIconConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using Wpf.Ui.Controls; 6 | 7 | namespace Wpf.Ui.Violeta.Controls; 8 | 9 | [ValueConversion(typeof(MessageBoxIcon), typeof(string))] 10 | public sealed class MessageBoxIconConverter : IValueConverter 11 | { 12 | public static MessageBoxIconConverter New => new(); 13 | 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is MessageBoxIcon icon) 17 | { 18 | return icon switch 19 | { 20 | MessageBoxIcon.Information => FontSymbols.Info, 21 | MessageBoxIcon.Success => FontSymbols.Accept, 22 | MessageBoxIcon.Warning => FontSymbols.Warning, 23 | MessageBoxIcon.Error => FontSymbols.Cancel, 24 | MessageBoxIcon.Question => FontSymbols.Unknown, 25 | MessageBoxIcon.None or _ => string.Empty, 26 | }; 27 | } 28 | return string.Empty; 29 | } 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | return DependencyProperty.UnsetValue; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxIconExtensions.cs: -------------------------------------------------------------------------------- 1 | using Wpf.Ui.Controls; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public static class MessageBoxIconExtensions 6 | { 7 | public static MessageBoxIcon ToMessageBoxIcon(this IconSource icon) 8 | { 9 | MessageBoxIcon boxIcon = MessageBoxIcon.None; 10 | 11 | if (icon is FontIconSource ficon) 12 | { 13 | if (ficon.Glyph == MessageBoxSymbolGlyph.Error.ToGlyph()) 14 | { 15 | boxIcon = MessageBoxIcon.Error; 16 | } 17 | else if (ficon.Glyph == MessageBoxSymbolGlyph.Info.ToGlyph()) 18 | { 19 | boxIcon = MessageBoxIcon.Information; 20 | } 21 | else if (ficon.Glyph == MessageBoxSymbolGlyph.Warning.ToGlyph()) 22 | { 23 | boxIcon = MessageBoxIcon.Warning; 24 | } 25 | else if (ficon.Glyph == MessageBoxSymbolGlyph.Question.ToGlyph()) 26 | { 27 | boxIcon = MessageBoxIcon.Question; 28 | } 29 | else if (ficon.Glyph == MessageBoxSymbolGlyph.None.ToGlyph()) 30 | { 31 | boxIcon = MessageBoxIcon.None; 32 | } 33 | } 34 | return boxIcon; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxIconForegroundConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using System.Windows.Media; 6 | 7 | namespace Wpf.Ui.Violeta.Controls; 8 | 9 | [ValueConversion(typeof(MessageBoxIcon), typeof(SolidColorBrush))] 10 | public sealed class MessageBoxIconForegroundConverter : IValueConverter 11 | { 12 | public static MessageBoxIconForegroundConverter New => new(); 13 | 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is MessageBoxIcon icon) 17 | { 18 | return icon switch 19 | { 20 | MessageBoxIcon.Information => "#55CEF1".ToColor().ToBrush(), 21 | MessageBoxIcon.Success => "#75CD43".ToColor().ToBrush(), 22 | MessageBoxIcon.Warning => "#F9D01A".ToColor().ToBrush(), 23 | MessageBoxIcon.Error => "#FF5656".ToColor().ToBrush(), 24 | MessageBoxIcon.Question => "#55CEF1".ToColor().ToBrush(), 25 | MessageBoxIcon.None or _ => "#55CEF1".ToColor().ToBrush(), 26 | }; 27 | } 28 | return "#55CEF1".ToColor().ToBrush(); 29 | } 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | return DependencyProperty.UnsetValue; 34 | } 35 | } 36 | 37 | file static class ColorExtension 38 | { 39 | public static Color ToColor(this string hex) => (Color)ColorConverter.ConvertFromString(hex); 40 | 41 | public static SolidColorBrush ToBrush(this Color color) => new(color); 42 | } 43 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxImageExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public static class MessageBoxImageExtensions 7 | { 8 | public static string ToGlyph(this MessageBoxSymbolGlyph symbol) 9 | { 10 | return char.ConvertFromUtf32((int)symbol); 11 | } 12 | 13 | public static MessageBoxSymbolGlyph ToSymbol(this MessageBoxImage image) 14 | { 15 | return image switch 16 | { 17 | MessageBoxImage.Error => MessageBoxSymbolGlyph.Error, 18 | MessageBoxImage.Information => MessageBoxSymbolGlyph.Info, 19 | MessageBoxImage.Warning => MessageBoxSymbolGlyph.Warning, 20 | MessageBoxImage.Question => MessageBoxSymbolGlyph.Question, 21 | MessageBoxImage.None => (MessageBoxSymbolGlyph)0x2007, 22 | _ => throw new NotSupportedException(), 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxOpenedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public class MessageBoxOpenedEventArgs : RoutedEventArgs 6 | { 7 | internal MessageBoxOpenedEventArgs() 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxSymbolGlyph.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Controls; 2 | 3 | public enum MessageBoxSymbolGlyph 4 | { 5 | Info = 0xe946, 6 | Error = 0xe783, 7 | Warning = 0xe7ba, 8 | Question = 0xe9ce, 9 | None = 0x2007, 10 | } 11 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/MessageBox/MessageBoxTemplateSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using Wpf.Ui.Controls; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public class MessageBoxTemplateSettings : DependencyObject 7 | { 8 | internal MessageBoxTemplateSettings() 9 | { 10 | } 11 | 12 | private static readonly DependencyPropertyKey IconElementPropertyKey = 13 | DependencyProperty.RegisterReadOnly(nameof(IconElement), typeof(IconElement), typeof(MessageBoxTemplateSettings), null); 14 | 15 | public static readonly DependencyProperty IconElementProperty = IconElementPropertyKey.DependencyProperty; 16 | 17 | public IconElement IconElement 18 | { 19 | get => (IconElement)GetValue(IconElementProperty); 20 | internal set => SetValue(IconElementPropertyKey, value); 21 | } 22 | 23 | public static readonly DependencyProperty OKButtonTextProperty = 24 | DependencyProperty.Register(nameof(OKButtonText), typeof(string), typeof(MessageBoxTemplateSettings), new PropertyMetadata("OK")); 25 | 26 | public string OKButtonText 27 | { 28 | get => (string)GetValue(OKButtonTextProperty); 29 | set => SetValue(OKButtonTextProperty, value); 30 | } 31 | 32 | public static readonly DependencyProperty YesButtonTextProperty = 33 | DependencyProperty.Register(nameof(YesButtonText), typeof(string), typeof(MessageBoxTemplateSettings), new PropertyMetadata("YES")); 34 | 35 | public string YesButtonText 36 | { 37 | get => (string)GetValue(YesButtonTextProperty); 38 | set => SetValue(YesButtonTextProperty, value); 39 | } 40 | 41 | public static readonly DependencyProperty NoButtonTextProperty = 42 | DependencyProperty.Register(nameof(NoButtonText), typeof(string), typeof(MessageBoxTemplateSettings), new PropertyMetadata("NO")); 43 | 44 | public string NoButtonText 45 | { 46 | get => (string)GetValue(NoButtonTextProperty); 47 | set => SetValue(NoButtonTextProperty, value); 48 | } 49 | 50 | public static readonly DependencyProperty CancelButtonTextProperty = 51 | DependencyProperty.Register(nameof(CancelButtonText), typeof(string), typeof(MessageBoxTemplateSettings), new PropertyMetadata("CANCEL")); 52 | 53 | public string CancelButtonText 54 | { 55 | get => (string)GetValue(CancelButtonTextProperty); 56 | set => SetValue(CancelButtonTextProperty, value); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/PendingBox/IPendingHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public interface IPendingHandler : IDisposable 6 | { 7 | public event EventHandler? Closed; 8 | 9 | public event EventHandler? Cancel; 10 | 11 | public DateTime StartTime { get; set; } 12 | 13 | public string? Message { get; set; } 14 | 15 | public bool IsShowCancel { get; set; } 16 | 17 | public bool Canceled { get; } 18 | 19 | public void Show(); 20 | 21 | public bool? ShowDialog(); 22 | 23 | public void Close(); 24 | } 25 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/PendingBox/Loading.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/PendingBox/PendingBoxDialog.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 16 | 17 | 71 | 72 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/PendingBox/PendingHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public class PendingHandler(PendingBoxDialog pendingBoxDialog) : IPendingHandler 7 | { 8 | public PendingBoxDialog Dialog { get; protected set; } = pendingBoxDialog; 9 | 10 | public event EventHandler? Closed; 11 | 12 | public event EventHandler? Cancel; 13 | 14 | public DateTime StartTime { get; set; } = DateTime.Now; 15 | 16 | public virtual string? Message 17 | { 18 | get => Dialog.Dispatcher.Invoke(() => Dialog.Message); 19 | set => Dialog.Dispatcher.Invoke(() => Dialog.Message = value); 20 | } 21 | 22 | public virtual bool IsShowCancel 23 | { 24 | get => Dialog.Dispatcher.Invoke(() => Dialog.IsShowCancel); 25 | set => Dialog.Dispatcher.Invoke(() => Dialog.IsShowCancel = value); 26 | } 27 | 28 | public virtual bool Canceled { get; protected set; } = false; 29 | 30 | public virtual bool CloseOnCanceled { get; set; } = true; 31 | 32 | [SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize")] 33 | [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression")] 34 | public virtual void Dispose() 35 | { 36 | try 37 | { 38 | Close(); 39 | } 40 | catch 41 | { 42 | /// 43 | } 44 | } 45 | 46 | public virtual void Show() 47 | => Dialog?.Show(); 48 | 49 | [Obsolete("Use Show instead")] 50 | public virtual bool? ShowDialog() 51 | => Dialog?.ShowDialog(); 52 | 53 | public virtual void Close() 54 | { 55 | Dialog?.Dispatcher.Invoke(() => 56 | { 57 | if (Dialog != null) 58 | { 59 | Dialog.IsClosedByHandler = true; 60 | Dialog.Close(); 61 | } 62 | }); 63 | } 64 | 65 | public virtual void RaiseClosedEvent(object? sender, EventArgs e) 66 | => Closed?.Invoke(sender, e); 67 | 68 | public virtual void RaiseCanceledEvent(object? sender, EventArgs e) 69 | { 70 | Cancel?.Invoke(sender, e); 71 | if (CloseOnCanceled) 72 | { 73 | Close(); 74 | } 75 | Canceled = true; 76 | } 77 | } 78 | 79 | public class PendingHandlerAsync(PendingBoxDialog pendingBoxDialog) : PendingHandler(pendingBoxDialog); 80 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/PersonPicture/PersonPictureAutomationPeer.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Automation.Peers; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public class PersonPictureAutomationPeer(PersonPicture owner) : FrameworkElementAutomationPeer(owner) 6 | { 7 | protected override AutomationControlType GetAutomationControlTypeCore() 8 | { 9 | return AutomationControlType.Text; 10 | } 11 | 12 | protected override string GetClassNameCore() 13 | { 14 | return nameof(PersonPicture); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/PersonPicture/PersonPictureTemplateSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Media; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public sealed class PersonPictureTemplateSettings : DependencyObject 7 | { 8 | #region ActualImageBrush 9 | 10 | private static readonly DependencyPropertyKey ActualImageBrushPropertyKey = 11 | DependencyProperty.RegisterReadOnly( 12 | nameof(ActualImageBrush), 13 | typeof(ImageBrush), 14 | typeof(PersonPictureTemplateSettings), 15 | null); 16 | 17 | public static readonly DependencyProperty ActualImageBrushProperty = 18 | ActualImageBrushPropertyKey.DependencyProperty; 19 | 20 | public ImageBrush ActualImageBrush 21 | { 22 | get => (ImageBrush)GetValue(ActualImageBrushProperty); 23 | internal set => SetValue(ActualImageBrushPropertyKey, value); 24 | } 25 | 26 | #endregion ActualImageBrush 27 | 28 | #region ActualInitials 29 | 30 | private static readonly DependencyPropertyKey ActualInitialsPropertyKey = 31 | DependencyProperty.RegisterReadOnly( 32 | nameof(ActualInitials), 33 | typeof(string), 34 | typeof(PersonPictureTemplateSettings), 35 | new PropertyMetadata(string.Empty)); 36 | 37 | public static readonly DependencyProperty ActualInitialsProperty = 38 | ActualInitialsPropertyKey.DependencyProperty; 39 | 40 | public string ActualInitials 41 | { 42 | get => (string)GetValue(ActualInitialsProperty); 43 | internal set => SetValue(ActualInitialsPropertyKey, value); 44 | } 45 | 46 | #endregion ActualInitials 47 | } 48 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Primitives/IconElementEx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Data; 6 | using System.Windows.Documents; 7 | using System.Windows.Media; 8 | using Wpf.Ui.Controls; 9 | using Grid = System.Windows.Controls.Grid; 10 | 11 | namespace Wpf.Ui.Violeta.Controls.Primitives; 12 | 13 | [TypeConverter(typeof(IconElementConverter))] 14 | public abstract class IconElementEx : IconElement 15 | { 16 | private protected IconElementEx() 17 | { 18 | } 19 | 20 | protected override void OnForegroundChanged(DependencyPropertyChangedEventArgs args) 21 | { 22 | var baseValueSource = DependencyPropertyHelper.GetValueSource(this, args.Property).BaseValueSource; 23 | _isForegroundDefaultOrInherited = baseValueSource <= BaseValueSource.Inherited; 24 | UpdateShouldInheritForegroundFromVisualParent(); 25 | } 26 | 27 | private static readonly DependencyProperty VisualParentForegroundProperty = 28 | DependencyProperty.Register( 29 | nameof(VisualParentForeground), 30 | typeof(Brush), 31 | typeof(IconElementEx), 32 | new PropertyMetadata(null, OnVisualParentForegroundPropertyChanged)); 33 | 34 | protected Brush VisualParentForeground 35 | { 36 | get => (Brush)GetValue(VisualParentForegroundProperty); 37 | set => SetValue(VisualParentForegroundProperty, value); 38 | } 39 | 40 | private static void OnVisualParentForegroundPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 41 | { 42 | ((IconElementEx)sender).OnVisualParentForegroundPropertyChanged(args); 43 | } 44 | 45 | private protected virtual void OnVisualParentForegroundPropertyChanged(DependencyPropertyChangedEventArgs args) 46 | { 47 | } 48 | 49 | protected bool ShouldInheritForegroundFromVisualParent 50 | { 51 | get => _shouldInheritForegroundFromVisualParent; 52 | private set 53 | { 54 | if (_shouldInheritForegroundFromVisualParent != value) 55 | { 56 | _shouldInheritForegroundFromVisualParent = value; 57 | 58 | if (_shouldInheritForegroundFromVisualParent) 59 | { 60 | SetBinding(VisualParentForegroundProperty, 61 | new Binding 62 | { 63 | Path = new PropertyPath(TextElement.ForegroundProperty), 64 | Source = VisualParent 65 | }); 66 | } 67 | else 68 | { 69 | ClearValue(VisualParentForegroundProperty); 70 | } 71 | 72 | OnShouldInheritForegroundFromVisualParentChanged(); 73 | } 74 | } 75 | } 76 | 77 | private protected virtual void OnShouldInheritForegroundFromVisualParentChanged() 78 | { 79 | } 80 | 81 | private void UpdateShouldInheritForegroundFromVisualParent() 82 | { 83 | ShouldInheritForegroundFromVisualParent = 84 | _isForegroundDefaultOrInherited && 85 | Parent != null && 86 | VisualParent != null && 87 | Parent != VisualParent; 88 | } 89 | 90 | protected UIElementCollection Children 91 | { 92 | get 93 | { 94 | EnsureLayoutRoot(); 95 | return _layoutRoot.Children; 96 | } 97 | } 98 | 99 | protected override int VisualChildrenCount => 1; 100 | 101 | protected override Visual GetVisualChild(int index) 102 | { 103 | if (index == 0) 104 | { 105 | EnsureLayoutRoot(); 106 | return _layoutRoot; 107 | } 108 | else 109 | { 110 | throw new ArgumentOutOfRangeException(nameof(index)); 111 | } 112 | } 113 | 114 | protected override Size MeasureOverride(Size availableSize) 115 | { 116 | EnsureLayoutRoot(); 117 | _layoutRoot.Measure(availableSize); 118 | return _layoutRoot.DesiredSize; 119 | } 120 | 121 | protected override Size ArrangeOverride(Size finalSize) 122 | { 123 | EnsureLayoutRoot(); 124 | _layoutRoot.Arrange(new Rect(new Point(), finalSize)); 125 | return finalSize; 126 | } 127 | 128 | protected override void OnVisualParentChanged(DependencyObject oldParent) 129 | { 130 | base.OnVisualParentChanged(oldParent); 131 | UpdateShouldInheritForegroundFromVisualParent(); 132 | } 133 | 134 | private void EnsureLayoutRoot() 135 | { 136 | if (_layoutRoot != null) 137 | return; 138 | 139 | _layoutRoot = new Grid 140 | { 141 | Background = Brushes.Transparent, 142 | SnapsToDevicePixels = true, 143 | }; 144 | _ = InitializeChildren(); 145 | 146 | AddVisualChild(_layoutRoot); 147 | } 148 | 149 | private Grid _layoutRoot = null!; 150 | private bool _isForegroundDefaultOrInherited = true; 151 | private bool _shouldInheritForegroundFromVisualParent; 152 | } 153 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Primitives/MenuItemGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace Wpf.Ui.Violeta.Controls.Primitives; 6 | 7 | public class MenuItemGroup : List 8 | { 9 | public bool IsCanCancel { get; set; } = false; 10 | 11 | public static MenuItemGroup GetGroup(DependencyObject obj) 12 | { 13 | return (MenuItemGroup)obj.GetValue(GroupProperty); 14 | } 15 | 16 | public static void SetGroup(DependencyObject obj, MenuItemGroup value) 17 | { 18 | obj.SetValue(GroupProperty, value); 19 | } 20 | 21 | public static readonly DependencyProperty GroupProperty = 22 | DependencyProperty.RegisterAttached("Group", typeof(MenuItemGroup), typeof(MenuItemGroup), new PropertyMetadata(null!, OnGroupChanged)); 23 | 24 | private static void OnGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 25 | { 26 | if (d is MenuItem tb) 27 | { 28 | ((MenuItemGroup)e.NewValue).Join(tb); 29 | } 30 | } 31 | 32 | protected bool Handling { get; set; } = false; 33 | 34 | public MenuItemGroup JoinWith(MenuItem menuItem) 35 | { 36 | Join(menuItem); 37 | return this; 38 | } 39 | 40 | public void Join(MenuItem menuItem) 41 | { 42 | Add(menuItem); 43 | 44 | menuItem.Checked += (s, e) => 45 | { 46 | if (s is MenuItem mi && GetGroup(mi) is MenuItemGroup group) 47 | { 48 | Handling = true; 49 | foreach (MenuItem tb in group) 50 | { 51 | if (tb != mi) 52 | { 53 | tb.IsChecked = false; 54 | } 55 | } 56 | Handling = false; 57 | } 58 | }; 59 | menuItem.Unchecked += (s, e) => 60 | { 61 | if (!IsCanCancel && !Handling && s is MenuItem mi) 62 | { 63 | mi.IsChecked = true; 64 | } 65 | }; 66 | SetGroup(menuItem, this); 67 | } 68 | 69 | public void Unjoin(MenuItem checkBox) 70 | { 71 | Remove(checkBox); 72 | SetGroup(checkBox, null!); 73 | } 74 | 75 | public static MenuItemGroup New() => []; 76 | } 77 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Primitives/RadioButtonGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace Wpf.Ui.Violeta.Controls.Primitives; 6 | 7 | public class RadioButtonGroup : List 8 | { 9 | public static RadioButtonGroup GetGroup(DependencyObject obj) 10 | { 11 | return (RadioButtonGroup)obj.GetValue(GroupProperty); 12 | } 13 | 14 | public static void SetGroup(DependencyObject obj, RadioButtonGroup value) 15 | { 16 | obj.SetValue(GroupProperty, value); 17 | } 18 | 19 | public static readonly DependencyProperty GroupProperty = 20 | DependencyProperty.RegisterAttached("Group", typeof(RadioButtonGroup), typeof(RadioButtonGroup), new PropertyMetadata(null!, OnGroupChanged)); 21 | 22 | private static void OnGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 23 | { 24 | if (d is RadioButton tb) 25 | { 26 | ((RadioButtonGroup)e.NewValue).Join(tb); 27 | } 28 | } 29 | 30 | protected bool Handling { get; set; } = false; 31 | 32 | public RadioButtonGroup JoinWith(RadioButton radioButton) 33 | { 34 | Join(radioButton); 35 | return this; 36 | } 37 | 38 | public void Join(RadioButton radioButton) 39 | { 40 | Add(radioButton); 41 | 42 | radioButton.Checked += (s, e) => 43 | { 44 | if (s is RadioButton cb && GetGroup(cb) is RadioButtonGroup group) 45 | { 46 | Handling = true; 47 | foreach (RadioButton tb in group) 48 | { 49 | if (tb != cb) 50 | { 51 | tb.IsChecked = false; 52 | } 53 | } 54 | Handling = false; 55 | } 56 | }; 57 | radioButton.Unchecked += (s, e) => 58 | { 59 | if (!Handling && s is RadioButton tb) 60 | { 61 | tb.IsChecked = true; 62 | } 63 | }; 64 | SetGroup(radioButton, this); 65 | } 66 | 67 | public void Unjoin(RadioButton checkBox) 68 | { 69 | Remove(checkBox); 70 | SetGroup(checkBox, null!); 71 | } 72 | 73 | public static RadioButtonGroup New() => []; 74 | } 75 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Primitives/ToggleButtonGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Windows; 4 | using System.Windows.Controls.Primitives; 5 | 6 | namespace Wpf.Ui.Violeta.Controls.Primitives; 7 | 8 | public class ToggleButtonGroup : List 9 | { 10 | public bool IsCanCancel { get; set; } = false; 11 | 12 | public static ToggleButtonGroup GetGroup(DependencyObject obj) 13 | { 14 | return (ToggleButtonGroup)obj.GetValue(GroupProperty); 15 | } 16 | 17 | public static void SetGroup(DependencyObject obj, ToggleButtonGroup value) 18 | { 19 | obj.SetValue(GroupProperty, value); 20 | } 21 | 22 | public static readonly DependencyProperty GroupProperty = 23 | DependencyProperty.RegisterAttached("Group", typeof(ToggleButtonGroup), typeof(ToggleButtonGroup), new PropertyMetadata(null!, OnGroupChanged)); 24 | 25 | private static void OnGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 26 | { 27 | if (d is ToggleButton tb) 28 | { 29 | ((ToggleButtonGroup)e.NewValue).Join(tb); 30 | } 31 | } 32 | 33 | protected bool Handling { get; set; } = false; 34 | 35 | public ToggleButtonGroup JoinWith(ToggleButton toggleButton) 36 | { 37 | Join(toggleButton); 38 | return this; 39 | } 40 | 41 | public void Join(ToggleButton toggleButton) 42 | { 43 | Add(toggleButton); 44 | 45 | toggleButton.Checked += (s, e) => 46 | { 47 | if (s is ToggleButton cb && GetGroup(cb) is ToggleButtonGroup group) 48 | { 49 | Handling = true; 50 | foreach (ToggleButton tb in group) 51 | { 52 | if (tb != cb) 53 | { 54 | tb.IsChecked = false; 55 | } 56 | } 57 | Handling = false; 58 | } 59 | }; 60 | toggleButton.Unchecked += (s, e) => 61 | { 62 | if (!IsCanCancel && !Handling && s is ToggleButton cb) 63 | { 64 | cb.IsChecked = true; 65 | } 66 | }; 67 | SetGroup(toggleButton, this); 68 | } 69 | 70 | public void Unjoin(ToggleButton checkBox) 71 | { 72 | Remove(checkBox); 73 | SetGroup(checkBox, null!); 74 | } 75 | 76 | public static ToggleButtonGroup New() => []; 77 | } 78 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Primitives/VisualStateGroupListener.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Wpf.Ui.Controls.Primitives; 4 | 5 | public class VisualStateGroupListener : FrameworkElement 6 | { 7 | static VisualStateGroupListener() 8 | { 9 | VisibilityProperty.OverrideMetadata(typeof(VisualStateGroupListener), new FrameworkPropertyMetadata(Visibility.Collapsed)); 10 | } 11 | 12 | public VisualStateGroupListener() 13 | { 14 | } 15 | 16 | public static readonly DependencyProperty GroupProperty = 17 | DependencyProperty.Register(nameof(Group), typeof(VisualStateGroup), typeof(VisualStateGroupListener), new PropertyMetadata(OnGroupChanged)); 18 | 19 | public VisualStateGroup Group 20 | { 21 | get => (VisualStateGroup)GetValue(GroupProperty); 22 | set => SetValue(GroupProperty, value); 23 | } 24 | 25 | private static void OnGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 26 | { 27 | ((VisualStateGroupListener)d).OnGroupChanged((VisualStateGroup)e.OldValue, (VisualStateGroup)e.NewValue); 28 | } 29 | 30 | private void OnGroupChanged(VisualStateGroup oldGroup, VisualStateGroup newGroup) 31 | { 32 | if (oldGroup != null) 33 | { 34 | oldGroup.CurrentStateChanged -= OnCurrentStateChanged; 35 | } 36 | 37 | if (newGroup != null) 38 | { 39 | newGroup.CurrentStateChanged += OnCurrentStateChanged; 40 | } 41 | 42 | UpdateCurrentStateName(newGroup?.CurrentState!); 43 | } 44 | 45 | private void OnCurrentStateChanged(object? sender, VisualStateChangedEventArgs e) 46 | { 47 | UpdateCurrentStateName(e.NewState); 48 | } 49 | 50 | private static readonly DependencyPropertyKey CurrentStateNamePropertyKey = 51 | DependencyProperty.RegisterReadOnly(nameof(CurrentStateName), typeof(string), typeof(VisualStateGroupListener), null); 52 | 53 | public static readonly DependencyProperty CurrentStateNameProperty = 54 | CurrentStateNamePropertyKey.DependencyProperty; 55 | 56 | public string CurrentStateName 57 | { 58 | get => (string)GetValue(CurrentStateNameProperty); 59 | private set => SetValue(CurrentStateNamePropertyKey, value); 60 | } 61 | 62 | private void UpdateCurrentStateName(VisualState currentState) 63 | { 64 | if (currentState != null) 65 | { 66 | CurrentStateName = currentState.Name; 67 | } 68 | else 69 | { 70 | ClearValue(CurrentStateNamePropertyKey); 71 | } 72 | } 73 | 74 | public static readonly DependencyProperty ListenerProperty = 75 | DependencyProperty.RegisterAttached("Listener", typeof(VisualStateGroupListener), typeof(VisualStateGroupListener), new PropertyMetadata(OnListenerChanged)); 76 | 77 | public static VisualStateGroupListener GetListener(VisualStateGroup group) 78 | { 79 | return (VisualStateGroupListener)group.GetValue(ListenerProperty); 80 | } 81 | 82 | public static void SetListener(VisualStateGroup group, VisualStateGroupListener value) 83 | { 84 | group.SetValue(ListenerProperty, value); 85 | } 86 | 87 | private static void OnListenerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 88 | { 89 | if (e.OldValue is VisualStateGroupListener oldListener) 90 | { 91 | oldListener.ClearValue(GroupProperty); 92 | } 93 | 94 | if (e.NewValue is VisualStateGroupListener newListener) 95 | { 96 | newListener.Group = (VisualStateGroup)d; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/SmoothScrollViewer/SmoothScrollViewer.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Splash/Splash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO.Packaging; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Interop; 8 | using System.Windows.Threading; 9 | using Wpf.Ui.Violeta.Win32; 10 | 11 | namespace Wpf.Ui.Violeta.Controls; 12 | 13 | public static class Splash 14 | { 15 | private static STAThread? Current { get; set; } 16 | 17 | static Splash() 18 | { 19 | if (!UriParser.IsKnownScheme("pack")) 20 | { 21 | // Ensure the Pack URI scheme is registered. 22 | _ = PackUriHelper.UriSchemePack; 23 | } 24 | } 25 | 26 | public static void ShowAsync(string imageUriString, double opacity = 1d, Action? completed = null) 27 | { 28 | ShowAsync(new Uri(imageUriString), opacity, completed); 29 | } 30 | 31 | public static void ShowAsync(Uri imageUri, double opacity = 1d, Action? completed = null) 32 | { 33 | Current = new(sta => 34 | { 35 | if (string.IsNullOrWhiteSpace(sta.Value.Name)) 36 | { 37 | sta.Value.Name = "Splash Thread"; 38 | } 39 | sta.Result = new SplashWindow(imageUri) 40 | { 41 | Opacity = opacity, 42 | }; 43 | sta.Result.Show(); 44 | }); 45 | Current.Start(); 46 | 47 | if (completed != null) 48 | { 49 | _ = Task.Run(() => 50 | { 51 | try 52 | { 53 | Current.Value.Join(); 54 | completed?.Invoke(); 55 | } 56 | catch (Exception ex) 57 | { 58 | Debug.WriteLine(ex); 59 | } 60 | }); 61 | } 62 | } 63 | 64 | public static void CloseAsync(Window? owner = null, bool forced = false) 65 | { 66 | try 67 | { 68 | SplashWindow? current = Current?.Result; 69 | 70 | if (current == null) 71 | { 72 | return; 73 | } 74 | current.Closing += (_, _) => 75 | { 76 | owner?.Dispatcher.Invoke(() => 77 | { 78 | nint hwnd = new WindowInteropHelper(owner).Handle; 79 | 80 | _ = User32.SetForegroundWindow(hwnd); 81 | _ = User32.BringWindowToTop(hwnd); 82 | }); 83 | }; 84 | if (forced) 85 | { 86 | current.Shutdown(); 87 | } 88 | else 89 | { 90 | if (!current.AutoEnd) 91 | { 92 | current.StartEnd(); 93 | } 94 | } 95 | } 96 | catch (Exception ex) 97 | { 98 | Debug.WriteLine(ex); 99 | } 100 | } 101 | 102 | public static void CloseOnLoaded(Window? owner = null, int? minimumMilliseconds = null, bool forced = false) 103 | { 104 | if (owner == null) 105 | { 106 | throw new ArgumentNullException(nameof(owner)); 107 | } 108 | 109 | owner.Loaded += OnLoaded; 110 | 111 | async void OnLoaded(object? sender, RoutedEventArgs e) 112 | { 113 | owner.Loaded -= OnLoaded; 114 | 115 | if (minimumMilliseconds != null) 116 | { 117 | SplashWindow? current = Current?.Result; 118 | 119 | if (current != null) 120 | { 121 | double survivalMilliseconds = (DateTime.Now - current.TimeOfCtor).TotalMilliseconds; 122 | if (survivalMilliseconds < minimumMilliseconds) 123 | { 124 | await Task.Delay((int)(minimumMilliseconds.Value - survivalMilliseconds)); 125 | } 126 | } 127 | } 128 | 129 | CloseAsync(owner, forced); 130 | } 131 | } 132 | 133 | private sealed class STAThread(Action> start) : STAThread(start) 134 | { 135 | } 136 | 137 | private class STAThread : STADispatcherObject, IDisposable where T : class 138 | { 139 | public Thread Value { get; set; } = null!; 140 | public T Result { get; set; } = null!; 141 | 142 | public STAThread(Action> start) 143 | { 144 | Value = new(() => 145 | { 146 | Dispatcher = Dispatcher.CurrentDispatcher; 147 | start?.Invoke(this); 148 | Dispatcher.Run(); 149 | }) 150 | { 151 | IsBackground = true, 152 | Name = $"STAThread<{typeof(T)}>", 153 | }; 154 | Value.SetApartmentState(ApartmentState.STA); 155 | } 156 | 157 | public void Start() 158 | { 159 | Value?.Start(); 160 | } 161 | 162 | public void Forget() 163 | { 164 | Dispose(); 165 | } 166 | 167 | public void Dispose() 168 | { 169 | if (typeof(IDisposable).IsAssignableFrom(typeof(T))) 170 | { 171 | try 172 | { 173 | ((IDisposable?)Result)?.Dispose(); 174 | } 175 | catch 176 | { 177 | } 178 | } 179 | Dispatcher?.InvokeShutdown(); 180 | } 181 | } 182 | 183 | private class STADispatcherObject(Dispatcher dispatcher = null!) 184 | { 185 | public Dispatcher Dispatcher { get; set; } = dispatcher; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Splash/SplashConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Wpf.Ui.Violeta.Controls; 4 | 5 | public sealed class SplashConfig 6 | { 7 | public static double ImageHeight { get; set; } = 300d; 8 | 9 | public static CornerRadius CornerRadius { get; set; } = new CornerRadius(24d); 10 | } 11 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Splash/SplashWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Runtime.CompilerServices; 5 | using System.Windows; 6 | using System.Windows.Media.Animation; 7 | 8 | namespace Wpf.Ui.Violeta.Controls; 9 | 10 | public partial class SplashWindow : Window, INotifyPropertyChanged 11 | { 12 | public event PropertyChangedEventHandler? PropertyChanged; 13 | 14 | public Uri ImageUri { get; } 15 | 16 | protected string hint = null!; 17 | 18 | public string Hint 19 | { 20 | get => hint; 21 | set => SetProperty(ref hint, value); 22 | } 23 | 24 | public bool AutoEnd { get; set; } = false; 25 | 26 | public DateTime TimeOfCtor = DateTime.Now; 27 | 28 | public SplashWindow(Uri imageUri) 29 | { 30 | DataContext = this; 31 | ImageUri = imageUri; 32 | InitializeComponent(); 33 | 34 | MouseLeftButtonDown += (sender, _) => 35 | { 36 | if (sender is DependencyObject depObject) 37 | { 38 | GetWindow(depObject)?.DragMove(); 39 | } 40 | }; 41 | } 42 | 43 | protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) 44 | { 45 | PropertyChanged?.Invoke(this, e); 46 | } 47 | 48 | protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) 49 | { 50 | OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 51 | } 52 | 53 | protected bool SetProperty(ref T field, T newValue, [CallerMemberName] string? propertyName = null) 54 | { 55 | if (EqualityComparer.Default.Equals(field, newValue)) 56 | { 57 | return false; 58 | } 59 | 60 | field = newValue; 61 | OnPropertyChanged(propertyName); 62 | return true; 63 | } 64 | 65 | private void Start_Completed(object? sender, EventArgs e) 66 | { 67 | if (AutoEnd) 68 | { 69 | StartEnd(); 70 | } 71 | } 72 | 73 | private void End_Completed(object? sender, EventArgs e) 74 | { 75 | Shutdown(); 76 | } 77 | 78 | public void StartEnd() 79 | { 80 | Dispatcher.Invoke(() => 81 | { 82 | Storyboard storyboard = (Storyboard)FindResource("End"); 83 | storyboard.Begin(); 84 | }); 85 | } 86 | 87 | public void Shutdown() 88 | { 89 | Dispatcher.Invoke(() => 90 | { 91 | Close(); 92 | Dispatcher?.InvokeShutdown(); 93 | }); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TextBox/TextBox.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 12 | 13 | 88 | 89 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Toast/Toast.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Windows; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public static class Toast 7 | { 8 | public static void Information(FrameworkElement owner, string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 9 | => Show(owner, message, new ToastConfig(ToastIcon.Information, location, offsetMargin, time)); 10 | 11 | public static void Information(string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 12 | => Show(null!, message, new ToastConfig(ToastIcon.Information, location, offsetMargin, time)); 13 | 14 | public static void Success(FrameworkElement owner, string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 15 | => Show(owner, message, new ToastConfig(ToastIcon.Success, location, offsetMargin, time)); 16 | 17 | public static void Success(string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 18 | => Show(null!, message, new ToastConfig(ToastIcon.Success, location, offsetMargin, time)); 19 | 20 | public static void Error(FrameworkElement owner, string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 21 | => Show(owner, message, new ToastConfig(ToastIcon.Error, location, offsetMargin, time)); 22 | 23 | public static void Error(string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 24 | => Show(null!, message, new ToastConfig(ToastIcon.Error, location, offsetMargin, time)); 25 | 26 | public static void Warning(FrameworkElement owner, string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 27 | => Show(owner, message, new ToastConfig(ToastIcon.Warning, location, offsetMargin, time)); 28 | 29 | public static void Warning(string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 30 | => Show(null!, message, new ToastConfig(ToastIcon.Warning, location, offsetMargin, time)); 31 | 32 | public static void Question(FrameworkElement owner, string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 33 | => Show(owner, message, new ToastConfig(ToastIcon.Question, location, offsetMargin, time)); 34 | 35 | public static void Question(string message, ToastLocation location = ToastLocation.TopCenter, Thickness offsetMargin = default, int time = ToastConfig.NormalTime) 36 | => Show(null!, message, new ToastConfig(ToastIcon.Question, location, offsetMargin, time)); 37 | 38 | public static void Show(FrameworkElement owner, string message, ToastConfig? options = null) 39 | { 40 | ToastControl toast = new( 41 | owner ?? Application.Current.Windows.OfType() 42 | .Where(win => win.IsActive) 43 | .FirstOrDefault()!, 44 | message, options); 45 | toast.ShowCore(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Toast/ToastConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Media; 3 | 4 | namespace Wpf.Ui.Violeta.Controls; 5 | 6 | public sealed class ToastConfig 7 | { 8 | public const int FastTime = 1500; 9 | public const int NormalTime = 2000; 10 | public const int SlowTime = 3000; 11 | 12 | public int Time { get; set; } = NormalTime; 13 | 14 | public ToastIcon ToastIcon { get; set; } = ToastIcon.None; 15 | 16 | public ToastLocation Location { get; set; } = ToastLocation.TopCenter; 17 | 18 | public FontStyle FontStyle { get; set; } = SystemFonts.MessageFontStyle; 19 | 20 | public FontStretch FontStretch { get; set; } = FontStretches.Normal; 21 | 22 | public double FontSize { get; set; } = SystemFonts.MessageFontSize; 23 | 24 | public FontWeight FontWeight { get; set; } = SystemFonts.MenuFontWeight; 25 | 26 | public double IconSize { get; set; } = 16d; 27 | 28 | public CornerRadius CornerRadius { get; set; } = new CornerRadius(3d); 29 | 30 | public Brush BorderBrush { get; set; } = (Brush)new BrushConverter().ConvertFromString("#1B1B1B")!; 31 | 32 | public Thickness BorderThickness { get; set; } = new Thickness(1d); 33 | 34 | public HorizontalAlignment HorizontalContentAlignment { get; set; } = HorizontalAlignment.Left; 35 | 36 | public VerticalAlignment VerticalContentAlignment { get; set; } = VerticalAlignment.Center; 37 | 38 | public Thickness OffsetMargin { get; set; } = new Thickness(15d); 39 | 40 | public ToastConfig() 41 | { 42 | } 43 | 44 | public ToastConfig(ToastIcon icon, ToastLocation location, Thickness offsetMargin, int time) : this() 45 | { 46 | ToastIcon = icon; 47 | Location = location; 48 | if (offsetMargin != default) 49 | { 50 | OffsetMargin = offsetMargin; 51 | } 52 | Time = time; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Toast/ToastControl.xaml: -------------------------------------------------------------------------------- 1 |  16 | 17 | pack://application:,,,/Wpf.Ui.Violeta;component/Resources/Fonts/Segoe Fluent Icons.ttf#Segoe Fluent Icons 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 42 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Toast/ToastIcon.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Controls; 2 | 3 | public enum ToastIcon 4 | { 5 | None, 6 | Information, 7 | Success, 8 | Error, 9 | Warning, 10 | Question, 11 | } 12 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Toast/ToastIconConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using Wpf.Ui.Controls; 6 | 7 | namespace Wpf.Ui.Violeta.Controls; 8 | 9 | [ValueConversion(typeof(ToastIcon), typeof(string))] 10 | internal sealed class ToastIconConverter : IValueConverter 11 | { 12 | public static ToastIconConverter New => new(); 13 | 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is ToastIcon icon) 17 | { 18 | return icon switch 19 | { 20 | ToastIcon.Information => FontSymbols.Info, 21 | ToastIcon.Success => FontSymbols.Accept, 22 | ToastIcon.Warning => FontSymbols.Warning, 23 | ToastIcon.Error => FontSymbols.Cancel, 24 | ToastIcon.Question => FontSymbols.Unknown, 25 | ToastIcon.None or _ => string.Empty, 26 | }; 27 | } 28 | return string.Empty; 29 | } 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | return DependencyProperty.UnsetValue; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Toast/ToastIconForegroundConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using System.Windows.Media; 6 | 7 | namespace Wpf.Ui.Violeta.Controls; 8 | 9 | [ValueConversion(typeof(ToastIcon), typeof(SolidColorBrush))] 10 | internal sealed class ToastIconForegroundConverter : IValueConverter 11 | { 12 | public static ToastIconForegroundConverter New => new(); 13 | 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value is ToastIcon icon) 17 | { 18 | return icon switch 19 | { 20 | ToastIcon.Information => "#55CEF1".ToColor().ToBrush(), 21 | ToastIcon.Success => "#75CD43".ToColor().ToBrush(), 22 | ToastIcon.Warning => "#F9D01A".ToColor().ToBrush(), 23 | ToastIcon.Error => "#FF5656".ToColor().ToBrush(), 24 | ToastIcon.Question => "#55CEF1".ToColor().ToBrush(), 25 | ToastIcon.None or _ => "#55CEF1".ToColor().ToBrush(), 26 | }; 27 | } 28 | return "#55CEF1".ToColor().ToBrush(); 29 | } 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | return DependencyProperty.UnsetValue; 34 | } 35 | } 36 | 37 | file static class ColorExtension 38 | { 39 | public static Color ToColor(this string hex) => (Color)ColorConverter.ConvertFromString(hex); 40 | 41 | public static SolidColorBrush ToBrush(this Color color) => new(color); 42 | } 43 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Toast/ToastIconVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace Wpf.Ui.Violeta.Controls; 7 | 8 | [ValueConversion(typeof(ToastIcon), typeof(Visibility))] 9 | internal sealed class ToastIconVisibilityConverter : IValueConverter 10 | { 11 | public static ToastIconVisibilityConverter New => new(); 12 | 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if (value is ToastIcon icon) 16 | { 17 | return icon switch 18 | { 19 | ToastIcon.None => Visibility.Collapsed, 20 | _ => Visibility.Visible, 21 | }; 22 | } 23 | return Visibility.Collapsed; 24 | } 25 | 26 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 27 | { 28 | return DependencyProperty.UnsetValue; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/Toast/ToastLocation.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Controls; 2 | 3 | public enum ToastLocation 4 | { 5 | Center, 6 | Left, 7 | Right, 8 | TopLeft, 9 | TopCenter, 10 | TopRight, 11 | BottomLeft, 12 | BottomCenter, 13 | BottomRight, 14 | } 15 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeListView/TreeLevelToIndentConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace Wpf.Ui.Controls; 7 | 8 | [ValueConversion(typeof(int), typeof(Thickness))] 9 | internal sealed class TreeLevelToIndentConverter : IValueConverter 10 | { 11 | private const double IndentSize = 19d; 12 | 13 | public object? Convert(object? value, Type type, object? parameter, CultureInfo culture) 14 | { 15 | return new Thickness((int)value! * IndentSize, 0d, 0d, 0d); 16 | } 17 | 18 | public object? ConvertBack(object? value, Type type, object? parameter, CultureInfo culture) 19 | { 20 | throw new NotSupportedException(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeListView/TreeListView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Reflection; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using Wpf.Ui.Violeta.Controls.Primitives; 7 | 8 | namespace Wpf.Ui.Controls; 9 | 10 | public class TreeListView : TreeView 11 | { 12 | public new object SelectedItem 13 | { 14 | get => GetValue(SelectedItemProperty); 15 | set => SetValue(SelectedItemProperty, value); 16 | } 17 | 18 | public new static readonly DependencyProperty SelectedItemProperty = 19 | DependencyProperty.Register(nameof(SelectedItem), typeof(object), typeof(TreeListView), new PropertyMetadata(null, OnNewSelectedItemChanged)); 20 | 21 | private static void OnNewSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 22 | { 23 | // Use the private `TreeView::SelectedItemProperty` to 24 | // allow writing to the readonly `TreeView::SelectedItemProperty`. 25 | if (d is TreeListView treeListView) 26 | { 27 | object? newValue = e.NewValue; 28 | 29 | if (typeof(TreeView).GetField("SelectedItemPropertyKey", BindingFlags.NonPublic | BindingFlags.Static) is FieldInfo fieldInfo) 30 | { 31 | DependencyPropertyKey selectedItemPropertyKey = (DependencyPropertyKey)fieldInfo.GetValue(null)!; 32 | object? currentValue = treeListView.GetValue(TreeView.SelectedItemProperty); 33 | 34 | if (currentValue != newValue) 35 | { 36 | treeListView.SetValue(selectedItemPropertyKey, newValue); 37 | } 38 | } 39 | } 40 | } 41 | 42 | public TreeListView() 43 | { 44 | SelectedItemChanged += (sender, e) => 45 | { 46 | // Avoid exceptions caused by possible inconsistencies with 47 | // the handling of readonly `TreeView::SelectedItemProperty`. 48 | try 49 | { 50 | if (SelectedItem != base.SelectedItem) 51 | { 52 | SelectedItem = base.SelectedItem; 53 | } 54 | } 55 | catch (Exception ex) 56 | { 57 | Debug.WriteLine(ex); 58 | } 59 | }; 60 | 61 | MouseRightButtonDown += (_, e) => 62 | { 63 | if (e.OriginalSource is DependencyObject source) 64 | { 65 | if (source.FindAscendant() is TreeViewItem { } treeViewItem) 66 | { 67 | treeViewItem.IsSelected = true; 68 | } 69 | } 70 | }; 71 | } 72 | 73 | public GridViewColumnCollection Columns 74 | { 75 | get => (GridViewColumnCollection)GetValue(ColumnsProperty); 76 | set => SetValue(ColumnsProperty, value); 77 | } 78 | 79 | public static readonly DependencyProperty ColumnsProperty = 80 | DependencyProperty.Register(nameof(Columns), typeof(GridViewColumnCollection), typeof(TreeListView), new PropertyMetadata(null)); 81 | 82 | public CornerRadius CornerRadius 83 | { 84 | get => (CornerRadius)GetValue(CornerRadiusProperty); 85 | set => SetValue(CornerRadiusProperty, value); 86 | } 87 | 88 | public static readonly DependencyProperty CornerRadiusProperty = 89 | DependencyProperty.Register(nameof(CornerRadius), typeof(CornerRadius), typeof(TreeListView), new PropertyMetadata(new CornerRadius(4d))); 90 | 91 | protected override DependencyObject GetContainerForItemOverride() 92 | { 93 | return new TreeListViewItem(); 94 | } 95 | 96 | protected override bool IsItemItsOwnContainerOverride(object item) 97 | { 98 | return item is TreeListViewItem; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeListView/TreeListViewItem.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Wpf.Ui.Controls; 4 | 5 | public class TreeListViewItem : TreeViewItem 6 | { 7 | private int _level = -1; 8 | 9 | /// 10 | /// Item's hierarchy in the tree 11 | /// 12 | public int Level 13 | { 14 | get 15 | { 16 | if (_level == -1) 17 | { 18 | _level = (ItemsControlFromItemContainer(this) is TreeListViewItem parent) ? parent.Level + 1 : 0; 19 | } 20 | return _level; 21 | } 22 | } 23 | 24 | protected override DependencyObject GetContainerForItemOverride() 25 | { 26 | return new TreeListViewItem(); 27 | } 28 | 29 | protected override bool IsItemItsOwnContainerOverride(object item) 30 | { 31 | return item is TreeListViewItem; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeListView/TreeRowExpander.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace Wpf.Ui.Controls; 5 | 6 | public class TreeRowExpander : ContentControl 7 | { 8 | static TreeRowExpander() 9 | { 10 | DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeRowExpander), new FrameworkPropertyMetadata(typeof(TreeRowExpander))); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeModelListView/ITreeModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace Wpf.Ui.Controls; 4 | 5 | public interface ITreeModel 6 | { 7 | /// 8 | /// Get list of children of the specified parent 9 | /// 10 | public IEnumerable? GetChildren(object parent); 11 | 12 | /// 13 | /// returns wheather specified parent has any children or not. 14 | /// 15 | public bool HasChildren(object parent); 16 | } 17 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeModelListView/TreeModelCanExpandConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace Wpf.Ui.Controls; 7 | 8 | [ValueConversion(typeof(bool), typeof(Visibility))] 9 | internal sealed class TreeModelCanExpandConverter : IValueConverter 10 | { 11 | public bool IsInverted { get; set; } = false; 12 | 13 | public object Convert(object o, Type type, object parameter, CultureInfo culture) 14 | { 15 | return ((bool)o ^ IsInverted) ? Visibility.Visible : Visibility.Hidden; 16 | } 17 | 18 | public object ConvertBack(object o, Type type, object parameter, CultureInfo culture) 19 | { 20 | throw new NotSupportedException(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeModelListView/TreeModelCollection{T}.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | 5 | namespace Wpf.Ui.Controls; 6 | 7 | public class TreeModelCollection : IEnumerable, IList, ICollection, ITreeModel where T : TreeModelObject, new() 8 | { 9 | public virtual T Root { get; set; } = new T(); 10 | 11 | public virtual ObservableCollection Children 12 | { 13 | get => Root.Children; 14 | set => Root.Children = value; 15 | } 16 | 17 | public virtual int Count => Root.Children.Count; 18 | public virtual bool IsReadOnly => false; 19 | 20 | public T this[int index] 21 | { 22 | get => Root.Children[index]; 23 | set => Root.Children[index] = value; 24 | } 25 | 26 | public virtual void Add(T item) 27 | { 28 | Root.Children.Add(item); 29 | } 30 | 31 | public virtual void Clear() 32 | { 33 | Root.Children.Clear(); 34 | } 35 | 36 | public virtual bool Contains(T item) 37 | { 38 | return Root.Children.Contains(item); 39 | } 40 | 41 | public virtual void CopyTo(T[] array, int arrayIndex) 42 | { 43 | Root.Children.CopyTo(array, arrayIndex); 44 | } 45 | 46 | public virtual bool Remove(T item) 47 | { 48 | return Root.Children.Remove(item); 49 | } 50 | 51 | public virtual void RemoveAt(int index) 52 | { 53 | if (IsReadOnly) 54 | { 55 | return; 56 | } 57 | 58 | if ((uint)index >= (uint)Count) 59 | { 60 | return; 61 | } 62 | 63 | Root.Children.RemoveAt(index); 64 | } 65 | 66 | public virtual int IndexOf(T item) 67 | { 68 | return Root.Children.IndexOf(item); 69 | } 70 | 71 | public virtual void Insert(int index, T item) 72 | { 73 | Root.Children.Insert(index, item); 74 | } 75 | 76 | public virtual IEnumerator GetEnumerator() 77 | { 78 | return Root.Children.GetEnumerator(); 79 | } 80 | 81 | IEnumerator IEnumerable.GetEnumerator() 82 | { 83 | return Root.Children.GetEnumerator(); 84 | } 85 | 86 | public virtual IEnumerable? GetChildren(object parent) 87 | { 88 | parent ??= Root; 89 | 90 | if (parent is T { } root) 91 | { 92 | return root.Children; 93 | } 94 | return null; 95 | } 96 | 97 | public virtual bool HasChildren(object parent) 98 | { 99 | if (parent is T { } root) 100 | { 101 | return root.Children.Count > 0; 102 | } 103 | return false; 104 | } 105 | 106 | public virtual void AddChild(T child) 107 | { 108 | Root.Children.Add(child); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeModelListView/TreeModelLevelToIndentConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace Wpf.Ui.Controls; 7 | 8 | [ValueConversion(typeof(int), typeof(Thickness))] 9 | internal sealed class TreeModelLevelToIndentConverter : IValueConverter 10 | { 11 | private const double IndentSize = 19d; 12 | 13 | public object? Convert(object? value, Type type, object? parameter, CultureInfo culture) 14 | { 15 | return new Thickness((int)value! * IndentSize, 0d, 0d, 0d); 16 | } 17 | 18 | public object? ConvertBack(object value, Type type, object? parameter, CultureInfo culture) 19 | { 20 | throw new NotSupportedException(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeModelListView/TreeModelListViewItem.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows.Controls; 3 | using System.Windows.Input; 4 | 5 | namespace Wpf.Ui.Controls; 6 | 7 | public class TreeModelListViewItem : ListViewItem, INotifyPropertyChanged 8 | { 9 | private TreeModelNode? _node; 10 | 11 | public TreeModelNode? Node 12 | { 13 | get => _node; 14 | internal set 15 | { 16 | _node = value; 17 | OnPropertyChanged(nameof(Node)); 18 | } 19 | } 20 | 21 | public TreeModelListViewItem() 22 | { 23 | } 24 | 25 | protected override void OnKeyDown(KeyEventArgs e) 26 | { 27 | if (Node != null) 28 | { 29 | switch (e.Key) 30 | { 31 | case Key.Right: 32 | e.Handled = true; 33 | if (!Node.IsExpanded) 34 | { 35 | Node.IsExpanded = true; 36 | ChangeFocus(Node); 37 | } 38 | else if (Node.Children.Count > 0) 39 | { 40 | ChangeFocus(Node.Children[0]); 41 | } 42 | 43 | break; 44 | 45 | case Key.Left: 46 | 47 | e.Handled = true; 48 | if (Node.IsExpanded && Node.IsExpandable) 49 | { 50 | Node.IsExpanded = false; 51 | ChangeFocus(Node); 52 | } 53 | else 54 | { 55 | ChangeFocus(Node.Parent); 56 | } 57 | 58 | break; 59 | 60 | case Key.Subtract: 61 | e.Handled = true; 62 | Node.IsExpanded = false; 63 | ChangeFocus(Node); 64 | break; 65 | 66 | case Key.Add: 67 | e.Handled = true; 68 | Node.IsExpanded = true; 69 | ChangeFocus(Node); 70 | break; 71 | } 72 | } 73 | 74 | if (!e.Handled) 75 | { 76 | base.OnKeyDown(e); 77 | } 78 | } 79 | 80 | private void ChangeFocus(TreeModelNode? node) 81 | { 82 | if (node?.Tree is TreeModelListView { } tree) 83 | { 84 | if (tree.ItemContainerGenerator.ContainerFromItem(node) is TreeModelListViewItem item) 85 | { 86 | item.Focus(); 87 | } 88 | else 89 | { 90 | tree.PendingFocusNode = node; 91 | } 92 | } 93 | } 94 | 95 | public event PropertyChangedEventHandler? PropertyChanged; 96 | 97 | private void OnPropertyChanged(string name) 98 | { 99 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeModelListView/TreeModelObject{T}.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace Wpf.Ui.Controls; 4 | 5 | public class TreeModelObject where T : new() 6 | { 7 | public virtual ObservableCollection Children { get; set; } = []; 8 | } 9 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeModelListView/TreeModelRowCollection{T}.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Collections.Specialized; 4 | using System.ComponentModel; 5 | 6 | namespace Wpf.Ui.Controls; 7 | 8 | public class TreeModelRowCollection : ObservableCollection 9 | { 10 | public void RemoveRange(int index, int count) 11 | { 12 | CheckReentrancy(); 13 | if (Items is List { } items) 14 | { 15 | items.RemoveRange(index, count); 16 | } 17 | OnReset(); 18 | } 19 | 20 | public void InsertRange(int index, IEnumerable collection) 21 | { 22 | CheckReentrancy(); 23 | if (Items is List { } items) 24 | { 25 | items.InsertRange(index, collection); 26 | } 27 | OnReset(); 28 | } 29 | 30 | /// 31 | /// 32 | /// 33 | /// 34 | private void OnReset() 35 | { 36 | OnPropertyChanged(nameof(Count)); 37 | OnPropertyChanged("Item[]"); 38 | OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 39 | } 40 | 41 | private void OnPropertyChanged(string propertyName) 42 | { 43 | OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Controls/TreeModelListView/TreeModelRowExpander.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace Wpf.Ui.Controls; 5 | 6 | public class TreeModelRowExpander : ContentControl 7 | { 8 | static TreeModelRowExpander() 9 | { 10 | DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeModelRowExpander), new FrameworkPropertyMetadata(typeof(TreeModelRowExpander))); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Converters/FileSizeStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace Wpf.Ui.Violeta.Converters; 6 | 7 | [ValueConversion(typeof(IConvertible), typeof(string))] 8 | public class FileSizeStringConverter : IValueConverter 9 | { 10 | public static FileSizeStringConverter Instance { get; } = new(); 11 | 12 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) 13 | { 14 | if (value is IConvertible convertibleValue) 15 | { 16 | try 17 | { 18 | long lengthOfDocument = System.Convert.ToInt64(convertibleValue); 19 | return ToFileSizeString(lengthOfDocument); 20 | } 21 | catch (FormatException) 22 | { 23 | if (parameter is string fallback) 24 | { 25 | return fallback; 26 | } 27 | return "Invalid Size"; 28 | } 29 | } 30 | return null; 31 | } 32 | 33 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) 34 | { 35 | throw new NotImplementedException(); 36 | } 37 | 38 | public static string ToFileSizeString(long lengthOfDocument) 39 | { 40 | if (lengthOfDocument < 1024L) 41 | { 42 | return $"{lengthOfDocument} B"; 43 | } 44 | else if (lengthOfDocument < Math.Pow(1024d, 2d)) 45 | { 46 | return $"{lengthOfDocument / 1024d:F3} KB"; 47 | } 48 | else if (lengthOfDocument < Math.Pow(1024d, 3d)) 49 | { 50 | return $"{lengthOfDocument / Math.Pow(1024d, 2d):F3} MB"; 51 | } 52 | else if (lengthOfDocument < Math.Pow(1024d, 4d)) 53 | { 54 | return $"{lengthOfDocument / Math.Pow(1024d, 3d):F3} GB"; 55 | } 56 | else if (lengthOfDocument < Math.Pow(1024d, 5d)) 57 | { 58 | return $"{lengthOfDocument / Math.Pow(1024d, 4d):F3} TB"; 59 | } 60 | else if (lengthOfDocument < Math.Pow(1024d, 6d)) 61 | { 62 | return $"{lengthOfDocument / Math.Pow(1024d, 5d):F3} PB"; 63 | } 64 | else if (lengthOfDocument < Math.Pow(1024d, 7d)) 65 | { 66 | return $"{lengthOfDocument / Math.Pow(1024d, 6d):F3} EB"; 67 | } 68 | else if (lengthOfDocument < Math.Pow(1024d, 8d)) 69 | { 70 | return $"{lengthOfDocument / Math.Pow(1024d, 7d):F3} ZB"; 71 | } 72 | else 73 | { 74 | return $"{lengthOfDocument / Math.Pow(1024d, 8d):F3} YB"; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Converters/PathToIconConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.IO; 4 | using System.Windows.Data; 5 | using System.Windows.Media; 6 | using Wpf.Ui.Violeta.Win32; 7 | 8 | namespace Wpf.Ui.Violeta.Converters; 9 | 10 | [ValueConversion(typeof(string), typeof(ImageSource))] 11 | public class PathToIconConverter : IValueConverter 12 | { 13 | public static PathToIconConverter Instance { get; } = new(); 14 | 15 | public bool Large { get; set; } = false; 16 | 17 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) 18 | { 19 | if (value is string path) 20 | { 21 | if (Directory.Exists(path)) 22 | { 23 | return IconManager.FindIconForDir(Large); 24 | } 25 | else 26 | { 27 | return IconManager.FindIconForFilename(path, Large); 28 | } 29 | } 30 | 31 | return null; 32 | } 33 | 34 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | [assembly: SuppressMessage("Style", "IDE0130:Namespace does not match folder structure")] 4 | [assembly: SuppressMessage("Interoperability", "SYSLIB1054:Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time")] 5 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Markup/ControlsDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Markup; 4 | 5 | namespace Wpf.Ui.Violeta.Markup; 6 | 7 | /// 8 | /// Provides a dictionary implementation that contains WPF UI controls resources used by components and other elements of a WPF application. 9 | /// 10 | /// 11 | /// 12 | /// <Application 13 | /// xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"> 14 | /// <Application.Resources> 15 | /// <ResourceDictionary> 16 | /// <ResourceDictionary.MergedDictionaries> 17 | /// <ui:ControlsDictionary /> 18 | /// </ResourceDictionary.MergedDictionaries> 19 | /// </ResourceDictionary> 20 | /// </Application.Resources> 21 | /// </Application> 22 | /// 23 | /// 24 | [Localizability(LocalizationCategory.Ignore)] 25 | [Ambient] 26 | [UsableDuringInitialization(true)] 27 | public class ControlsDictionary : ResourceDictionary 28 | { 29 | private const string DictionaryUri = "pack://application:,,,/Wpf.Ui.Violeta;component/Resources/Wpf.Ui.xaml"; 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// Default constructor defining of the WPF UI controls dictionary. 34 | /// 35 | public ControlsDictionary() 36 | { 37 | Source = new Uri(DictionaryUri, UriKind.Absolute); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Markup/StaticResourceExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Markup; 2 | 3 | public class StaticResourceExtension : System.Windows.StaticResourceExtension 4 | { 5 | public StaticResourceExtension() 6 | { 7 | } 8 | 9 | public StaticResourceExtension(object resourceKey) : base(resourceKey) 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Markup/ThemesDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Markup; 4 | using Wpf.Ui.Appearance; 5 | using Wpf.Ui.Violeta.Appearance; 6 | 7 | namespace Wpf.Ui.Violeta.Markup; 8 | 9 | [Localizability(LocalizationCategory.Ignore)] 10 | [Ambient] 11 | [UsableDuringInitialization(true)] 12 | public class ThemesDictionary : ResourceDictionary 13 | { 14 | public ApplicationTheme Theme 15 | { 16 | set => SetSourceBasedOnSelectedTheme(value); 17 | } 18 | 19 | static ThemesDictionary() 20 | { 21 | ThemeManager.RegisterApplicationThemeChanged(); 22 | } 23 | 24 | public ThemesDictionary() 25 | { 26 | SetSourceBasedOnSelectedTheme(ApplicationTheme.Light); 27 | } 28 | 29 | private void SetSourceBasedOnSelectedTheme(ApplicationTheme? selectedApplicationTheme) 30 | { 31 | var themeName = selectedApplicationTheme switch 32 | { 33 | ApplicationTheme.Dark => "Dark", 34 | //ApplicationTheme.HighContrast => "HighContrast", 35 | _ => "Light" 36 | }; 37 | 38 | Source = new Uri($"pack://application:,,,/Wpf.Ui.Violeta;component/Resources/Theme/{themeName}.xaml", UriKind.Absolute); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/Fonts/Segoe Fluent Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/src/Wpf.Ui.Violeta/Resources/Fonts/Segoe Fluent Icons.ttf -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/Fonts/Violeta Fluent Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/src/Wpf.Ui.Violeta/Resources/Fonts/Violeta Fluent Icons.ttf -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/Images/background-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/src/Wpf.Ui.Violeta/Resources/Images/background-b.png -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/Images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emako/wpfui.violeta/75b03bfa4615e0cddf133952c97be5db52596382/src/Wpf.Ui.Violeta/Resources/Images/background.png -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/ManifestResourceProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | namespace Wpf.Ui.Violeta.Resources; 7 | 8 | public static class ManifestResourceProvider 9 | { 10 | public static bool HasResource(string name, Assembly? assembly = null) 11 | { 12 | if (assembly == null) 13 | { 14 | StackTrace stackTrace = new(1); 15 | StackFrame? stackFrame = stackTrace.GetFrame(0); 16 | MethodBase? methodBase = stackFrame?.GetMethod()!; 17 | 18 | assembly = methodBase?.DeclaringType?.Assembly!; 19 | } 20 | 21 | return assembly.GetManifestResourceInfo(name) != null; 22 | } 23 | 24 | public static Stream GetStream(string name, Assembly? assembly = null) 25 | { 26 | if (assembly == null) 27 | { 28 | StackTrace stackTrace = new(1); 29 | StackFrame? stackFrame = stackTrace.GetFrame(0); 30 | MethodBase? methodBase = stackFrame?.GetMethod()!; 31 | 32 | assembly = methodBase?.DeclaringType?.Assembly!; 33 | } 34 | 35 | Stream stream = assembly.GetManifestResourceStream(name)!; 36 | return stream; 37 | } 38 | 39 | public static string GetString(string name, Encoding? encoding = null, Assembly? assembly = null) 40 | { 41 | if (assembly == null) 42 | { 43 | StackTrace stackTrace = new(1); 44 | StackFrame? stackFrame = stackTrace.GetFrame(0); 45 | MethodBase? methodBase = stackFrame?.GetMethod()!; 46 | 47 | assembly = methodBase?.DeclaringType?.Assembly!; 48 | } 49 | 50 | using Stream stream = GetStream(name, assembly); 51 | using StreamReader streamReader = new(stream, encoding ?? Encoding.UTF8); 52 | return streamReader.ReadToEnd(); 53 | } 54 | 55 | public static byte[] GetBytes(string name, Assembly? assembly = null) 56 | { 57 | if (assembly == null) 58 | { 59 | StackTrace stackTrace = new(1); 60 | StackFrame? stackFrame = stackTrace.GetFrame(0); 61 | MethodBase? methodBase = stackFrame?.GetMethod()!; 62 | 63 | assembly = methodBase?.DeclaringType?.Assembly!; 64 | } 65 | 66 | using Stream stream = GetStream(name, assembly); 67 | using BinaryReader reader = new(stream); 68 | return reader.ReadBytes((int)stream.Length); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/ResourcesProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Packaging; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Resources; 7 | 8 | namespace Wpf.Ui.Violeta.Resources; 9 | 10 | public static class ResourcesProvider 11 | { 12 | static ResourcesProvider() 13 | { 14 | if (!UriParser.IsKnownScheme("pack")) 15 | { 16 | _ = PackUriHelper.UriSchemePack; 17 | } 18 | } 19 | 20 | /// 21 | /// Low performance method to check if a resource exists. 22 | /// 23 | /// 24 | /// 25 | public static bool HasResource(Uri uri) 26 | { 27 | try 28 | { 29 | StreamResourceInfo info = Application.GetResourceStream(uri); 30 | using Stream? stream = info?.Stream; 31 | _ = stream; 32 | return true; 33 | } 34 | catch 35 | { 36 | } 37 | return false; 38 | } 39 | 40 | public static bool HasResource(string uriString) 41 | { 42 | return HasResource(new Uri(uriString)); 43 | } 44 | 45 | public static Stream GetStream(Uri uri) 46 | { 47 | StreamResourceInfo info = Application.GetResourceStream(uri); 48 | return info?.Stream!; 49 | } 50 | 51 | public static Stream GetStream(string uriString) 52 | { 53 | return GetStream(new Uri(uriString)); 54 | } 55 | 56 | public static Stream? TryFindStream(Uri uri) 57 | { 58 | try 59 | { 60 | return GetStream(uri); 61 | } 62 | catch 63 | { 64 | } 65 | return null; 66 | } 67 | 68 | public static Stream? TryFindStream(string uriString) 69 | { 70 | return TryFindStream(new Uri(uriString)); 71 | } 72 | 73 | public static bool TryGetStream(Uri uri, out Stream? stream) 74 | { 75 | try 76 | { 77 | stream = GetStream(uri); 78 | return true; 79 | } 80 | catch 81 | { 82 | } 83 | stream = null; 84 | return false; 85 | } 86 | 87 | public static bool TryGetStream(string uriString, out Stream? stream) 88 | { 89 | return TryGetStream(new Uri(uriString), out stream); 90 | } 91 | 92 | public static string GetString(Uri uri, Encoding? encoding = null) 93 | { 94 | using Stream stream = GetStream(uri); 95 | using StreamReader streamReader = new(stream, encoding ?? Encoding.UTF8); 96 | return streamReader.ReadToEnd(); 97 | } 98 | 99 | public static string GetString(string uriString, Encoding? encoding = null) 100 | { 101 | return GetString(new Uri(uriString), encoding); 102 | } 103 | 104 | public static byte[] GetBytes(Uri uri) 105 | { 106 | using Stream stream = GetStream(uri); 107 | using BinaryReader streamReader = new(stream); 108 | return streamReader.ReadBytes((int)stream.Length); 109 | } 110 | 111 | public static byte[] GetBytes(string uriString) 112 | { 113 | return GetBytes(new Uri(uriString)); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/Theme/Dark.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | pack://application:,,,/Wpf.Ui.Violeta;component/Resources/Images/background-b.png 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/Theme/Light.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | pack://application:,,,/Wpf.Ui.Violeta;component/Resources/Images/background.png 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/Variables.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 0,0,0,1 8 | 9 | 00:00:00.250 10 | 00:00:00.167 11 | 00:00:00.168 12 | 00:00:00.083 13 | 14 | 1 15 | 16 | 320 17 | 548 18 | 184 19 | 756 20 | 21 | 8 22 | 0,0,0,12 23 | 24 24 | 0,0,0,1 25 | 26 | 130 27 | 202 28 | 32 29 | 32 30 | 56 31 | 32 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Resources/Wpf.Ui.xaml: -------------------------------------------------------------------------------- 1 |  2 | pack://application:,,,/Wpf.Ui.Violeta;component/Resources/Fonts/Segoe Fluent Icons.ttf#Segoe Fluent Icons 3 | pack://application:,,,/Wpf.Ui.Violeta;component/Resources/Fonts/Violeta Fluent Icons.ttf#Violeta Fluent Icons 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Threading/ApplicationDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | 4 | namespace Wpf.Ui.Violeta.Threading; 5 | 6 | public static class ApplicationDispatcher 7 | { 8 | public static Window MainWindow => Application.Current.Dispatcher.Invoke(() => Application.Current.MainWindow); 9 | 10 | public static void Invoke(Action callback, params object[] args) 11 | { 12 | _ = Application.Current?.Dispatcher.Invoke(callback, args); 13 | } 14 | 15 | public static void Invoke(Action callback) 16 | { 17 | _ = Application.Current?.Dispatcher.Invoke(callback, MainWindow); 18 | } 19 | 20 | public static T Invoke(Func action) 21 | { 22 | T t = default!; 23 | Application.Current?.Dispatcher.Invoke(() => t = action()); 24 | return t; 25 | } 26 | 27 | public static void BeginInvoke(Action callback, params object[] args) 28 | { 29 | _ = Application.Current?.Dispatcher.BeginInvoke(callback, args); 30 | } 31 | 32 | public static void BeginInvoke(Action callback) 33 | { 34 | _ = Application.Current?.Dispatcher.BeginInvoke(callback, MainWindow); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Threading/STAThread.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Threading; 4 | using System.Windows.Threading; 5 | 6 | namespace Wpf.Ui.Violeta.Threading; 7 | 8 | public sealed class STAThread(Action> start) : STAThread(start); 9 | 10 | public class STAThread : STADispatcherObject, IDisposable where T : class 11 | { 12 | public Thread Value { get; set; } = null!; 13 | public T Result { get; set; } = default!; 14 | 15 | public STAThread(Action> start) 16 | { 17 | Value = new(() => 18 | { 19 | Dispatcher = Dispatcher.CurrentDispatcher; 20 | start?.Invoke(this); 21 | Dispatcher.Run(); 22 | }) 23 | { 24 | IsBackground = true, 25 | Name = $"STAThread<{typeof(T)}>", 26 | }; 27 | Value.SetApartmentState(ApartmentState.STA); 28 | } 29 | 30 | public void Start() 31 | { 32 | Value?.Start(); 33 | } 34 | 35 | public void Forget() 36 | { 37 | Dispose(); 38 | } 39 | 40 | [SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize")] 41 | [SuppressMessage("CodeQuality", "IDE0079:Unnecessary SuppressMessage attribute suppression")] 42 | public void Dispose() 43 | { 44 | if (typeof(IDisposable).IsAssignableFrom(typeof(T))) 45 | { 46 | try 47 | { 48 | ((IDisposable?)Result)?.Dispose(); 49 | } 50 | catch 51 | { 52 | /// 53 | } 54 | } 55 | Dispatcher?.InvokeShutdown(); 56 | } 57 | } 58 | 59 | public class STADispatcherObject(Dispatcher dispatcher = null!) 60 | { 61 | public Dispatcher Dispatcher 62 | { 63 | get => dispatcher; 64 | set => dispatcher = value; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/DpiAware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Media; 3 | 4 | namespace Wpf.Ui.Violeta.Win32; 5 | 6 | public static class DpiAware 7 | { 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 0 13 | /// 1 14 | /// 2 (default) 15 | /// 16 | /// 17 | public static bool SetProcessDpiAwareness(int awareness = (int)SHCore.PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE) 18 | { 19 | if (NTdll.RtlGetVersion(out NTdll.OSVERSIONINFOEX osv) == NTdll.NTStatus.STATUS_SUCCESS) 20 | { 21 | Version osVersion = new(osv.MajorVersion, osv.MinorVersion, osv.BuildNumber, osv.PlatformId); 22 | 23 | if (Environment.OSVersion.Platform == PlatformID.Win32NT && osVersion >= new Version(6, 3)) 24 | { 25 | if (SHCore.SetProcessDpiAwareness((SHCore.PROCESS_DPI_AWARENESS)awareness) == 0) 26 | { 27 | return true; 28 | } 29 | } 30 | } 31 | return false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/DpiHelper.cs: -------------------------------------------------------------------------------- 1 | namespace Wpf.Ui.Violeta.Win32; 2 | 3 | internal static class DpiHelper 4 | { 5 | public static float ScaleX => GetScale().X; 6 | public static float ScaleY => GetScale().Y; 7 | 8 | private static (float X, float Y) GetScale() 9 | { 10 | nint hdc = User32.GetDC(0); 11 | float scaleX = Gdi32.GetDeviceCaps(hdc, Gdi32.DeviceCap.LOGPIXELSX); 12 | float scaleY = Gdi32.GetDeviceCaps(hdc, Gdi32.DeviceCap.LOGPIXELSY); 13 | _ = User32.ReleaseDC(0, hdc); 14 | return new(scaleX / 96f, scaleY / 96f); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/Gdi32.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Wpf.Ui.Violeta.Win32; 4 | 5 | internal static class Gdi32 6 | { 7 | [DllImport("gdi32.dll", SetLastError = false, ExactSpelling = true)] 8 | public static extern int GetDeviceCaps(nint hdc, DeviceCap nIndex); 9 | 10 | public enum DeviceCap 11 | { 12 | LOGPIXELSX = 88, 13 | LOGPIXELSY = 90, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/IconManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Windows; 7 | using System.Windows.Interop; 8 | using System.Windows.Media; 9 | using System.Windows.Media.Imaging; 10 | 11 | namespace Wpf.Ui.Violeta.Win32; 12 | 13 | public static class IconManager 14 | { 15 | private static ImageSource SmallDirIcon = null!; 16 | private static ImageSource LargeDirIcon = null!; 17 | private static readonly Dictionary SmallIconCache = []; 18 | private static readonly Dictionary LargeIconCache = []; 19 | 20 | /// 21 | /// May [".exe", ".png", ".jpg", ".jpeg", ".bmp", ".gif", ".tiff", ".ico"] 22 | /// 23 | public static string[] CacheExcludeExtensions { get; set; } = null!; 24 | 25 | public static void ClearCache() 26 | { 27 | SmallDirIcon = LargeDirIcon = null!; 28 | 29 | SmallIconCache.Clear(); 30 | LargeIconCache.Clear(); 31 | } 32 | 33 | /// 34 | /// Get the icon of a directory 35 | /// 36 | /// 16x16 or 32x32 icon 37 | /// an icon 38 | public static ImageSource FindIconForDir(bool large = false) 39 | { 40 | ImageSource icon = large ? LargeDirIcon : SmallDirIcon; 41 | 42 | if (icon != null) 43 | { 44 | return icon; 45 | } 46 | 47 | nint hIcon = IconReader.GetFolderIcon(large ? IconReader.IconSize.Large : IconReader.IconSize.Small, false); 48 | 49 | icon = hIcon.ToImageSource(); 50 | _ = User32.DestroyIcon(hIcon); 51 | 52 | if (large) 53 | { 54 | LargeDirIcon = icon; 55 | } 56 | else 57 | { 58 | SmallDirIcon = icon; 59 | } 60 | 61 | return icon; 62 | } 63 | 64 | /// 65 | /// Get an icon for a given filename 66 | /// 67 | /// any filename 68 | /// 16x16 or 32x32 icon 69 | /// null if path is null, otherwise - an icon 70 | public static ImageSource FindIconForFilename(string fileName, bool large = false) 71 | { 72 | string extension = Path.GetExtension(fileName); 73 | if (extension == null) 74 | { 75 | return null!; 76 | } 77 | 78 | bool needCache = !(CacheExcludeExtensions?.Contains(extension) ?? false); 79 | 80 | Dictionary cache = large ? LargeIconCache : SmallIconCache; 81 | if (needCache && cache.TryGetValue(extension, out ImageSource? icon)) 82 | { 83 | return icon; 84 | } 85 | 86 | nint hIcon = IconReader.GetFileIcon(fileName, large ? IconReader.IconSize.Large : IconReader.IconSize.Small, false); 87 | 88 | icon = hIcon.ToImageSource(); 89 | _ = User32.DestroyIcon(hIcon); 90 | 91 | if (needCache) 92 | { 93 | cache.Add(extension, icon); 94 | } 95 | 96 | return icon; 97 | } 98 | 99 | private static ImageSource ToImageSource(this nint icon) 100 | { 101 | ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(icon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); 102 | return imageSource; 103 | } 104 | 105 | /// 106 | /// Provides static methods to read system icons for both folders and files. 107 | /// 108 | private static class IconReader 109 | { 110 | /// 111 | /// Options to specify the size of icons to return. 112 | /// 113 | public enum IconSize 114 | { 115 | /// 116 | /// Specify large icon - 32 pixels by 32 pixels. 117 | /// 118 | Large = 0, 119 | 120 | /// 121 | /// Specify small icon - 16 pixels by 16 pixels. 122 | /// 123 | Small = 1 124 | } 125 | 126 | /// 127 | /// Returns the icon of a folder. 128 | /// 129 | /// Large or small 130 | /// Whether to include the link icon 131 | /// hIcon 132 | public static nint GetFolderIcon(IconSize size, bool linkOverlay) 133 | { 134 | Shell32.Shfileinfo shfi = new(); 135 | uint flags = Shell32.ShgfiIcon | Shell32.ShgfiUsefileattributes; 136 | 137 | if (linkOverlay) 138 | { 139 | flags |= Shell32.ShgfiLinkoverlay; 140 | } 141 | 142 | if (IconSize.Small == size) 143 | { 144 | flags |= Shell32.ShgfiSmallicon; 145 | } 146 | else 147 | { 148 | flags |= Shell32.ShgfiLargeicon; 149 | } 150 | 151 | _ = Shell32.SHGetFileInfo("placeholder", Shell32.FileAttributeDirectory, ref shfi, (uint)Marshal.SizeOf(shfi), flags); 152 | return shfi.hIcon; 153 | } 154 | 155 | /// 156 | /// Returns an icon for a given file - indicated by the name parameter. 157 | /// 158 | /// Pathname for file. 159 | /// Large or small 160 | /// Whether to include the link icon 161 | /// hIcon 162 | public static nint GetFileIcon(string name, IconSize size, bool linkOverlay) 163 | { 164 | Shell32.Shfileinfo shfi = new(); 165 | uint flags = Shell32.ShgfiIcon | Shell32.ShgfiUsefileattributes; 166 | 167 | if (linkOverlay) 168 | { 169 | flags |= Shell32.ShgfiLinkoverlay; 170 | } 171 | 172 | if (IconSize.Small == size) 173 | { 174 | flags |= Shell32.ShgfiSmallicon; 175 | } 176 | else 177 | { 178 | flags |= Shell32.ShgfiLargeicon; 179 | } 180 | 181 | _ = Shell32.SHGetFileInfo(name, Shell32.FileAttributeNormal, ref shfi, (uint)Marshal.SizeOf(shfi), flags); 182 | return shfi.hIcon; 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/NTdll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Security; 4 | 5 | namespace Wpf.Ui.Violeta.Win32; 6 | 7 | internal static class NTdll 8 | { 9 | [SecurityCritical] 10 | [DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Unicode)] 11 | [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] 12 | public static extern int RtlGetVersion(out OSVERSIONINFOEX versionInfo); 13 | 14 | public static Version GetOSVersion() 15 | { 16 | if (RtlGetVersion(out OSVERSIONINFOEX osv) == 0) 17 | { 18 | return new Version(osv.MajorVersion, osv.MinorVersion, osv.BuildNumber, osv.PlatformId); 19 | } 20 | 21 | return Environment.OSVersion.Version; 22 | } 23 | 24 | [StructLayout(LayoutKind.Sequential)] 25 | public struct OSVERSIONINFOEX 26 | { 27 | public int OSVersionInfoSize; 28 | public int MajorVersion; 29 | public int MinorVersion; 30 | public int BuildNumber; 31 | public int PlatformId; 32 | 33 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 34 | public string CSDVersion; 35 | 36 | public ushort ServicePackMajor; 37 | public ushort ServicePackMinor; 38 | public short SuiteMask; 39 | public byte ProductType; 40 | public byte Reserved; 41 | } 42 | 43 | public static class NTStatus 44 | { 45 | public const int STATUS_SUCCESS = 0; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/SHCore.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Wpf.Ui.Violeta.Win32; 4 | 5 | internal static class SHCore 6 | { 7 | [DllImport("shcore.dll")] 8 | public static extern uint SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness); 9 | 10 | [DllImport("shcore.dll")] 11 | public static extern int GetDpiForMonitor(nint hMonitor, MONITOR_DPI_TYPE dpiType, out uint dpiX, out uint dpiY); 12 | 13 | public enum PROCESS_DPI_AWARENESS 14 | { 15 | PROCESS_DPI_UNAWARE, 16 | PROCESS_SYSTEM_DPI_AWARE, 17 | PROCESS_PER_MONITOR_DPI_AWARE, 18 | } 19 | 20 | public enum MONITOR_DPI_TYPE 21 | { 22 | MDT_EFFECTIVE_DPI = 0, 23 | MDT_ANGULAR_DPI, 24 | MDT_RAW_DPI, 25 | MDT_DEFAULT = MDT_EFFECTIVE_DPI, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/Shell32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Wpf.Ui.Violeta.Win32; 5 | 6 | internal static class Shell32 7 | { 8 | private const int MaxPath = 256; 9 | public const uint ShgfiIcon = 0x000000100; // get icon 10 | public const uint ShgfiLinkoverlay = 0x000008000; // put a link overlay on icon 11 | public const uint ShgfiLargeicon = 0x000000000; // get large icon 12 | public const uint ShgfiSmallicon = 0x000000001; // get small icon 13 | public const uint ShgfiUsefileattributes = 0x000000010; // use passed dwFileAttribute 14 | public const uint FileAttributeNormal = 0x00000080; 15 | public const uint FileAttributeDirectory = 0x00000010; 16 | 17 | [DllImport("shell32.dll")] 18 | public static extern nint SHGetFileInfo(string pszPath, uint dwFileAttributes, ref Shfileinfo psfi, uint cbFileInfo, uint uFlags); 19 | 20 | [DllImport("shell32.dll", CharSet = CharSet.Unicode)] 21 | public static extern bool Shell_NotifyIcon(int dwMessage, ref NotifyIconData pnid); 22 | 23 | [StructLayout(LayoutKind.Sequential)] 24 | public struct Shfileinfo 25 | { 26 | private const int Namesize = 80; 27 | public readonly IntPtr hIcon; 28 | private readonly int iIcon; 29 | private readonly uint dwAttributes; 30 | 31 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxPath)] 32 | private readonly string szDisplayName; 33 | 34 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = Namesize)] 35 | private readonly string szTypeName; 36 | } 37 | 38 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 39 | public struct NotifyIconData 40 | { 41 | public int cbSize; 42 | public nint hWnd; 43 | public int uID; 44 | public int uFlags; 45 | public int uCallbackMessage; 46 | public nint hIcon; 47 | 48 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] 49 | public string szTip; 50 | 51 | public uint dwState; 52 | public uint dwStateMask; 53 | 54 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 55 | public string szInfo; 56 | 57 | public uint uTimeoutOrVersion; 58 | 59 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] 60 | public string szInfoTitle; 61 | 62 | public uint dwInfoFlags; 63 | 64 | public Guid guidItem; 65 | 66 | public nint hBalloonIcon; 67 | } 68 | 69 | [Flags] 70 | public enum NotifyIconState : uint 71 | { 72 | NIS_HIDDEN = 0x1, 73 | NIS_SHAREDICON = 0x2 74 | } 75 | 76 | [Flags] 77 | public enum NotifyIconFlags : uint 78 | { 79 | NIF_MESSAGE = 0x00000001, 80 | NIF_ICON = 0x00000002, 81 | NIF_TIP = 0x00000004, 82 | NIF_STATE = 0x00000008, 83 | NIF_INFO = 0x00000010, 84 | NIF_GUID = 0x00000020, 85 | NIF_REALTIME = 0x00000040, 86 | NIF_SHOWTIP = 0x00000080 87 | } 88 | 89 | public enum NOTIFY_COMMAND : uint 90 | { 91 | NIM_ADD = 0x00000000, 92 | NIM_MODIFY = 0x00000001, 93 | NIM_DELETE = 0x00000002, 94 | NIM_SETVERSION = 0x00000004 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/Structs/POINT.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Wpf.Ui.Violeta.Win32; 4 | 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct POINT(int x, int y) 7 | { 8 | public int X = x; 9 | public int Y = y; 10 | 11 | public void Offset(int dx, int dy) 12 | { 13 | X += dx; 14 | Y += dy; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/Structs/RECT.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Wpf.Ui.Violeta.Win32; 4 | 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct RECT(int left, int top, int right, int bottom) 7 | { 8 | public int Left = left; 9 | public int Top = top; 10 | public int Right = right; 11 | public int Bottom = bottom; 12 | 13 | public int Width => Right - Left; 14 | 15 | public int Height => Bottom - Top; 16 | 17 | public void Offset(int dx, int dy) 18 | { 19 | Left += dx; 20 | Top += dy; 21 | Right += dx; 22 | Bottom += dy; 23 | } 24 | 25 | public bool IsEmpty => Left >= Right || Top >= Bottom; 26 | } 27 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Win32/UxTheme.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Wpf.Ui.Violeta.Win32; 5 | 6 | internal static class UxTheme 7 | { 8 | [DllImport("uxtheme.dll", EntryPoint = "#132", SetLastError = true, CharSet = CharSet.Unicode)] 9 | public static extern bool ShouldAppsUseDarkMode(); 10 | 11 | /// 12 | /// Windows 10 1903, aka 18362, broke the API. 13 | /// - Before 18362, the #135 is AllowDarkModeForApp(BOOL) 14 | /// - After 18362, the #135 is SetPreferredAppMode(PreferredAppMode) 15 | /// Since the support for AllowDarkModeForApp is uncertain, it will not be considered for use. 16 | /// 17 | [DllImport("uxtheme.dll", EntryPoint = "#135", SetLastError = true, CharSet = CharSet.Unicode)] 18 | public static extern int SetPreferredAppMode(PreferredAppMode preferredAppMode); 19 | 20 | [DllImport("uxtheme.dll", EntryPoint = "#135", SetLastError = true, CharSet = CharSet.Unicode)] 21 | [Obsolete("Since the support for AllowDarkModeForApp is uncertain, it will not be considered for use.")] 22 | public static extern void AllowDarkModeForApp(bool allowDark); 23 | 24 | [DllImport("uxtheme.dll", EntryPoint = "#136", SetLastError = true, CharSet = CharSet.Unicode)] 25 | public static extern void FlushMenuThemes(); 26 | 27 | [DllImport("uxtheme.dll", EntryPoint = "#138", SetLastError = true, CharSet = CharSet.Unicode)] 28 | public static extern bool ShouldSystemUseDarkMode(); 29 | 30 | public enum PreferredAppMode : int { Default, AllowDark, ForceDark, ForceLight, Max }; 31 | } 32 | -------------------------------------------------------------------------------- /src/Wpf.Ui.Violeta/Wpf.Ui.Violeta.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WPF-UI.Violeta 5 | WPF-UI.Violeta 6 | net462;net47;net471;net472;net48;net481;net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows; 7 | true 8 | false 9 | false 10 | latest 11 | enable 12 | true 13 | true 14 | true 15 | 4.0.2.3 16 | 4.0.2.3 17 | $(VersionPrefix)4.0.2.3 18 | ema 19 | ema 20 | WPF UI Violeta is based on WPF UI, and provides the Fluent experience in your known and loved WPF framework. Some new immersive controls like like `Toast`, `Flyout`, `ContentDialog`, `MessageBox` and etc. 21 | https://github.com/emako/wpfui.violeta 22 | https://github.com/emako/wpfui.violeta 23 | git 24 | WPF-UI .NET WPF Violeta 25 | MIT 26 | README.md 27 | wpfui.png 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ResXFileCodeGenerator 43 | SH.Designer.cs 44 | 45 | 46 | ResXFileCodeGenerator 47 | SH.en.Designer.cs 48 | 49 | 50 | ResXFileCodeGenerator 51 | SH.fr.Designer.cs 52 | 53 | 54 | ResXFileCodeGenerator 55 | SH.id.Designer.cs 56 | 57 | 58 | ResXFileCodeGenerator 59 | SH.ja.Designer.cs 60 | 61 | 62 | ResXFileCodeGenerator 63 | SH.ko.Designer.cs 64 | 65 | 66 | ResXFileCodeGenerator 67 | SH.pt.Designer.cs 68 | 69 | 70 | ResXFileCodeGenerator 71 | SH.ru.Designer.cs 72 | 73 | 74 | ResXFileCodeGenerator 75 | SH.vi.Designer.cs 76 | 77 | 78 | ResXFileCodeGenerator 79 | SH.zh-Hant.Designer.cs 80 | 81 | 82 | 83 | 84 | 85 | True 86 | True 87 | SH.resx 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | all 107 | build; analyzers 108 | 109 | 110 | all 111 | runtime; build; native; contentfiles; analyzers; buildtransitive 112 | 113 | 114 | all 115 | runtime; build; native; contentfiles; analyzers; buildtransitive 116 | 117 | 118 | 119 | 120 | --------------------------------------------------------------------------------