├── .XamlStyler ├── .editorconfig ├── .gitattributes ├── .github └── dependabot.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── .vsconfig ├── Directory.Packages.props ├── LICENSE.md ├── MvvmScarletToolkit.ruleset ├── MvvmScarletToolkit.sln ├── MvvmScarletToolkit.slnf ├── README.md ├── build.config ├── build.ps1 ├── cake ├── Build.csproj ├── Build.sln ├── BuildContext.cs ├── BuildLifetime.cs ├── Program.cs ├── Tasks │ ├── BuildAndPack.cs │ ├── CleanSolution.cs │ ├── CleanSolutionAgain.cs │ ├── CoberturaReport.cs │ ├── ConvertCoverage.cs │ ├── Default.cs │ ├── HtmlReport.cs │ ├── PushGithub.cs │ ├── PushLocally.cs │ ├── PushNuget.cs │ ├── Test.cs │ ├── TestAndUploadReport.cs │ ├── UpdateAssemblyInfo.cs │ └── UploadCodecovReport.cs └── Utils.cs ├── codecov.yml ├── global.json ├── nuget.config ├── src ├── Directory.Build.props ├── Directory.Build.targets ├── MvvmScarletToolkit.Abstractions │ ├── AbstractBuilder.cs │ ├── IAsyncCommand.cs │ ├── IBuilder.cs │ ├── IBusinessViewModelListBase.cs │ ├── IBusyStack.cs │ ├── ICancelCommand.cs │ ├── IChangeTracker.cs │ ├── IConcurrentCommand.cs │ ├── IExitService.cs │ ├── ILocalizationProvider.cs │ ├── ILocalizationService.cs │ ├── ILocalizationViewModel.cs │ ├── IObservableBusyStack.cs │ ├── IScarletCommandManager.cs │ ├── IScarletDispatcher.cs │ ├── IScarletEventManager.cs │ ├── IScarletExceptionHandler.cs │ ├── IToolkitChangeTracker.cs │ ├── IVirtualizationViewModel.cs │ ├── MvvmScarletToolkit.Abstractions.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ └── readme.md ├── MvvmScarletToolkit.Avalonia │ ├── Implementations │ │ ├── ScarletCommandBuilder.cs │ │ ├── ScarletCommandManager.cs │ │ ├── ScarletDispatcher.cs │ │ ├── ScarletExitService.cs │ │ ├── ScarletLocalizationProvider.cs │ │ ├── ScarletWeakEventManager.cs │ │ └── WeakEventManager.cs │ └── MvvmScarletToolkit.Avalonia.csproj ├── MvvmScarletToolkit.Commands │ ├── CommandBuilderContext.cs │ ├── CommandBuilderContextExtensions.cs │ ├── CommandBuilderExtensions.cs │ ├── Core │ │ ├── CancelCommand.cs │ │ ├── ConcurrentCancelCommand.cs │ │ ├── ConcurrentCommand.cs │ │ ├── ConcurrentCommandBase.cs │ │ ├── ConcurrentCommandDecoratorBase.cs │ │ ├── GenericConcurrentCommandBase.cs │ │ ├── IgnoreExceptionHandler.cs │ │ ├── NoCancellationCommand.cs │ │ ├── NotifyTaskCompletion.cs │ │ └── SequentialAsyncCommandDecorator.cs │ ├── IScarletCommandBuilder.cs │ ├── MvvmScarletToolkit.Commands.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ └── readme.md ├── MvvmScarletToolkit.Incubator │ ├── BoolDialogResultViewModel.cs │ ├── DialogResultViewModel.cs │ ├── DialogResultViewModel_generic.cs │ ├── ImageLoading │ │ └── SharedBitmapSource.cs │ ├── MvvmScarletToolkit.Incubator.csproj │ └── Properties │ │ └── AssemblyInfo.cs ├── MvvmScarletToolkit.Observables.Tests │ ├── AttributedBroadCastViewModel.cs │ ├── BroadCastViewModel.cs │ ├── ITestViewModel.cs │ ├── MvvmScarletToolkit.Observables.Tests.csproj │ ├── NotifyProperyChangedTrackerTests.cs │ ├── ObservableDictionaryTests.cs │ ├── PropertyChangedMessageTrackerTests.cs │ └── ViewModel.cs ├── MvvmScarletToolkit.Observables │ ├── DispatcherProgress.cs │ ├── Extensions │ │ └── ObservableCollectionExtensions.cs │ ├── IChange.cs │ ├── MvvmScarletToolkit.Observables.csproj │ ├── NotifyProperyChangedTracker.cs │ ├── ObservableCircularBuffer.cs │ ├── ObservableDictionary.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── PropertyChangedMessageTracker.cs │ ├── ViewModels │ │ ├── Base │ │ │ ├── BusinessViewModelBase.cs │ │ │ ├── BusinessViewModelBase_generic.cs │ │ │ ├── BusinessViewModelListBase.cs │ │ │ ├── DomainViewModelListBase.cs │ │ │ ├── Messages │ │ │ │ ├── ViewModelListBaseSelectionChanged.cs │ │ │ │ ├── ViewModelListBaseSelectionChanging.cs │ │ │ │ └── ViewModelListBaseSelectionsChanged.cs │ │ │ ├── Rx │ │ │ │ ├── BusinessSourceListViewModelBase.cs │ │ │ │ ├── INotifyRefreshRequired.cs │ │ │ │ ├── NotifyRefreshRequiredAdapter.cs │ │ │ │ ├── SourceListViewModelBase.cs │ │ │ │ └── VariableThresholdObservableCollectionAdaptor.cs │ │ │ ├── ViewModelBase.cs │ │ │ ├── ViewModelBase_generic.cs │ │ │ └── ViewModelListBase.cs │ │ ├── EnumViewModel.cs │ │ ├── EnumViewModel_generic.cs │ │ ├── Localization │ │ │ ├── LocalizationViewModel.cs │ │ │ ├── LocalizationsViewModel.cs │ │ │ ├── ServiceExtensions.cs │ │ │ └── samples.md │ │ ├── Navigation │ │ │ ├── Scene.cs │ │ │ ├── Scenes.cs │ │ │ └── samples.md │ │ ├── Paging │ │ │ ├── IPagedDataProvider.cs │ │ │ └── PagedSourceListViewModelBase.cs │ │ ├── PagingViewModel.cs │ │ ├── State │ │ │ ├── BusyStack.cs │ │ │ ├── BusyToken.cs │ │ │ ├── DisposalToken.cs │ │ │ ├── ObservableBusyStack.cs │ │ │ └── samples.md │ │ ├── VersionViewModel.cs │ │ └── ViewModelContainer.cs │ └── readme.md ├── MvvmScarletToolkit.Wpf.Samples │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── Controls │ │ ├── DragAndDrop.xaml │ │ └── DragAndDrop.xaml.cs │ ├── Features │ │ ├── AsyncState │ │ │ ├── AsyncStateListViewModel.cs │ │ │ └── AsyncStateViewModel.cs │ │ ├── Busy │ │ │ ├── BusyViewModel.cs │ │ │ └── ObservableBusyViewModel.cs │ │ ├── ContextMenu │ │ │ ├── ContextMenuViewModel.cs │ │ │ └── ContextMenuViewModels.cs │ │ ├── DataGrid │ │ │ ├── DataGridDataProvider.cs │ │ │ ├── DataGridRowViewModel.cs │ │ │ └── DataGridViewModel.cs │ │ ├── DialogViewModel.cs │ │ ├── Enums │ │ │ ├── EnumViewModel.cs │ │ │ └── ViewModelEnum.cs │ │ ├── FormViewModel.cs │ │ ├── Geometry │ │ │ ├── GeometryContainer.cs │ │ │ └── GeometryRenderViewModel.cs │ │ ├── Image │ │ │ ├── Image.cs │ │ │ ├── ImageFactory.cs │ │ │ └── Images.cs │ │ ├── NavigationViewModel.cs │ │ ├── ObservableDictionaryViewModel.cs │ │ ├── PasswordViewModel.cs │ │ ├── Process │ │ │ ├── ProcessData.cs │ │ │ ├── ProcessErrorData.cs │ │ │ ├── ProcessViewModel.cs │ │ │ └── ProcessingImagesViewModel.cs │ │ ├── ProgressViewModel.cs │ │ ├── ToastsViewmodel.cs │ │ └── Virtualization │ │ │ ├── DataEntriesViewModel.cs │ │ │ └── DataEntryViewModel.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── MvvmScarletToolkit.Wpf.Samples.csproj │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ └── PublishProfiles │ │ │ └── FolderProfile.pubxml │ └── Resources │ │ ├── Death_to_Stock_Photography_RideorDie_8.jpg │ │ ├── Moon_Color_Hypersaturated_Stars_900.jpg │ │ ├── wallhaven-245035.jpg │ │ ├── wallhaven-319605.jpg │ │ ├── wallhaven-401406.jpg │ │ └── wallhaven-75354.jpg ├── MvvmScarletToolkit.Wpf.Tests │ ├── AllowableCharactersTextBoxBehaviorServiceTests.cs │ ├── CommandBuilderContextTests.cs │ ├── CommandBuilderExtensionTests.cs │ ├── ConcurrentCommandTests.cs │ ├── IsNotNullOrEmptyTests.cs │ ├── IsNotNullOrWhiteSpaceTests.cs │ ├── IsNotNullTests.cs │ ├── IsNotTests.cs │ ├── IsNullOrEmptyTests.cs │ ├── IsNullOrWhiteSpaceTests.cs │ ├── IsNullTests.cs │ ├── MvvmScarletToolkit.Wpf.Tests.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── ScarletCommandBuilderTests.cs │ ├── TestData │ │ ├── DerivedObjectViewModelBase.cs │ │ ├── DerivedViewModelBase.cs │ │ └── DerivedViewModelListBase.cs │ ├── Util │ │ ├── TestDispatcher.cs │ │ └── Utils.cs │ ├── ViewModelBaseTests.cs │ └── ViewModelListBaseTests.cs ├── MvvmScarletToolkit.Wpf │ ├── Attached Properties │ │ ├── Filter.cs │ │ └── Focus.cs │ ├── Behaviors │ │ ├── AllowableCharactersTextBoxBehavior.cs │ │ ├── AllowableCharactersTextBoxBehaviorService.cs │ │ ├── AutoRepositionPopupBehavior.cs │ │ ├── AutoScrollBehavior.cs │ │ ├── LaunchNavigateUriAsNewProcessBehavior.cs │ │ ├── MultiListBoxSelectionBehavior.cs │ │ ├── MultiSelectionBehavior.cs │ │ ├── PasswordBindingBehavior.cs │ │ ├── ScrollSelectionIntoViewBehavior.cs │ │ ├── ScrollToEndCommandBehavior.cs │ │ ├── SelectedTreeViewItemBehavior.cs │ │ ├── SmoothProgressBehavior.cs │ │ └── WatermarkBehavior.cs │ ├── BindingProxy.cs │ ├── Extensions │ │ └── DependencyObjectExtensions.cs │ ├── Features │ │ ├── DataTemplateSelectionManagement │ │ │ ├── DataTemplateCollection.cs │ │ │ └── TypeDataTemplateSelector.cs │ │ ├── FileSystemBrowser │ │ │ ├── FileExtensions.cs │ │ │ ├── FileSystemBrowser.cs │ │ │ ├── FileSystemViewModelFactory.cs │ │ │ ├── FileSystemViewModelFactoryExtensions.cs │ │ │ ├── Generic.xaml │ │ │ ├── Interfaces │ │ │ │ ├── IFileSystemChild.cs │ │ │ │ ├── IFileSystemDirectory.cs │ │ │ │ ├── IFileSystemDrive.cs │ │ │ │ ├── IFileSystemFile.cs │ │ │ │ ├── IFileSystemInfo.cs │ │ │ │ ├── IFileSystemParent.cs │ │ │ │ ├── IFileSystemViewModelFactory.cs │ │ │ │ └── IMimeTypeResolver.cs │ │ │ ├── MimeTypeResolvers │ │ │ │ ├── MagicNumberMimeTypeResolver.cs │ │ │ │ ├── RegistryFileExtensionMimeTypeResolver.cs │ │ │ │ ├── StaticFileExtensionMimeTypeResolver.cs │ │ │ │ └── UrlMonMimeTypeResolver.cs │ │ │ ├── ViewModels │ │ │ │ ├── FileSystemOptionsViewModel.cs │ │ │ │ ├── FileSystemViewModel.cs │ │ │ │ ├── ScarletDirectory.cs │ │ │ │ ├── ScarletDrive.cs │ │ │ │ └── ScarletFile.cs │ │ │ └── readme.md │ │ ├── GridViewColumnSizing │ │ │ ├── FixedColumn.cs │ │ │ ├── LayoutColumn.cs │ │ │ ├── ListViewLayoutManager.cs │ │ │ ├── ProportionalColumn.cs │ │ │ └── RangeColumn.cs │ │ ├── GroupManagement │ │ │ ├── GroupViewModel.cs │ │ │ ├── GroupingViewModel.cs │ │ │ ├── GroupsViewModel.cs │ │ │ └── GroupsViewModelRemoved.cs │ │ ├── HtmlTextBlock │ │ │ ├── Enums │ │ │ │ ├── CharType.cs │ │ │ │ ├── HTMLFlag.cs │ │ │ │ └── SymbolType.cs │ │ │ ├── HtmlAttributeStringSerializer.cs │ │ │ ├── HtmlHighlightTextBlock.cs │ │ │ ├── HtmlParser.cs │ │ │ ├── HtmlTextBlock.cs │ │ │ ├── InlineCreationContext.cs │ │ │ ├── Interfaces │ │ │ │ ├── IParamParser.cs │ │ │ │ └── IPropertySerializer.cs │ │ │ ├── Models │ │ │ │ ├── HTMLTagInfo.cs │ │ │ │ ├── HtmlTag.cs │ │ │ │ ├── HtmlTagNode.cs │ │ │ │ └── HtmlTagTree.cs │ │ │ └── ParamParser.cs │ │ ├── ToastNotification │ │ │ ├── Generic.xaml │ │ │ ├── IToast.cs │ │ │ ├── IToastService.cs │ │ │ ├── ToastService.cs │ │ │ ├── ToastServiceConfiguration.cs │ │ │ ├── ToastServiceExtensions.cs │ │ │ ├── ToastType.cs │ │ │ └── ToastViewModel.cs │ │ ├── VirtualizingTilePanel.cs │ │ └── VirtualizingWrapPanel.cs │ ├── Implementations │ │ ├── ScarletCommandBuilder.cs │ │ ├── ScarletCommandManager.cs │ │ ├── ScarletDispatcher.cs │ │ ├── ScarletExitService.cs │ │ ├── ScarletLocalizationProvider.cs │ │ └── ScarletWeakEventManager.cs │ ├── MarkupExtensions │ │ ├── ConverterMarkupExtension.cs │ │ ├── Converters │ │ │ ├── DebugConverter.cs │ │ │ ├── Flatten.cs │ │ │ ├── GreaterThan.cs │ │ │ ├── IgnoreNullOrEmptyStrings.cs │ │ │ ├── InvertBooleanToVisibilityConverter.cs │ │ │ ├── IsNot.cs │ │ │ ├── IsNotNull.cs │ │ │ ├── IsNotNullOrEmpty.cs │ │ │ ├── IsNotNullOrWhiteSpace.cs │ │ │ ├── IsNull.cs │ │ │ ├── IsNullOrEmpty.cs │ │ │ ├── IsNullOrWhiteSpace.cs │ │ │ ├── MultiBooleanAndConverter.cs │ │ │ ├── RadioButtonCheckedConverter.cs │ │ │ ├── SmallerThan.cs │ │ │ └── ToCase.cs │ │ ├── EnumBindingSourceExtension.cs │ │ ├── MultiConverterMarkupExtension.cs │ │ └── StartProcessExtension.cs │ ├── MvvmScarletToolkit.Wpf.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── TriggerActions │ │ ├── ClearPasswordBoxAction.cs │ │ └── ClearTextBoxAction.cs │ └── readme.md ├── MvvmScarletToolkit │ ├── Extensions │ │ ├── CommandExtensions.cs │ │ ├── DispatcherExtensions.cs │ │ ├── EnumExtensions.cs │ │ ├── EnumerableExtensions.cs │ │ ├── EventExtension.cs │ │ ├── ListExtensions.cs │ │ ├── ObservableCollectionExtensions.cs │ │ ├── QueryExtensions.cs │ │ └── TypeExtensions.cs │ ├── Messages │ │ └── CancellableGenericScarletMessage.cs │ ├── MvvmScarletToolkit.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ └── readme.md └── SharedAssemblyInfo.cs └── version.json /.XamlStyler: -------------------------------------------------------------------------------- 1 | { 2 | "AttributesTolerance": 2, 3 | "KeepFirstAttributeOnSameLine": true, 4 | "MaxAttributeCharatersPerLine": 0, 5 | "MaxAttributesPerLine": 1, 6 | "NewlineExemptionElements": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransfom, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter", 7 | "SeparateByGroups": false, 8 | "AttributeIndentation": 0, 9 | "AttributeIndentationStyle": 1, 10 | "RemoveDesignTimeReferences": false, 11 | "EnableAttributeReordering": true, 12 | "AttributeOrderingRuleGroups": [ 13 | "x:Class", 14 | "xmlns, xmlns:x", 15 | "xmlns:*", 16 | "x:Key, Key, x:Name, Name, x:Uid, Uid, Title", 17 | "Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom", 18 | "Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight", 19 | "Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex", 20 | "*:*, *", 21 | "PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint", 22 | "mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText", 23 | "options:Freeze, op:Freeze, o:Freeze, 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": true, 34 | "ReorderCanvasChildren": false, 35 | "ReorderSetters": 1, 36 | "FormatMarkupExtension": true, 37 | "NoNewLineMarkupExtensions": "x:Bind, Binding", 38 | "ThicknessSeparator": 1, 39 | "ThicknessAttributes": "Margin, Padding, BorderThickness, ThumbnailClipMargin", 40 | "FormatOnSave": false, 41 | "CommentPadding": 2, 42 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | target-branch: dev 10 | ignore: 11 | - dependency-name: Magick.NET.SystemWindowsMedia 12 | versions: 13 | - 3.0.5 14 | - 3.0.6 15 | - dependency-name: Magick.NET-Q8-AnyCPU 16 | versions: 17 | - 7.23.1 18 | - 7.23.2 19 | - dependency-name: IDisposableAnalyzers 20 | versions: 21 | - 3.4.10 22 | - dependency-name: FluentValidation 23 | versions: 24 | - 9.4.0 25 | - 9.5.0 26 | - dependency-name: Moq 27 | versions: 28 | - 4.16.0 29 | - dependency-name: nunit 30 | versions: 31 | - 3.13.0 32 | - dependency-name: Microsoft.Xaml.Behaviors.Wpf 33 | versions: 34 | - 1.1.31 35 | - dependency-name: WpfAnalyzers 36 | versions: 37 | - 3.5.4 38 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (WpfApp)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/DemoApp/bin/Debug/netcoreapp3.1/win10-x64/DemoApp.exe", 14 | "args": [], 15 | "externalConsole": true, 16 | "cwd": "${workspaceFolder}/DemoApp", 17 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 18 | "console": "internalConsole", 19 | "stopAtEntry": false 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/DemoApp/DemoApp.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/DemoApp/DemoApp.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/DemoApp/DemoApp.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /.vsconfig: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "components": [ 4 | "Microsoft.VisualStudio.Component.CoreEditor", 5 | "Microsoft.VisualStudio.Workload.CoreEditor", 6 | "Microsoft.NetCore.Component.SDK", 7 | "Microsoft.VisualStudio.Component.NuGet", 8 | "Microsoft.VisualStudio.Component.Roslyn.Compiler", 9 | "Microsoft.VisualStudio.Component.Roslyn.LanguageServices", 10 | "Microsoft.NetCore.Component.DevelopmentTools", 11 | "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites", 12 | "Microsoft.Component.MSBuild", 13 | "Microsoft.VisualStudio.Component.TextTemplating", 14 | "Microsoft.VisualStudio.Component.ManagedDesktop.Core", 15 | "Microsoft.VisualStudio.Component.IntelliCode", 16 | "Microsoft.VisualStudio.Component.Debugger.JustInTime", 17 | "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites", 18 | "Microsoft.VisualStudio.Workload.ManagedDesktop" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 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 | -------------------------------------------------------------------------------- /MvvmScarletToolkit.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /MvvmScarletToolkit.slnf: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "path": "MvvmScarletToolkit.sln", 4 | "projects": [ 5 | "src\\MvvmScarletToolkit.Abstractions\\MvvmScarletToolkit.Abstractions.csproj", 6 | "src\\MvvmScarletToolkit.Commands\\MvvmScarletToolkit.Commands.csproj", 7 | "src\\MvvmScarletToolkit.Observables\\MvvmScarletToolkit.Observables.csproj", 8 | "src\\MvvmScarletToolkit.Wpf\\MvvmScarletToolkit.Wpf.csproj", 9 | "src\\MvvmScarletToolkit.Xamarin.Forms\\MvvmScarletToolkit.Xamarin.Forms.csproj", 10 | "src\\MvvmScarletToolkit\\MvvmScarletToolkit.csproj" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /build.config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | DOTNET_VERSION=7.0.102 3 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | dotnet run --project cake/Build.csproj -- $args 2 | exit $LASTEXITCODE; 3 | -------------------------------------------------------------------------------- /cake/Build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net9.0 5 | true 6 | 7 | 8 | $(MSBuildProjectDirectory) 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /cake/Build.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31025.194 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "Build.csproj", "{79BD7F6E-3F66-4597-99B9-6207D04DEB69}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {79BD7F6E-3F66-4597-99B9-6207D04DEB69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {79BD7F6E-3F66-4597-99B9-6207D04DEB69}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {79BD7F6E-3F66-4597-99B9-6207D04DEB69}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {79BD7F6E-3F66-4597-99B9-6207D04DEB69}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {FC9F05C6-BBA9-4B34-BCD1-3FDE3469CE81} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /cake/BuildLifetime.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common; 2 | using Cake.Common.Build; 3 | using Cake.Common.Diagnostics; 4 | using Cake.Common.Tools.GitVersion; 5 | using Cake.Core; 6 | using Cake.Frosting; 7 | using Cake.GitVersioning; 8 | 9 | namespace Build 10 | { 11 | public sealed class BuildLifetime : FrostingLifetime 12 | { 13 | public override void Setup(BuildContext context, ISetupContext info) 14 | { 15 | context.GitVersion = context.GitVersioningGetVersion(); 16 | context.Branch = context.GitVersion().BranchName; 17 | 18 | context.Information("Branch: {0}", context.Branch); 19 | 20 | if (context.IsPublicRelease && context.Branch == "master") 21 | { 22 | context.Information("Building a {0} release.", "public"); 23 | } 24 | else 25 | { 26 | context.Information("Building a {0}release.", "pre-"); 27 | } 28 | 29 | context.Information("Provider: {0}", context.BuildSystem().Provider); 30 | context.Information("Platform: {0} ({1})", context.Environment.Platform.Family, context.Environment.Platform.Is64Bit ? "x64" : "x86"); 31 | 32 | context.Information("NUGETORG_APIKEY was {0} set.", string.IsNullOrEmpty(context.EnvironmentVariable("NUGETORG_APIKEY")) ? "not" : ""); 33 | context.Information("GITHUB_APIKEY was {0} set.", string.IsNullOrEmpty(context.EnvironmentVariable("GITHUB_APIKEY")) ? "not" : ""); 34 | context.Information("CODECOV_TOKEN was {0} set.", string.IsNullOrEmpty(context.EnvironmentVariable("CODECOV_TOKEN")) ? "not" : ""); 35 | 36 | context.Information("reportsFolder: {0}", context.ReportsPath.FullPath); 37 | context.Information("coberturaResultFile: {0}", context.CoberturaResultFile.FullPath); 38 | 39 | context.Information("dotnet tool: {0}", context.Tools.Resolve("dotnet.exe")); 40 | } 41 | 42 | public override void Teardown(BuildContext context, ITeardownContext info) 43 | { 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cake/Program.cs: -------------------------------------------------------------------------------- 1 | using Cake.Frosting; 2 | using System; 3 | 4 | namespace Build 5 | { 6 | public static class Program 7 | { 8 | public static int Main(string[] args) 9 | { 10 | return new CakeHost() 11 | .InstallTool(new Uri("nuget:?package=Codecov&version=1.13.0")) 12 | .InstallTool(new Uri("nuget:?package=NUnit.ConsoleRunner&version=3.20.0")) 13 | .InstallTool(new Uri("nuget:?package=ReportGenerator&version=5.4.5")) 14 | .InstallTool(new Uri("nuget:?package=GitVersion.CommandLine&version=5.12.0")) 15 | .InstallTool(new Uri("nuget:?package=nuget.commandline&version=6.13.2")) 16 | .InstallTool(new Uri("nuget:?package=dotnet-coverage&version=17.13.1")) 17 | .UseContext() 18 | .UseLifetime() 19 | .UseWorkingDirectory("..") 20 | .Run(args); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cake/Tasks/BuildAndPack.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common; 2 | using Cake.Core; 3 | using Cake.Core.IO; 4 | using Cake.Frosting; 5 | using System.Linq; 6 | 7 | namespace Build 8 | { 9 | public sealed class BuildAndPack : FrostingTask 10 | { 11 | public override void Run(BuildContext context) 12 | { 13 | foreach (var project in context.NugetPackageProjects) 14 | { 15 | var semver = context.GitVersion.SemVer2; 16 | var settings = new ProcessSettings() 17 | .UseWorkingDirectory(".") 18 | .WithArguments(builder => builder 19 | .Append("pack") 20 | .AppendQuoted(project.ProjectFile.FullPath) 21 | .Append($"-c {BuildContext.BuildConfiguration}") 22 | .Append($"--output \"{context.PackagePath.FullPath}\"") 23 | .Append($"-p:PackageVersion={context.GitVersion.SemVer2}") 24 | .Append($"-p:PublicRelease={context.IsPublicRelease}") // Nerdbank.GitVersioning - omit git commit ID 25 | 26 | // Creating symbol packages 27 | .Append($"--include-symbols") 28 | .Append("--include-source") 29 | .Append($"-p:SymbolPackageFormat=snupkg") 30 | 31 | // enable source linking 32 | .Append($"-p:PublishRepositoryUrl=true") 33 | 34 | // Deterministic Builds 35 | .Append($"-p:EmbedUntrackedSources=true") 36 | 37 | .Append($"-p:DebugType=portable") 38 | .Append($"-p:DebugSymbols=true") 39 | ); 40 | 41 | context.StartProcess("dotnet", settings); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /cake/Tasks/CleanSolution.cs: -------------------------------------------------------------------------------- 1 | using Cake.Frosting; 2 | 3 | namespace Build 4 | { 5 | public sealed class CleanSolution : FrostingTask 6 | { 7 | public override void Run(BuildContext context) 8 | { 9 | context.Clean(true, true, true, true); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /cake/Tasks/CleanSolutionAgain.cs: -------------------------------------------------------------------------------- 1 | using Cake.Frosting; 2 | 3 | namespace Build 4 | { 5 | public sealed class CleanSolutionAgain : FrostingTask 6 | { 7 | public override void Run(BuildContext context) 8 | { 9 | context.Clean(true, true, true, false); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /cake/Tasks/CoberturaReport.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common.IO; 2 | using Cake.Common.Tools.ReportGenerator; 3 | using Cake.Core; 4 | using Cake.Core.IO; 5 | using Cake.Frosting; 6 | 7 | namespace Build 8 | { 9 | [IsDependentOn(typeof(ConvertCoverage))] 10 | public sealed class CoberturaReport : FrostingTask 11 | { 12 | public override void Run(BuildContext context) 13 | { 14 | context.MergeReports("./results/coverage/**/*.xml", ReportGeneratorReportType.Cobertura, "cobertura"); 15 | } 16 | 17 | public override bool ShouldRun(BuildContext context) 18 | { 19 | var files = context.GetFiles("./results/coverage/**/*.xml"); 20 | return files.Count > 0; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cake/Tasks/ConvertCoverage.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common; 2 | using Cake.Common.IO; 3 | using Cake.Core; 4 | using Cake.Core.IO; 5 | using Cake.Frosting; 6 | using System.Linq; 7 | 8 | namespace Build 9 | { 10 | [IsDependentOn(typeof(Test))] 11 | public sealed class ConvertCoverage : FrostingTask 12 | { 13 | public override void Run(BuildContext context) 14 | { 15 | var dotnetExe = context.Tools.Resolve("dotnet.exe"); 16 | var codeCoverageExe = context.Tools.Resolve("dotnet-coverage.dll"); 17 | 18 | foreach (var file in context.GetFiles($"{context.ResultsPath.FullPath}/coverage/**/*.coverage")) 19 | { 20 | var result = System.IO.Path.ChangeExtension(file.FullPath, ".xml"); 21 | 22 | var settings = new ProcessSettings() 23 | .UseWorkingDirectory(context.ResultsPath) 24 | .WithArguments(builder => builder 25 | .AppendQuoted(codeCoverageExe.FullPath) 26 | .Append("merge") 27 | .Append("--remove-input-files") 28 | .AppendSwitchQuoted(@"--output", " ", result) 29 | .AppendSwitch("--output-format", "xml") 30 | .Append(file.FullPath) 31 | ); 32 | 33 | context.StartProcess(dotnetExe.FullPath, settings); 34 | } 35 | } 36 | 37 | public override bool ShouldRun(BuildContext context) 38 | { 39 | return context.Tools.Resolve("dotnet-coverage.dll") != null; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cake/Tasks/Default.cs: -------------------------------------------------------------------------------- 1 | using Cake.Frosting; 2 | 3 | namespace Build 4 | { 5 | [IsDependentOn(typeof(CleanSolution))] 6 | [IsDependentOn(typeof(UpdateAssemblyInfo))] 7 | [IsDependentOn(typeof(TestAndUploadReport))] 8 | [IsDependentOn(typeof(CleanSolutionAgain))] 9 | [IsDependentOn(typeof(BuildAndPack))] 10 | [IsDependentOn(typeof(PushNuget))] 11 | [IsDependentOn(typeof(PushGithub))] 12 | [IsDependentOn(typeof(PushLocally))] 13 | public sealed class Default : FrostingTask; 14 | } 15 | -------------------------------------------------------------------------------- /cake/Tasks/HtmlReport.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common.IO; 2 | using Cake.Common.Tools.ReportGenerator; 3 | using Cake.Core; 4 | using Cake.Core.IO; 5 | using Cake.Frosting; 6 | 7 | namespace Build 8 | { 9 | [IsDependentOn(typeof(ConvertCoverage))] 10 | public sealed class HtmlReport : FrostingTask 11 | { 12 | public override void Run(BuildContext context) 13 | { 14 | context.MergeReports("./results/coverage/**/*.xml", ReportGeneratorReportType.Html, "html"); 15 | } 16 | 17 | public override bool ShouldRun(BuildContext context) 18 | { 19 | var files = context.GetFiles("./results/coverage/**/*.xml"); 20 | return files.Count > 0; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cake/Tasks/PushGithub.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common; 2 | using Cake.Common.Build; 3 | using Cake.Common.IO; 4 | using Cake.Core; 5 | using Cake.Core.IO; 6 | using Cake.Frosting; 7 | 8 | namespace Build 9 | { 10 | public sealed class PushGithub : FrostingTask 11 | { 12 | public override void Run(BuildContext context) 13 | { 14 | foreach (var package in context.GetFiles(context.PackagePath.FullPath + "/*.nupkg")) 15 | { 16 | var settings = new ProcessSettings() 17 | .UseWorkingDirectory(".") 18 | .WithArguments(builder => builder 19 | .Append("nuget push") 20 | .AppendQuoted(package.FullPath) 21 | 22 | .AppendSwitchQuotedSecret("--api-key", context.EnvironmentVariable("GITHUB_APIKEY")) 23 | .AppendSwitch("--source", "https://nuget.pkg.github.com/insire/index.json") 24 | .Append("--skip-duplicate") 25 | ); 26 | 27 | context.StartProcess("dotnet", settings); 28 | } 29 | } 30 | 31 | public override bool ShouldRun(BuildContext context) 32 | { 33 | return base.ShouldRun(context) 34 | && context.BuildSystem().IsRunningOnAzurePipelines 35 | && !string.IsNullOrEmpty(context.EnvironmentVariable("GITHUB_APIKEY")) 36 | && !context.IsPublicRelease; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cake/Tasks/PushLocally.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common.Build; 2 | using Cake.Common.IO; 3 | using Cake.Common.Tools.NuGet; 4 | using Cake.Core; 5 | using Cake.Core.IO; 6 | using Cake.Frosting; 7 | 8 | namespace Build 9 | { 10 | public sealed class PushLocally : FrostingTask 11 | { 12 | private const string LocalNugetFolder = @"D:\Drop\NuGet"; 13 | 14 | public override void Run(BuildContext context) 15 | { 16 | if (!context.NuGetHasSource(LocalNugetFolder)) 17 | { 18 | context.NuGetAddSource("Local", LocalNugetFolder); 19 | } 20 | 21 | foreach (var package in context.GetFiles(context.PackagePath.FullPath + "/*.nupkg")) 22 | { 23 | context.NuGetPush(package, new Cake.Common.Tools.NuGet.Push.NuGetPushSettings() 24 | { 25 | Source = LocalNugetFolder, 26 | }); 27 | } 28 | } 29 | 30 | public override bool ShouldRun(BuildContext context) 31 | { 32 | return base.ShouldRun(context) 33 | && context.BuildSystem().IsLocalBuild 34 | && context.DirectoryExists(LocalNugetFolder); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cake/Tasks/PushNuget.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common; 2 | using Cake.Common.Build; 3 | using Cake.Common.IO; 4 | using Cake.Common.Tools.NuGet; 5 | using Cake.Core; 6 | using Cake.Core.IO; 7 | using Cake.Frosting; 8 | 9 | namespace Build 10 | { 11 | public sealed class PushNuget : FrostingTask 12 | { 13 | public override void Run(BuildContext context) 14 | { 15 | foreach (var package in context.GetFiles(context.PackagePath.FullPath + "/*.nupkg")) 16 | { 17 | context.NuGetPush(package, new Cake.Common.Tools.NuGet.Push.NuGetPushSettings() 18 | { 19 | ApiKey = context.EnvironmentVariable("NUGETORG_APIKEY"), 20 | Source = "https://api.nuget.org/v3/index.json", 21 | SkipDuplicate = true, 22 | }); 23 | } 24 | } 25 | 26 | public override bool ShouldRun(BuildContext context) 27 | { 28 | return base.ShouldRun(context) 29 | && context.BuildSystem().IsRunningOnAzurePipelines 30 | && !string.IsNullOrEmpty(context.EnvironmentVariable("NUGETORG_APIKEY")) 31 | && context.IsPublicRelease; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cake/Tasks/Test.cs: -------------------------------------------------------------------------------- 1 | using Cake.Common.IO; 2 | using Cake.Common.Tools.DotNet; 3 | using Cake.Common.Tools.DotNet.Test; 4 | using Cake.Core; 5 | using Cake.Frosting; 6 | using System.Collections.Generic; 7 | 8 | namespace Build 9 | { 10 | public sealed class Test : FrostingTask 11 | { 12 | public override void Run(BuildContext context) 13 | { 14 | var testSettings = new DotNetTestSettings 15 | { 16 | Configuration = BuildContext.BuildConfiguration, 17 | NoBuild = false, 18 | NoRestore = false, 19 | NoLogo = false, 20 | ResultsDirectory = context.CoveragePath, 21 | Loggers = new[] { "trx" }, 22 | Collectors = new[] { "Code Coverage" }, 23 | EnvironmentVariables = new Dictionary() { ["Environment"] = "Test" }, 24 | }; 25 | 26 | var files = context.GetFiles(context.SourcePath.FullPath + "/*/*.Tests.csproj"); 27 | foreach (var file in files) 28 | { 29 | context.DotNetTest(file.FullPath, testSettings); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cake/Tasks/TestAndUploadReport.cs: -------------------------------------------------------------------------------- 1 | using Cake.Frosting; 2 | 3 | namespace Build 4 | { 5 | [IsDependentOn(typeof(HtmlReport))] 6 | [IsDependentOn(typeof(UploadCodecovReport))] 7 | public sealed class TestAndUploadReport : FrostingTask; 8 | } 9 | -------------------------------------------------------------------------------- /cake/Tasks/UploadCodecovReport.cs: -------------------------------------------------------------------------------- 1 | using Cake.Codecov; 2 | using Cake.Common; 3 | using Cake.Common.IO; 4 | using Cake.Frosting; 5 | 6 | namespace Build 7 | { 8 | [IsDependentOn(typeof(CoberturaReport))] 9 | public sealed class UploadCodecovReport : FrostingTask 10 | { 11 | public override void Run(BuildContext context) 12 | { 13 | var settings = new CodecovSettings() 14 | { 15 | Verbose = true, 16 | WorkingDirectory = context.CoberturaResultsPath, 17 | Files = new[] { context.CoberturaResultFile.FullPath }, 18 | Token = context.EnvironmentVariable("CODECOV_TOKEN"), 19 | }; 20 | 21 | context.Codecov(settings); 22 | } 23 | 24 | public override bool ShouldRun(BuildContext context) 25 | { 26 | return base.ShouldRun(context) 27 | && context.FileExists(context.CoberturaResultFile) 28 | && !string.IsNullOrEmpty(context.EnvironmentVariable("CODECOV_TOKEN")); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "70...100" 8 | 9 | parsers: 10 | gcov: 11 | branch_detection: 12 | conditional: yes 13 | loop: yes 14 | method: no 15 | macro: no 16 | 17 | comment: 18 | layout: "reach,diff,flags,tree" 19 | behavior: default 20 | require_changes: no 21 | 22 | fixes: 23 | - "./a/1/s/::" 24 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.203", 4 | "rollForward": "disable" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MvvmScarletToolkit 5 | SoftThorn 6 | Peter Vietense 7 | MIT 8 | https://github.com/Insire/MvvmScarletToolkit 9 | https://github.com/Insire/MvvmScarletToolkit 10 | git 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | False 10 | 13 11 | ..\..\MvvmScarletToolkit.ruleset 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | True 23 | Full 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/AbstractBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public abstract class AbstractBuilder : IBuilder 4 | { 5 | public static implicit operator TElement(AbstractBuilder @this) 6 | { 7 | return @this.Build(); 8 | } 9 | 10 | public abstract TElement Build(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IAsyncCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public interface IAsyncCommand : IConcurrentCommand, IDisposable; 6 | } 7 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public interface IBuilder 4 | { 5 | TElement Build(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IBusinessViewModelListBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.ComponentModel; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public interface IBusinessViewModelListBase : IVirtualizationViewModel 7 | where TViewModel : class, INotifyPropertyChanged 8 | { 9 | ReadOnlyObservableCollection Items { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IBusyStack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public interface IBusyStack 6 | { 7 | /// 8 | /// Generate a disposeable token 9 | /// 10 | IDisposable GetToken(); 11 | 12 | /// 13 | /// remove a token from the stack 14 | /// 15 | void Pull(); 16 | 17 | /// 18 | /// add a token to the stack 19 | /// 20 | void Push(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/ICancelCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Windows.Input; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public interface ICancelCommand : ICommand, IDisposable 8 | { 9 | CancellationToken Token { get; } 10 | 11 | void NotifyCommandFinished(); 12 | 13 | void NotifyCommandStarting(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IConcurrentCommand.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Threading.Tasks; 3 | using System.Windows.Input; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public interface IConcurrentCommand : ICommand, INotifyPropertyChanged 8 | { 9 | Task ExecuteAsync(object parameter); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IExitService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public interface IExitService 6 | { 7 | Task ShutDown(); 8 | 9 | void UnloadOnExit(IVirtualizationViewModel viewModel); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/ILocalizationProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public interface ILocalizationProvider 7 | { 8 | /// 9 | /// Translates the key into a localized value 10 | /// 11 | /// The key. 12 | /// 13 | string Translate(string key, CultureInfo culture); 14 | 15 | /// 16 | /// Gets the available languages. 17 | /// 18 | /// The available languages. 19 | IEnumerable Languages { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/ILocalizationService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public interface ILocalizationService : INotifyPropertyChanged 8 | { 9 | CultureInfo? CurrentLanguage { get; set; } 10 | 11 | /// 12 | /// Translates the key into a localized value 13 | /// 14 | /// The key. 15 | /// 16 | string Translate(string key); 17 | 18 | /// 19 | /// Gets the available languages. 20 | /// 21 | /// The available languages. 22 | IEnumerable Languages { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/ILocalizationViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public interface ILocalizationViewModel : INotifyPropertyChanged, IDisposable 7 | { 8 | string Value { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IObservableBusyStack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public interface IObservableBusyStack : IObservable, IBusyStack, IDisposable; 6 | } 7 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IScarletCommandManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public interface IScarletCommandManager 6 | { 7 | void InvalidateRequerySuggested(); 8 | 9 | event EventHandler RequerySuggested; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IScarletDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public interface IScarletDispatcher 8 | { 9 | Task Invoke(Action action, CancellationToken token); 10 | 11 | Task Invoke(Func action, CancellationToken token); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IScarletEventManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public interface IScarletEventManager 6 | where TEventArgs : EventArgs 7 | { 8 | void AddHandler(TEventSource source, string eventName, EventHandler handler); 9 | 10 | void RemoveHandler(TEventSource source, string eventName, EventHandler handler); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IScarletExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public interface IScarletExceptionHandler 7 | { 8 | Task Handle(Exception ex); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/IVirtualizationViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using System.Windows.Input; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | public interface IVirtualizationViewModel : INotifyPropertyChanged 9 | { 10 | bool IsLoaded { get; } 11 | 12 | /// 13 | /// conditional refresh 14 | /// 15 | ICommand LoadCommand { get; } 16 | 17 | /// 18 | /// explicit refresh, updates filtering and the current children 19 | /// 20 | ICommand RefreshCommand { get; } 21 | 22 | ICommand UnloadCommand { get; } 23 | 24 | Task Load(CancellationToken token); 25 | 26 | Task Refresh(CancellationToken token); 27 | 28 | Task Unload(CancellationToken token); 29 | 30 | bool CanLoad(); 31 | 32 | bool CanRefresh(); 33 | 34 | bool CanUnload(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/MvvmScarletToolkit.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | Library 5 | enable 6 | MvvmScarletToolkit.Abstractions is part of the MvvmScarletToolkit framework, containing interfaces and extensions used by the rest of the framework. 7 | MvvmScarletToolkit,MVVM,C#,Toolkit,Scarlet,Library,.NET,OSS,OpenSource 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("MvvmScarletToolkit.Abstractions")] 8 | [assembly: AssemblyDescription("MvvmScarletToolkit.Abstractions is part of the MvvmScarletToolkit framework, containing interfaces and extensions used by the rest of the framework.")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCulture("")] 11 | 12 | // Setting ComVisible to false makes the types in this assembly not visible 13 | // to COM components. If you need to access a type in this assembly from 14 | // COM, set the ComVisible attribute to true on that type. 15 | [assembly: ComVisible(false)] 16 | 17 | // The following GUID is for the ID of the typelib if this project is exposed to COM 18 | [assembly: Guid("4d4558e2-878f-48c8-bbe2-e22db20e9a55")] 19 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Abstractions/readme.md: -------------------------------------------------------------------------------- 1 | # MvvmScarletToolkit.Abstractions 2 | 3 | ![.NET standard 2.0](https://img.shields.io/badge/.NET-standard2.0-brightgreen) 4 | 5 | ## Goals 6 | 7 | This library aims to provide interfaces and extensions that are being used by the rest of the framework. 8 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Avalonia/Implementations/ScarletCommandManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | /// 6 | /// simple wrapper around 7 | /// 8 | public sealed class ScarletCommandManager : IScarletCommandManager 9 | { 10 | private static readonly Lazy _default = new Lazy(() => new ScarletCommandManager()); 11 | 12 | public static IScarletCommandManager Default => _default.Value; 13 | 14 | public event EventHandler? RequerySuggested; 15 | 16 | public void InvalidateRequerySuggested() 17 | { 18 | RequerySuggested?.Invoke(this, EventArgs.Empty); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Avalonia/Implementations/ScarletLocalizationProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Linq; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public sealed class ScarletLocalizationProvider : ILocalizationProvider 8 | { 9 | public IEnumerable Languages { get; } 10 | 11 | public ScarletLocalizationProvider() 12 | { 13 | Languages = Enumerable.Empty(); 14 | } 15 | 16 | public string Translate(string key, CultureInfo culture) 17 | { 18 | return key; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Avalonia/Implementations/ScarletWeakEventManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public sealed class ScarletWeakEventManager : IScarletEventManager 7 | { 8 | private readonly WeakEventManager _weakEventManager; 9 | private static readonly Lazy _default = new Lazy(() => new ScarletWeakEventManager()); 10 | 11 | public static IScarletEventManager Default => _default.Value; 12 | 13 | public ScarletWeakEventManager() 14 | { 15 | _weakEventManager = new WeakEventManager(); 16 | } 17 | 18 | public void AddHandler(INotifyPropertyChanged source, string eventName, EventHandler handler) 19 | { 20 | _weakEventManager.AddEventHandler(handler, eventName); 21 | } 22 | 23 | public void RemoveHandler(INotifyPropertyChanged source, string eventName, EventHandler handler) 24 | { 25 | _weakEventManager.RemoveEventHandler(handler, eventName); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Avalonia/MvvmScarletToolkit.Avalonia.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows;net9.0-windows 4 | Library 5 | enable 6 | MvvmScarletToolkit.Implementations is part of the MvvmScarletToolkit framework, containing concrete implementations for Avalonia that have been abstracted away in the MvvmScarletToolkit.Abstractions library. 7 | MvvmScarletToolkit,MVVM,C#,Toolkit,Scarlet,Avalonia,Library,.NET,OSS,OpenSource 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Commands/Core/ConcurrentCommandDecoratorBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Diagnostics; 4 | using System.Threading.Tasks; 5 | 6 | namespace MvvmScarletToolkit.Commands 7 | { 8 | public abstract class ConcurrentCommandDecoratorBase : ConcurrentCommandBase 9 | { 10 | protected readonly ConcurrentCommandBase Command; 11 | 12 | [Bindable(true, BindingDirection.OneWay)] 13 | public override bool IsBusy => Command.IsBusy; 14 | 15 | [Bindable(true, BindingDirection.OneWay)] 16 | public override Task Completion => Command.Completion; 17 | 18 | protected ConcurrentCommandDecoratorBase(in IScarletCommandManager commandManager, in ConcurrentCommandBase command) 19 | : base(commandManager) 20 | { 21 | Command = command ?? throw new ArgumentNullException(nameof(command)); 22 | Command.PropertyChanged += Command_PropertyChanged; 23 | CancelCommand = Command.CancelCommand; 24 | } 25 | 26 | private void Command_PropertyChanged(object sender, PropertyChangedEventArgs e) 27 | { 28 | OnPropertyChanged(e); 29 | } 30 | 31 | [DebuggerStepThrough] 32 | public override sealed async void Execute(object parameter) 33 | { 34 | await ExecuteAsync(parameter) 35 | .ConfigureAwait(true); // return to UI thread here 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Commands/Core/GenericConcurrentCommandBase.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Threading.Tasks; 3 | 4 | namespace MvvmScarletToolkit.Commands 5 | { 6 | /// 7 | /// Base implementation providing interface members for cancellation support and exposing current execution 8 | /// 9 | public abstract class GenericConcurrentCommandBase : ConcurrentCommandBase 10 | { 11 | [Bindable(true, BindingDirection.OneWay)] 12 | public override sealed Task Completion => Execution.TaskCompletion; 13 | 14 | private NotifyTaskCompletion _execution; 15 | [Bindable(true, BindingDirection.OneWay)] 16 | public NotifyTaskCompletion Execution 17 | { 18 | get { return _execution; } 19 | protected set 20 | { 21 | if (SetValue(ref _execution, value)) 22 | { 23 | OnPropertyChanged(nameof(Completion)); 24 | } 25 | } 26 | } 27 | 28 | protected GenericConcurrentCommandBase(in IScarletCommandManager commandManager) 29 | : base(commandManager) 30 | { 31 | _execution = NotifyTaskCompletion.Completed; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Commands/Core/IgnoreExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public sealed class IgnoreExceptionHandler : IScarletExceptionHandler 7 | { 8 | private static readonly Lazy _default = new Lazy(() => new IgnoreExceptionHandler()); 9 | 10 | public static IScarletExceptionHandler Default => _default.Value; 11 | 12 | public Task Handle(Exception ex) 13 | { 14 | #if DEBUG 15 | System.Diagnostics.Debug.WriteLine(ex.ToString()); 16 | #endif 17 | 18 | return Task.CompletedTask; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Commands/Core/NoCancellationCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace MvvmScarletToolkit.Commands 5 | { 6 | internal sealed class NoCancellationCommand : ICancelCommand 7 | { 8 | private static readonly Lazy _default = new Lazy(() => new NoCancellationCommand()); 9 | 10 | public static NoCancellationCommand Default => _default.Value; 11 | 12 | public CancellationToken Token { get; } 13 | 14 | public event EventHandler? CanExecuteChanged; 15 | 16 | private NoCancellationCommand() 17 | { 18 | } 19 | 20 | public bool CanExecute(object parameter) 21 | { 22 | return false; 23 | } 24 | 25 | public void Dispose() 26 | { 27 | } 28 | 29 | public void Execute(object parameter) 30 | { 31 | } 32 | 33 | public void NotifyCommandFinished() 34 | { 35 | CanExecuteChanged?.Invoke(this, EventArgs.Empty); 36 | } 37 | 38 | public void NotifyCommandStarting() 39 | { 40 | CanExecuteChanged?.Invoke(this, EventArgs.Empty); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Commands/IScarletCommandBuilder.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging; 2 | using MvvmScarletToolkit.Commands; 3 | using System; 4 | using System.ComponentModel; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace MvvmScarletToolkit 9 | { 10 | public interface IScarletCommandBuilder 11 | { 12 | IScarletExceptionHandler ExceptionHandler { get; } 13 | IScarletCommandManager CommandManager { get; } 14 | IScarletDispatcher Dispatcher { get; } 15 | IMessenger Messenger { get; } 16 | IExitService Exit { get; } 17 | IScarletEventManager WeakEventManager { get; } 18 | 19 | CommandBuilderContext Create(Func execute, Func canExecute); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Commands/MvvmScarletToolkit.Commands.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | Library 5 | enable 6 | MvvmScarletToolkit.Commands is part of the MvvmScarletToolkit framework, containing asynchrnous and synchronous implementations of the ICommand interface for .NET. 7 | MvvmScarletToolkit,MVVM,C#,Toolkit,Scarlet,Library,.NET,OSS,OpenSource 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Commands/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MvvmScarletToolkit.Commands")] 9 | [assembly: AssemblyDescription("MvvmScarletToolkit.Commands is part of the MvvmScarletToolkit framework, containing asynchrnous and synchronous implementations of the ICommand interface for .NET.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCulture("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("a85ecf94-f42d-43b2-9c65-c27e2963cf08")] 20 | [assembly: InternalsVisibleTo("MvvmScarletToolkit.Wpf.Tests")] 21 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Commands/readme.md: -------------------------------------------------------------------------------- 1 | # MvvmScarletToolkit.Commands 2 | 3 | ![.NET standard 2.0](https://img.shields.io/badge/.NET-standard2.0-brightgreen) 4 | 5 | ## Goals 6 | 7 | This library aims to provide easy to use [ICommand](https://docs.microsoft.com/en-gb/dotnet/api/system.windows.input.icommand) implementations for use in viewmodels. 8 | 9 | ## Contents 10 | 11 | |class|summary| 12 | |---|---| 13 | |``ConcurrentCommand``|A Task based ``ICommand`` implementation supporting fluent configuration via ``IScarletCommandBuilder``.| 14 | 15 | ## Usage 16 | 17 | 18 | ### ConcurrentCommand\ 19 | 20 | ```cs 21 | public class SomeClass 22 | { 23 | public ICommand Command { get; } 24 | 25 | public SomeClass(IScarletCommandBuilder commandBuilder) 26 | { 27 | Command = commandBuilder 28 | .Create(Do, CanDo) 29 | .WithSingleExecution() // prevent from running multiple instances of this command at the same time 30 | .WithBusyNotification(BusyStack) // notify an IBusyStack instance that this command is running 31 | .WithAsyncCancellation() // use an async ICommand implementation for cancellation support 32 | .Build(); 33 | } 34 | 35 | private Task Do(CancellationToken token) 36 | { 37 | return Task.Delay(2000); 38 | } 39 | 40 | private bool CanDo() 41 | { 42 | return true; 43 | } 44 | } 45 | ``` 46 | 47 | 48 | ## Credits 49 | 50 | ``ConcurrentCommand`` is largely based on Stephen Clearlys blog post: [Async Programming : Patterns for Asynchronous MVVM Applications](https://msdn.microsoft.com/en-us/magazine/dn630647.aspx?f=255&MSPPError=-2147217396) 51 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Incubator/BoolDialogResultViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public sealed class BoolDialogResultViewModel : DialogResultViewModel 4 | { 5 | public BoolDialogResultViewModel(IScarletCommandBuilder commandBuilder) 6 | : base(commandBuilder, false) 7 | { 8 | } 9 | 10 | public BoolDialogResultViewModel(IScarletCommandBuilder commandBuilder, bool model) 11 | : base(commandBuilder, model) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Incubator/MvvmScarletToolkit.Incubator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows;net9.0-windows 4 | Library 5 | true 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Incubator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | #if NET5_0_OR_GREATER 5 | [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows7.0")] 6 | #endif 7 | 8 | // General Information about an assembly is controlled through the following 9 | // set of attributes. Change these attribute values to modify the information 10 | // associated with an assembly. 11 | [assembly: AssemblyTitle("MvvmScarletToolkit.Incubator")] 12 | [assembly: AssemblyDescription("MvvmScarletToolkit.Incubator is part of the MvvmScarletToolkit framework, containing ideas and features that are still in a concept phase or classes which haven't been useful enough to be publically distributed.")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("67e41cb4-774b-455b-b0fc-86ef13677fb7")] 23 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables.Tests/AttributedBroadCastViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit.Observables.Tests 4 | { 5 | [ObservableRecipient] 6 | internal sealed partial class AttributedBroadCastViewModel : ObservableObject, ITestViewModel 7 | { 8 | [ObservableProperty] 9 | [NotifyPropertyChangedRecipients] 10 | private string _property; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables.Tests/BroadCastViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Messaging; 3 | 4 | namespace MvvmScarletToolkit.Observables.Tests 5 | { 6 | internal sealed partial class BroadCastViewModel : ObservableRecipient, ITestViewModel 7 | { 8 | private string property; 9 | public string Property 10 | { 11 | get { return property; } 12 | set { SetProperty(ref property, value, true); } 13 | } 14 | 15 | public BroadCastViewModel(IMessenger messenger) 16 | : base(messenger) 17 | { 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables.Tests/ITestViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit.Observables.Tests 4 | { 5 | public interface ITestViewModel : INotifyPropertyChanged 6 | { 7 | string Property { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows;net9.0-windows 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables.Tests/ViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit.Observables.Tests 4 | { 5 | internal sealed partial class ViewModel : ObservableRecipient 6 | { 7 | [ObservableProperty] 8 | [NotifyPropertyChangedRecipients] 9 | private string _data; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/Extensions/ObservableCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using DynamicData.Binding; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | public static class ObservableCollectionExtensions 9 | { 10 | /// 11 | /// in place sort an 12 | /// 13 | /// 14 | /// will undo selection, when collection is bound for example to a ListBox 15 | /// 16 | public static void Sort(this IObservableCollection collection) 17 | where T : IComparable 18 | { 19 | var sorted = collection.OrderBy(x => x).ToList(); 20 | 21 | for (var i = 0; i < sorted.Count; i++) 22 | { 23 | collection.Move(collection.IndexOf(sorted[i]), i); 24 | } 25 | } 26 | 27 | /// 28 | /// in place sort an utilizing an 29 | /// 30 | /// 31 | /// will undo selection, when collection is bound for example to a ListBox 32 | /// 33 | public static void Sort(this IObservableCollection collection, IComparer comparer) 34 | { 35 | var sorted = collection.OrderBy(x => x, comparer).ToList(); 36 | 37 | for (var i = 0; i < sorted.Count; i++) 38 | { 39 | collection.Move(collection.IndexOf(sorted[i]), i); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/IChange.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public interface IChange 4 | { 5 | bool IsActualChange { get; } 6 | } 7 | 8 | public interface IChange : IChange 9 | { 10 | /// 11 | /// Gets the value that the property had before the change. 12 | /// 13 | T InitialValue { get; } 14 | 15 | /// 16 | /// Gets the value that the property has after the change. 17 | /// 18 | T NewValue { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/MvvmScarletToolkit.Observables.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | Library 5 | enable 6 | MvvmScarletToolkit.Observables is part of the MvvmScarletToolkit framework, containing opinionated base classes for viewmodels of the MVVM pattern. 7 | MvvmScarletToolkit,MVVM,C#,Toolkit,Scarlet,Library,.NET,OSS,OpenSource 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("MvvmScarletToolkit.Observables")] 8 | [assembly: AssemblyDescription("MvvmScarletToolkit.Observables is part of the MvvmScarletToolkit framework, containing opinionated base classes for viewmodels of the MVVM pattern.")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCulture("")] 11 | 12 | // Setting ComVisible to false makes the types in this assembly not visible 13 | // to COM components. If you need to access a type in this assembly from 14 | // COM, set the ComVisible attribute to true on that type. 15 | [assembly: ComVisible(false)] 16 | 17 | // The following GUID is for the ID of the typelib if this project is exposed to COM 18 | [assembly: Guid("3d7bbed6-6056-488d-ae6a-65df15d3d046")] 19 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Base/BusinessViewModelBase_generic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace MvvmScarletToolkit.Observables 5 | { 6 | /// 7 | /// ViewModelBase that bootstraps loading, unloading and refreshing of its content 8 | /// 9 | /// 10 | public abstract class BusinessViewModelBase : BusinessViewModelBase 11 | where TModel : class 12 | { 13 | private TModel? _model; 14 | [Bindable(true, BindingDirection.OneWay)] 15 | public TModel? Model 16 | { 17 | get { return _model; } 18 | protected set { SetProperty(ref _model, value); } 19 | } 20 | 21 | protected BusinessViewModelBase(in IScarletCommandBuilder commandBuilder) 22 | : base(commandBuilder) 23 | { 24 | } 25 | 26 | protected BusinessViewModelBase(in IScarletCommandBuilder commandBuilder, in TModel model) 27 | : base(commandBuilder) 28 | { 29 | _model = model ?? throw new ArgumentNullException(nameof(model)); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Base/DomainViewModelListBase.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.ComponentModel; 5 | 6 | namespace MvvmScarletToolkit.Observables 7 | { 8 | /// 9 | /// Collection ViewModelBase that adds support for paged loading 10 | /// 11 | /// 12 | public abstract partial class DomainViewModelListBase : BusinessViewModelListBase 13 | where TViewModel : class, INotifyPropertyChanged 14 | { 15 | private int _total; 16 | [Bindable(true, BindingDirection.TwoWay)] 17 | public int Total 18 | { 19 | get { return _total; } 20 | protected set { SetProperty(ref _total, value); } 21 | } 22 | 23 | [ObservableProperty] 24 | private int _pageSize; 25 | 26 | [ObservableProperty] 27 | private int _pageIndex; 28 | 29 | [Bindable(true, BindingDirection.OneWay)] 30 | public PagingViewModel Paging { get; } 31 | 32 | protected DomainViewModelListBase(in IScarletCommandBuilder commandBuilder, in IEnumerable pageSizes) 33 | : base(commandBuilder) 34 | { 35 | Paging = new PagingViewModel(commandBuilder, this, new ReadOnlyObservableCollection(new ObservableCollection(pageSizes))); 36 | } 37 | 38 | protected override void Dispose(bool disposing) 39 | { 40 | if (IsDisposed) 41 | { 42 | return; 43 | } 44 | 45 | if (disposing) 46 | { 47 | Paging.Dispose(); 48 | } 49 | 50 | base.Dispose(disposing); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Base/Messages/ViewModelListBaseSelectionChanged.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging.Messages; 2 | 3 | namespace MvvmScarletToolkit.Observables 4 | { 5 | public sealed class ViewModelListBaseSelectionChanged : ValueChangedMessage 6 | { 7 | public object Sender { get; } 8 | 9 | public ViewModelListBaseSelectionChanged(in object sender, in TViewModel content) 10 | : base(content) 11 | { 12 | Sender = sender ?? throw new System.ArgumentNullException(nameof(sender)); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Base/Messages/ViewModelListBaseSelectionChanging.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging.Messages; 2 | 3 | namespace MvvmScarletToolkit.Observables 4 | { 5 | public sealed class ViewModelListBaseSelectionChanging : ValueChangedMessage 6 | { 7 | public object Sender { get; } 8 | 9 | public ViewModelListBaseSelectionChanging(in object sender, in TViewModel content) 10 | : base(content) 11 | { 12 | Sender = sender ?? throw new System.ArgumentNullException(nameof(sender)); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Base/Messages/ViewModelListBaseSelectionsChanged.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging.Messages; 2 | using System.Collections.Generic; 3 | 4 | namespace MvvmScarletToolkit.Observables 5 | { 6 | public sealed class ViewModelListBaseSelectionsChanged : ValueChangedMessage> 7 | { 8 | public ViewModelListBaseSelectionsChanged(in IEnumerable content) 9 | : base(content) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Base/Rx/INotifyRefreshRequired.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public interface INotifyRefreshRequired 7 | { 8 | IObservable GetObservable(); 9 | 10 | void Notify(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Base/Rx/NotifyRefreshRequiredAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive; 3 | using System.Reactive.Linq; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// Adapter class around 9 | /// 10 | public sealed class NotifyRefreshRequiredAdapter : INotifyRefreshRequired 11 | { 12 | private event EventHandler? RefreshRequired; 13 | 14 | public void Notify() 15 | { 16 | RefreshRequired?.Invoke(this, Unit.Default); 17 | } 18 | 19 | public IObservable GetObservable() 20 | { 21 | return Observable 22 | .FromEventPattern, Unit>(e => RefreshRequired += e, e => RefreshRequired -= e) 23 | .Select(x => x.EventArgs); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Base/ViewModelBase_generic.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit.Observables 4 | { 5 | /// 6 | /// Generic version of exposing an injected (model)class 7 | /// 8 | public abstract class ViewModelBase : ViewModelBase 9 | where TModel : class 10 | { 11 | private TModel? _model; 12 | [Bindable(true, BindingDirection.OneWay)] 13 | public TModel? Model 14 | { 15 | get { return _model; } 16 | protected set { SetProperty(ref _model, value); } 17 | } 18 | 19 | protected ViewModelBase(in IScarletCommandBuilder commandBuilder) 20 | : base(commandBuilder) 21 | { 22 | } 23 | 24 | protected ViewModelBase(in IScarletCommandBuilder commandBuilder, in TModel model) 25 | : this(commandBuilder) 26 | { 27 | _model = model; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/EnumViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace MvvmScarletToolkit.Observables 5 | { 6 | public sealed class EnumViewModel 7 | { 8 | public static EnumViewModel Create(in TEnum value) 9 | where TEnum : Enum 10 | { 11 | return new EnumViewModel(value, value.GetAttributeOfType()?.Description); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/EnumViewModel_generic.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System; 3 | 4 | namespace MvvmScarletToolkit.Observables 5 | { 6 | public partial class EnumViewModel : ViewModelContainer 7 | where T : Enum 8 | { 9 | [ObservableProperty] 10 | private string _displayName; 11 | 12 | public EnumViewModel(in T value, in string? displayName) 13 | : base(value) 14 | { 15 | _displayName = displayName ?? "Undefined"; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Localization/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using System.ComponentModel; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public static class ServiceExtensions 7 | { 8 | public static ILocalizationViewModel CreateViewModel(this ILocalizationService localizationService, in IScarletEventManager weakEventManager, in string key) 9 | { 10 | return new LocalizationViewModel(weakEventManager, localizationService, key); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Localization/samples.md: -------------------------------------------------------------------------------- 1 | [back](../../readme.md) 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Navigation/Scene.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System; 3 | 4 | namespace MvvmScarletToolkit.Observables 5 | { 6 | public partial class Scene : ObservableObject 7 | { 8 | [ObservableProperty] 9 | private object? _content; 10 | 11 | [ObservableProperty] 12 | private bool _isSelected; 13 | 14 | [ObservableProperty] 15 | public int _sequence; 16 | 17 | public ILocalizationViewModel Localization { get; } 18 | 19 | public Scene(in ILocalizationViewModel localizationViewModel) 20 | { 21 | Localization = localizationViewModel ?? throw new ArgumentNullException(nameof(localizationViewModel)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Navigation/Scenes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MvvmScarletToolkit.Observables 5 | { 6 | public abstract class Scenes : ViewModelListBase 7 | { 8 | private readonly LocalizationsViewModel _localizationsViewModel; 9 | private readonly List _disposeables; 10 | 11 | private bool _disposed; 12 | 13 | protected Scenes(in IScarletCommandBuilder commandBuilder, in LocalizationsViewModel localizationsViewModel) 14 | : base(commandBuilder) 15 | { 16 | _localizationsViewModel = localizationsViewModel ?? throw new ArgumentNullException(nameof(LocalizationsViewModel)); 17 | _disposeables = new List(); 18 | } 19 | 20 | protected void Add(string key, object content) 21 | { 22 | var localization = _localizationsViewModel.CreateViewModel(WeakEventManager, key); 23 | _disposeables.Add(localization); 24 | 25 | var viewmodel = new Scene(localization) 26 | { 27 | Content = content, 28 | Sequence = Items.Count, 29 | }; 30 | 31 | _items.Add(viewmodel); 32 | } 33 | 34 | protected override void Dispose(bool disposing) 35 | { 36 | if (_disposed) 37 | { 38 | return; 39 | } 40 | 41 | if (disposing) 42 | { 43 | _items.Clear(); 44 | 45 | for (var i = 0; i < _disposeables.Count; i++) 46 | { 47 | _disposeables[i].Dispose(); 48 | _disposeables.Clear(); 49 | } 50 | } 51 | 52 | base.Dispose(disposing); 53 | _disposed = true; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Navigation/samples.md: -------------------------------------------------------------------------------- 1 | [back](../../readme.md) 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/Paging/IPagedDataProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | public interface IPagedDataProvider 9 | where TViewModel : class, INotifyPropertyChanged 10 | { 11 | Task> Get(int index, int count, CancellationToken token); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/State/BusyStack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | 5 | namespace MvvmScarletToolkit.Observables 6 | { 7 | /// 8 | /// Will notify its owner via a provided action on if it contains more tokens 9 | /// 10 | public sealed class BusyStack : IBusyStack 11 | { 12 | private readonly Action _onChanged; 13 | 14 | private int _items; 15 | 16 | public BusyStack(in Action onChanged) 17 | { 18 | _onChanged = onChanged ?? throw new ArgumentNullException(nameof(onChanged)); 19 | } 20 | 21 | public void Pull() 22 | { 23 | var oldValue = _items > 0; 24 | Interlocked.Decrement(ref _items); 25 | var newValue = _items > 0; 26 | 27 | if (oldValue.Equals(newValue)) 28 | { 29 | return; 30 | } 31 | 32 | InvokeOnChanged(newValue); 33 | } 34 | 35 | public void Push() 36 | { 37 | var oldValue = _items > 0; 38 | Interlocked.Increment(ref _items); 39 | var newValue = _items > 0; 40 | 41 | if (oldValue.Equals(newValue)) 42 | { 43 | return; 44 | } 45 | 46 | InvokeOnChanged(newValue); 47 | } 48 | 49 | /// 50 | /// Returns a new thats associated with instance of a 51 | /// 52 | /// a new 53 | [DebuggerStepThrough] 54 | public IDisposable GetToken() 55 | { 56 | return new BusyToken(this); 57 | } 58 | 59 | [DebuggerStepThrough] 60 | private void InvokeOnChanged(bool newValue) 61 | { 62 | _onChanged(newValue); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/State/BusyToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit.Observables 4 | { 5 | public struct BusyToken : IDisposable 6 | { 7 | private readonly IBusyStack _busyStack; 8 | 9 | private bool _disposed; 10 | 11 | public BusyToken(in IBusyStack busyStack) 12 | { 13 | _busyStack = busyStack ?? throw new ArgumentNullException(nameof(busyStack)); 14 | _disposed = false; 15 | 16 | busyStack.Push(); 17 | } 18 | 19 | public void Dispose() 20 | { 21 | if (_disposed) 22 | { 23 | return; 24 | } 25 | 26 | _busyStack.Pull(); 27 | _disposed = true; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/State/DisposalToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | 4 | namespace MvvmScarletToolkit.Observables 5 | { 6 | public sealed class DisposalToken : IDisposable 7 | { 8 | private readonly ConcurrentDictionary, object> _observerCollection; 9 | private readonly IObserver _observer; 10 | 11 | private bool _disposed; 12 | 13 | public DisposalToken(in IObserver observer, in ConcurrentDictionary, object> observerCollection) 14 | { 15 | _observerCollection = observerCollection ?? throw new ArgumentNullException(nameof(observerCollection)); 16 | _observer = observer ?? throw new ArgumentNullException(nameof(observer)); 17 | 18 | #pragma warning disable CS8603 // Possible null reference return. 19 | _ = _observerCollection.AddOrUpdate(observer, addValueFactory: _ => default, updateValueFactory: (_, __) => default); 20 | #pragma warning restore CS8603 // Possible null reference return. 21 | } 22 | 23 | public void Dispose() 24 | { 25 | if (_disposed) 26 | { 27 | return; 28 | } 29 | 30 | _observerCollection.TryRemove(_observer, out _); 31 | _disposed = true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/State/samples.md: -------------------------------------------------------------------------------- 1 | [back](../../readme.md) 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/VersionViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System.Collections.Generic; 3 | 4 | namespace MvvmScarletToolkit.Observables 5 | { 6 | /// 7 | /// Helper viewmodel for tracking changes 8 | /// 9 | /// literally any c# object 10 | public class VersionViewModel : ObservableObject 11 | { 12 | protected bool HasChanged { get; private set; } 13 | 14 | /// 15 | /// original value 16 | /// 17 | public T Default { get; } 18 | 19 | private T _current; 20 | /// 21 | /// new value 22 | /// 23 | public virtual T Current 24 | { 25 | get { return _current; } 26 | set 27 | { 28 | if (SetProperty(ref _current, value)) 29 | { 30 | if (!EqualityComparer.Default.Equals(_current, Default)) 31 | { 32 | HasChanged = true; 33 | } 34 | 35 | OnPropertyChanged(nameof(CurrentOrDefault)); 36 | } 37 | } 38 | } 39 | 40 | public T CurrentOrDefault => HasChanged ? Current : Default; 41 | 42 | #pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 43 | 44 | public VersionViewModel(in T defaultValue) 45 | { 46 | Default = defaultValue; 47 | } 48 | 49 | public VersionViewModel(in T defaultValue, in T current) 50 | { 51 | Default = defaultValue; 52 | Current = current; 53 | } 54 | 55 | #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Observables/ViewModels/ViewModelContainer.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | /// 6 | /// Generic wrapper viewmodel to add binding support to any c# object 7 | /// 8 | /// 9 | public partial class ViewModelContainer : ObservableObject 10 | { 11 | [ObservableProperty] 12 | private T _value; 13 | 14 | public ViewModelContainer(in T value) 15 | { 16 | _value = value; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Jot; 2 | using Jot.Storage; 3 | using MvvmScarletToolkit.Observables; 4 | using System; 5 | using System.Runtime.Versioning; 6 | using System.Threading; 7 | using System.Windows; 8 | 9 | namespace MvvmScarletToolkit.Wpf.Samples 10 | { 11 | [SupportedOSPlatform("windows7.0")] 12 | public partial class App : Application 13 | { 14 | private readonly Tracker _tracker; 15 | 16 | public App() 17 | { 18 | _tracker = new Tracker(new JsonFileStore(Environment.SpecialFolder.ApplicationData)); 19 | } 20 | 21 | protected override void OnStartup(StartupEventArgs e) 22 | { 23 | base.OnStartup(e); 24 | 25 | _tracker.Configure() 26 | .Id(w => w.Name, $"[Width={SystemParameters.VirtualScreenWidth},Height{SystemParameters.VirtualScreenHeight}]") 27 | .Properties(w => new { w.Height, w.Width, w.Left, w.Top, w.WindowState }) 28 | .PersistOn(nameof(Window.Closing)) 29 | .StopTrackingOn(nameof(Window.Closing)); 30 | 31 | var navigation = new NavigationViewModel(SynchronizationContext.Current, ScarletCommandBuilder.Default, new LocalizationsViewModel(new ScarletLocalizationProvider())); 32 | 33 | var window = new MainWindow(_tracker, navigation); 34 | 35 | window.Show(); 36 | } 37 | 38 | protected override async void OnExit(ExitEventArgs e) 39 | { 40 | base.OnExit(e); 41 | 42 | await ScarletExitService.Default.ShutDown().ConfigureAwait(false); 43 | 44 | _tracker.PersistAll(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/AsyncState/AsyncStateViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MvvmScarletToolkit.Observables; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace MvvmScarletToolkit.Wpf.Samples 7 | { 8 | public sealed partial class AsyncStateViewModel : BusinessViewModelBase 9 | { 10 | [ObservableProperty] 11 | private string _displayName; 12 | 13 | [ObservableProperty] 14 | private bool _isSelected; 15 | 16 | public AsyncStateViewModel(IScarletCommandBuilder commandBuilder) 17 | : base(commandBuilder) 18 | { 19 | DisplayName = "unknown"; 20 | } 21 | 22 | public AsyncStateViewModel(IScarletCommandBuilder commandBuilder, string displayName) 23 | : this(commandBuilder) 24 | { 25 | DisplayName = displayName; 26 | } 27 | 28 | protected override Task UnloadInternal(CancellationToken token) 29 | { 30 | return Task.Delay(2000, token); 31 | } 32 | 33 | protected override Task RefreshInternal(CancellationToken token) 34 | { 35 | return Task.Delay(2000, token); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/ContextMenu/ContextMenuViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace MvvmScarletToolkit.Wpf.Samples 5 | { 6 | public sealed partial class ContextMenuViewModel : ObservableObject 7 | { 8 | public string Name { get; } = "Test"; 9 | public ObservableCollection Items { get; } 10 | 11 | public ContextMenuViewModel() 12 | { 13 | Items = new ObservableCollection(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/ContextMenu/ContextMenuViewModels.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace MvvmScarletToolkit.Wpf.Samples 5 | { 6 | public sealed partial class ContextMenuViewModels : ObservableObject 7 | { 8 | public ObservableCollection Items { get; } 9 | 10 | public ContextMenuViewModels() 11 | { 12 | Items = new ObservableCollection() 13 | { 14 | new ContextMenuViewModel(), 15 | }; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/DataGrid/DataGridDataProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace MvvmScarletToolkit.Wpf.Samples 8 | { 9 | public sealed class DataGridDataProvider : IPagedDataProvider 10 | { 11 | private readonly IScarletCommandBuilder _scarletCommandBuilder; 12 | private readonly List _cache; 13 | 14 | public DataGridDataProvider(IScarletCommandBuilder scarletCommandBuilder, int pageCount, int pageSize) 15 | { 16 | _cache = new List(pageCount * pageSize); 17 | _scarletCommandBuilder = scarletCommandBuilder ?? throw new ArgumentNullException(nameof(scarletCommandBuilder)); 18 | 19 | var page = 1; 20 | for (var i = 0; i < pageCount * pageSize; i++) 21 | { 22 | if (i % pageSize == 0) 23 | { 24 | page++; 25 | } 26 | _cache.Add(new DataGridRowViewModel(_scarletCommandBuilder, page) 27 | { 28 | Id = i, 29 | CreatedOn = DateTime.Now, 30 | Name = Guid.NewGuid().ToString(), 31 | Color = $"#cc{i * 2:X2}{i * 3:X2}", 32 | }); 33 | } 34 | } 35 | 36 | public Task> Get(int index, int count, CancellationToken token) 37 | { 38 | return Task.FromResult>(_cache.AsQueryable().TryPage(index, count).ToList()); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/DialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using System.Windows.Input; 6 | 7 | namespace MvvmScarletToolkit.Wpf.Samples 8 | { 9 | public class DialogViewModel : ViewModelBase 10 | { 11 | public ICommand RunCommand { get; } 12 | 13 | public BoolDialogResultViewModel DialogResult { get; } 14 | 15 | public DialogViewModel(IScarletCommandBuilder commandBuilder) 16 | : base(commandBuilder) 17 | { 18 | RunCommand = CommandBuilder.Create(Run, CanRun) 19 | .WithBusyNotification(BusyStack) 20 | .WithSingleExecution() 21 | .WithAsyncCancellation() 22 | .Build(); 23 | 24 | DialogResult = new BoolDialogResultViewModel(commandBuilder); 25 | } 26 | 27 | private async Task Run(CancellationToken token) 28 | { 29 | using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); 30 | using var linked = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, token); 31 | await DialogResult.Show(linked.Token).ConfigureAwait(false); 32 | } 33 | 34 | private bool CanRun() 35 | { 36 | return !IsBusy; 37 | } 38 | 39 | protected override void Dispose(bool disposing) 40 | { 41 | if (disposing) 42 | { 43 | DialogResult.Dispose(); 44 | } 45 | 46 | base.Dispose(disposing); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Enums/EnumViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Samples 4 | { 5 | public sealed partial class EnumViewModel : ObservableObject 6 | { 7 | [ObservableProperty] 8 | private ViewModelEnum _value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Enums/ViewModelEnum.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit.Wpf.Samples 2 | { 3 | public enum ViewModelEnum 4 | { 5 | None, 6 | SomeValue, 7 | AnotherValue 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/FormViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Samples 4 | { 5 | public sealed partial class FormViewModel : ObservableObject 6 | { 7 | [ObservableProperty] 8 | private string _maxLengthInput; 9 | 10 | [ObservableProperty] 11 | private int _maxLength; 12 | 13 | [ObservableProperty] 14 | private string _regex; 15 | 16 | partial void OnMaxLengthInputChanged(string value) 17 | { 18 | if (int.TryParse(value, out var maxLength)) 19 | { 20 | MaxLength = maxLength; 21 | } 22 | } 23 | 24 | public FormViewModel() 25 | { 26 | Regex = "^\\d+$"; 27 | MaxLengthInput = "10"; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Geometry/GeometryContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Media; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Samples 4 | { 5 | public sealed class GeometryContainer : ViewModelContainer 6 | { 7 | public GeometryContainer(Geometry value) 8 | : base(value) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Geometry/GeometryRenderViewModel.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Media; 9 | 10 | namespace MvvmScarletToolkit.Wpf.Samples 11 | { 12 | public sealed class GeometryRenderViewModel : BusinessViewModelListBase 13 | { 14 | private readonly Typeface _typeface; 15 | private readonly NumberSubstitution _numberSubstitution; 16 | 17 | private readonly IEnumerable _geomtries; 18 | 19 | public GeometryRenderViewModel(IScarletCommandBuilder commandBuilder) 20 | : base(commandBuilder) 21 | { 22 | _typeface = new Typeface("Tahoma"); 23 | _numberSubstitution = new NumberSubstitution(); 24 | 25 | _geomtries = Enumerable.Range(0, 5000) 26 | .Select(c => (c % (97 - 32)) + 32) 27 | .Select(c => (char)c) 28 | .Select(c => new GeometryContainer(BuildGeometry(new string(c, 1), _typeface, _numberSubstitution))) 29 | .ToArray(); 30 | } 31 | 32 | private static Geometry BuildGeometry(string charachters, Typeface typeface, NumberSubstitution numberSubstitution) 33 | { 34 | var result = new FormattedText(charachters, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, 24, Brushes.Black, numberSubstitution, TextFormattingMode.Display, 90) 35 | .BuildGeometry(new Point(9, 9)); 36 | 37 | result.Freeze(); 38 | 39 | return result; 40 | } 41 | 42 | protected override Task RefreshInternal(CancellationToken token) 43 | { 44 | return AddRange(_geomtries, token); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Image/ImageFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | 4 | namespace MvvmScarletToolkit.Wpf.Samples 5 | { 6 | public class ImageFactory 7 | { 8 | private readonly IScarletCommandBuilder _commandBuilder; 9 | 10 | public ImageFactory(IScarletCommandBuilder commandBuilder) 11 | { 12 | _commandBuilder = commandBuilder ?? throw new System.ArgumentNullException(nameof(commandBuilder)); 13 | } 14 | 15 | public IEnumerable GetImageList() 16 | { 17 | var assembly = typeof(ImageFactory).Assembly; 18 | 19 | var index = 0; 20 | foreach (var name in assembly.GetManifestResourceNames()) 21 | { 22 | if (name.EndsWith(".jpg")) 23 | { 24 | yield return new Image(_commandBuilder, assembly) 25 | { 26 | IsSelected = index == 0, 27 | DisplayName = Path.GetFileName(name), 28 | Path = name, 29 | Sequence = index, 30 | }; 31 | 32 | index++; 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Image/Images.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace MvvmScarletToolkit.Wpf.Samples 6 | { 7 | public class Images : BusinessViewModelListBase 8 | { 9 | private readonly ImageFactory _imageFactory; 10 | 11 | public Images(IScarletCommandBuilder commandBuilder, ImageFactory imageFactory) 12 | : base(commandBuilder) 13 | { 14 | _imageFactory = imageFactory ?? throw new System.ArgumentNullException(nameof(imageFactory)); 15 | } 16 | 17 | protected override Task RefreshInternal(CancellationToken token) 18 | { 19 | return AddRange(_imageFactory.GetImageList(), token); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/ObservableDictionaryViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using MvvmScarletToolkit.Observables; 4 | using System; 5 | using System.Linq; 6 | 7 | namespace MvvmScarletToolkit.Wpf.Samples 8 | { 9 | internal sealed partial class ObservableDictionaryViewModel : ObservableObject 10 | { 11 | public ObservableDictionary Items { get; } 12 | 13 | public ObservableDictionaryViewModel() 14 | { 15 | Items = new ObservableDictionary() 16 | { 17 | [0] = Guid.NewGuid(), 18 | [1] = Guid.NewGuid(), 19 | [2] = Guid.NewGuid(), 20 | [3] = Guid.NewGuid(), 21 | [4] = Guid.NewGuid(), 22 | }; 23 | } 24 | 25 | [RelayCommand] 26 | private void Add() 27 | { 28 | if (Items.Count > 0) 29 | { 30 | Items.Add(Items.MaxBy(p => p.Key).Key + 1, Guid.NewGuid()); 31 | } 32 | else 33 | { 34 | Items.Add(0, Guid.NewGuid()); 35 | } 36 | } 37 | 38 | [RelayCommand(CanExecute = nameof(CanRemove))] 39 | private void Remove() 40 | { 41 | Items.Remove(Items.First()); 42 | } 43 | 44 | private bool CanRemove() 45 | { 46 | return Items.Count > 0; 47 | } 48 | 49 | [RelayCommand] 50 | private void Clear() 51 | { 52 | Items.Clear(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/PasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Samples 4 | { 5 | public sealed partial class PasswordViewModel : ObservableObject 6 | { 7 | [ObservableProperty] 8 | private string _password; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Process/ProcessData.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System; 3 | 4 | namespace MvvmScarletToolkit.Wpf.Samples 5 | { 6 | public sealed partial class ProcessData : ObservableObject 7 | { 8 | public string Message { get; } 9 | public DateTime TimeStamp { get; } 10 | 11 | public ProcessData(string message, DateTime timeStamp) 12 | { 13 | Message = message; 14 | TimeStamp = timeStamp; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Process/ProcessErrorData.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System; 3 | 4 | namespace MvvmScarletToolkit.Wpf.Samples 5 | { 6 | public sealed partial class ProcessErrorData : ObservableObject 7 | { 8 | public string Message { get; } 9 | public DateTime TimeStamp { get; } 10 | 11 | public ProcessErrorData(string message, DateTime timeStamp) 12 | { 13 | Message = message; 14 | TimeStamp = timeStamp; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Process/ProcessingImagesViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Samples 4 | { 5 | public partial class ProcessingImagesViewModel : ObservableObject 6 | { 7 | private Images _source; 8 | public Images Source 9 | { 10 | get { return _source; } 11 | private set { SetProperty(ref _source, value); } 12 | } 13 | 14 | private Images _target; 15 | public Images Target 16 | { 17 | get { return _target; } 18 | private set { SetProperty(ref _target, value); } 19 | } 20 | 21 | public ProcessingImagesViewModel(IScarletCommandBuilder commandBuilder, ImageFactory imageFactory) 22 | { 23 | Source = new Images(commandBuilder, imageFactory); 24 | Target = new Images(commandBuilder, imageFactory); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/ToastsViewmodel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MvvmScarletToolkit.Observables; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using System.Windows.Input; 6 | 7 | namespace MvvmScarletToolkit.Wpf.Samples 8 | { 9 | public sealed partial class ToastsViewModel : ViewModelBase 10 | { 11 | public ICommand ShowToastCommand { get; } 12 | public ICommand ShowManyToastsCommand { get; } 13 | 14 | [ObservableProperty] 15 | private ToastType _toastType; 16 | 17 | [ObservableProperty] 18 | private string _title; 19 | 20 | [ObservableProperty] 21 | private string _body; 22 | 23 | [ObservableProperty] 24 | private bool _persist; 25 | 26 | public ToastsViewModel(IScarletCommandBuilder commandBuilder) 27 | : base(commandBuilder) 28 | { 29 | ShowToastCommand = commandBuilder 30 | .Create(ShowToastImpl) 31 | .Build(); 32 | 33 | ShowManyToastsCommand = commandBuilder 34 | .Create(ShowManyToastsImpl) 35 | .Build(); 36 | 37 | _title = "Toast-Title"; 38 | _body = "Toast-Body"; 39 | _toastType = ToastType.Success; 40 | } 41 | 42 | private Task ShowToastImpl(CancellationToken token) 43 | { 44 | ToastService.Default.Show(Title, Body, ToastType, Persist); 45 | 46 | return Task.CompletedTask; 47 | } 48 | 49 | private Task ShowManyToastsImpl(CancellationToken token) 50 | { 51 | for (var i = 0; i < 100; i++) 52 | { 53 | ToastService.Default.Show(Title, Body, ToastType, Persist); 54 | } 55 | 56 | return Task.CompletedTask; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Features/Virtualization/DataEntryViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MvvmScarletToolkit.Observables; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace MvvmScarletToolkit.Wpf.Samples 8 | { 9 | public sealed partial class DataEntryViewModel : BusinessViewModelBase 10 | { 11 | [ObservableProperty] 12 | private string _message; 13 | 14 | [ObservableProperty] 15 | private DateTime _createdOn; 16 | 17 | [ObservableProperty] 18 | private Guid _id; 19 | 20 | [ObservableProperty] 21 | private bool _isSelected; 22 | 23 | public DataEntryViewModel(IScarletCommandBuilder commandBuilder) 24 | : base(commandBuilder) 25 | { 26 | Id = Guid.NewGuid(); 27 | } 28 | 29 | protected override Task UnloadInternal(CancellationToken token) 30 | { 31 | CreatedOn = DateTime.MinValue; 32 | Message = ""; 33 | 34 | return Task.CompletedTask; 35 | } 36 | 37 | protected override Task RefreshInternal(CancellationToken token) 38 | { 39 | CreatedOn = DateTime.UtcNow; 40 | Message = CreatedOn.GetHashCode().ToString(); 41 | 42 | return Task.CompletedTask; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using Jot; 2 | using System; 3 | using System.Windows; 4 | 5 | namespace MvvmScarletToolkit.Wpf.Samples 6 | { 7 | public partial class MainWindow 8 | { 9 | public Tracker Tracker 10 | { 11 | get { return (Tracker)GetValue(TrackerProperty); } 12 | set { SetValue(TrackerProperty, value); } 13 | } 14 | 15 | public static readonly DependencyProperty TrackerProperty = DependencyProperty.Register( 16 | nameof(Tracker), 17 | typeof(Tracker), 18 | typeof(MainWindow), 19 | new PropertyMetadata(null)); 20 | 21 | public MainWindow(Tracker tracker, NavigationViewModel navigationViewModel) 22 | { 23 | ArgumentNullException.ThrowIfNull(tracker, nameof(tracker)); 24 | ArgumentNullException.ThrowIfNull(navigationViewModel, nameof(navigationViewModel)); 25 | 26 | InitializeComponent(); 27 | 28 | tracker.Track(this); 29 | 30 | SetCurrentValue(TrackerProperty, tracker); 31 | 32 | DataContext = navigationViewModel; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MvvmScarletToolkit.Wpf.Samples")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCulture("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | //In order to begin building localizable applications, set 19 | //CultureYouAreCodingWith in your .csproj file 20 | //inside a . For example, if you are using US english 21 | //in your source files, set the to en-US. Then uncomment 22 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 23 | //the line below to match the UICulture setting in the project file. 24 | 25 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 26 | 27 | [assembly: ThemeInfo( 28 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 29 | //(used if a resource is not found in the page, 30 | // or application resource dictionaries) 31 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 32 | //(used if a resource is not found in the page, 33 | // app, or any theme specific resource dictionaries) 34 | )] 35 | [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows7.0")] 36 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Release 8 | Any CPU 9 | bin\Release\net6.0-windows\win10-x64\publish\win10-x64\ 10 | FileSystem 11 | net6.0-windows 12 | win10-x64 13 | true 14 | True 15 | true 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Resources/Death_to_Stock_Photography_RideorDie_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insire/MvvmScarletToolkit/c2cfa12e4d4f3e1b352ed3c1b32efe77858c0800/src/MvvmScarletToolkit.Wpf.Samples/Resources/Death_to_Stock_Photography_RideorDie_8.jpg -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Resources/Moon_Color_Hypersaturated_Stars_900.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insire/MvvmScarletToolkit/c2cfa12e4d4f3e1b352ed3c1b32efe77858c0800/src/MvvmScarletToolkit.Wpf.Samples/Resources/Moon_Color_Hypersaturated_Stars_900.jpg -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Resources/wallhaven-245035.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insire/MvvmScarletToolkit/c2cfa12e4d4f3e1b352ed3c1b32efe77858c0800/src/MvvmScarletToolkit.Wpf.Samples/Resources/wallhaven-245035.jpg -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Resources/wallhaven-319605.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insire/MvvmScarletToolkit/c2cfa12e4d4f3e1b352ed3c1b32efe77858c0800/src/MvvmScarletToolkit.Wpf.Samples/Resources/wallhaven-319605.jpg -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Resources/wallhaven-401406.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insire/MvvmScarletToolkit/c2cfa12e4d4f3e1b352ed3c1b32efe77858c0800/src/MvvmScarletToolkit.Wpf.Samples/Resources/wallhaven-401406.jpg -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Samples/Resources/wallhaven-75354.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insire/MvvmScarletToolkit/c2cfa12e4d4f3e1b352ed3c1b32efe77858c0800/src/MvvmScarletToolkit.Wpf.Samples/Resources/wallhaven-75354.jpg -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/IsNotNullOrEmptyTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Tests 4 | { 5 | public sealed class IsNotNullOrEmptyTests 6 | { 7 | [Test] 8 | public void Convert_Should_Return_True_For_Unsupported_DataType() 9 | { 10 | var converter = new IsNotNullOrEmpty(); 11 | 12 | Assert.Multiple(() => 13 | { 14 | Assert.That(converter.Convert(new object(), null, null, null), Is.EqualTo(true)); 15 | Assert.That(converter.Convert(1, null, null, null), Is.EqualTo(true)); 16 | Assert.That(converter.Convert("not null", null, null, null), Is.EqualTo(true)); 17 | }); 18 | } 19 | 20 | [Test] 21 | public void Convert_Should_Return_False_For_NullOrEmpty() 22 | { 23 | var converter = new IsNotNullOrEmpty(); 24 | 25 | Assert.Multiple(() => 26 | { 27 | Assert.That(converter.Convert(null, null, null, null), Is.EqualTo(false)); 28 | Assert.That(converter.Convert(string.Empty, null, null, null), Is.EqualTo(false)); 29 | }); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/IsNotNullOrWhiteSpaceTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Tests 4 | { 5 | public sealed class IsNotNullOrWhiteSpaceTests 6 | { 7 | [Test] 8 | public void Convert_Should_Return_False_For_Unsupported_DataType() 9 | { 10 | var converter = new IsNotNullOrWhiteSpace(); 11 | 12 | Assert.Multiple(() => 13 | { 14 | Assert.That(converter.Convert(1, null, null, null), Is.EqualTo(false)); 15 | Assert.That(converter.Convert(new object(), null, null, null), Is.EqualTo(false)); 16 | }); 17 | } 18 | 19 | [Test] 20 | public void Convert_Should_Return_True_For_NullOrWhiteSpacey() 21 | { 22 | var converter = new IsNotNullOrWhiteSpace(); 23 | 24 | Assert.Multiple(() => 25 | { 26 | Assert.That(converter.Convert(null, null, null, null), Is.EqualTo(false)); 27 | Assert.That(converter.Convert(string.Empty, null, null, null), Is.EqualTo(false)); 28 | Assert.That(converter.Convert(" ", null, null, null), Is.EqualTo(false)); 29 | }); 30 | } 31 | 32 | [Test] 33 | public void Convert_Should_Return_True_For_NonNullOrWhiteSpace() 34 | { 35 | var converter = new IsNotNullOrWhiteSpace(); 36 | 37 | Assert.That(converter.Convert("not null", null, null, null), Is.EqualTo(true)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/IsNotNullTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Tests 4 | { 5 | public sealed class IsNotNullTests 6 | { 7 | [Test] 8 | public void Convert_Should_Return_True_For_Anything_But_Null() 9 | { 10 | var converter = new IsNotNull(); 11 | 12 | Assert.Multiple(() => 13 | { 14 | Assert.That(converter.Convert(new object(), null, null, null), Is.EqualTo(true)); 15 | Assert.That(converter.Convert(1, null, null, null), Is.EqualTo(true)); 16 | }); 17 | } 18 | 19 | [Test] 20 | public void Convert_Should_Return_False_Null() 21 | { 22 | var converter = new IsNotNull(); 23 | 24 | Assert.That(converter.Convert(null, null, null, null), Is.EqualTo(false)); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/IsNotTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Tests 4 | { 5 | public sealed class IsNotTests 6 | { 7 | [Test] 8 | public void Convert_Should_Return_False_For_Unsupported_DataType() 9 | { 10 | var converter = new IsNot(); 11 | 12 | Assert.Multiple(() => 13 | { 14 | Assert.That(converter.Convert(new object(), null, null, null), Is.EqualTo(false)); 15 | Assert.That(converter.Convert(null, null, null, null), Is.EqualTo(false)); 16 | Assert.That(converter.Convert(1, null, null, null), Is.EqualTo(false)); 17 | }); 18 | } 19 | 20 | [Test] 21 | public void Convert_Should_Return_True_For_False() 22 | { 23 | var converter = new IsNot(); 24 | 25 | Assert.That(converter.Convert(false, null, null, null), Is.EqualTo(true)); 26 | } 27 | 28 | [Test] 29 | public void Convert_Should_Return_False_For_True() 30 | { 31 | var converter = new IsNot(); 32 | 33 | Assert.That(converter.Convert(false, null, null, null), Is.EqualTo(true)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/IsNullOrEmptyTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Tests 4 | { 5 | public sealed class IsNullOrEmptyTests 6 | { 7 | [Test] 8 | public void Convert_Should_Return_False_For_Unsupported_DataType() 9 | { 10 | var converter = new IsNullOrEmpty(); 11 | 12 | Assert.Multiple(() => 13 | { 14 | Assert.That(converter.Convert(new object(), null, null, null), Is.EqualTo(false)); 15 | Assert.That(converter.Convert(1, null, null, null), Is.EqualTo(false)); 16 | Assert.That(converter.Convert("not null", null, null, null), Is.EqualTo(false)); 17 | }); 18 | } 19 | 20 | [Test] 21 | public void Convert_Should_Return_True_For_NullOrEmpty() 22 | { 23 | var converter = new IsNullOrEmpty(); 24 | 25 | Assert.Multiple(() => 26 | { 27 | Assert.That(converter.Convert(null, null, null, null), Is.EqualTo(true)); 28 | Assert.That(converter.Convert(string.Empty, null, null, null), Is.EqualTo(true)); 29 | }); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/IsNullOrWhiteSpaceTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Tests 4 | { 5 | public sealed class IsNullOrWhiteSpaceTests 6 | { 7 | [Test] 8 | public void Convert_Should_Return_False_For_Unsupported_DataType() 9 | { 10 | var converter = new IsNullOrWhiteSpace(); 11 | 12 | Assert.Multiple(() => 13 | { 14 | Assert.That(converter.Convert(new object(), null, null, null), Is.EqualTo(false)); 15 | Assert.That(converter.Convert(1, null, null, null), Is.EqualTo(false)); 16 | Assert.That(converter.Convert("not null or white space", null, null, null), Is.EqualTo(false)); 17 | }); 18 | } 19 | 20 | [Test] 21 | public void Convert_Should_Return_True_For_NullOrEmpty() 22 | { 23 | var converter = new IsNullOrWhiteSpace(); 24 | 25 | Assert.Multiple(() => 26 | { 27 | Assert.That(converter.Convert(null, null, null, null), Is.EqualTo(true)); 28 | Assert.That(converter.Convert(string.Empty, null, null, null), Is.EqualTo(true)); 29 | Assert.That(converter.Convert(" ", null, null, null), Is.EqualTo(true)); 30 | }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/IsNullTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace MvvmScarletToolkit.Wpf.Tests 4 | { 5 | public sealed class IsNullTests 6 | { 7 | [Test] 8 | public void Convert_Should_Return_True_For_Anything_But_Null() 9 | { 10 | var converter = new IsNull(); 11 | 12 | Assert.Multiple(() => 13 | { 14 | Assert.That(converter.Convert(new object(), null, null, null), Is.EqualTo(false)); 15 | Assert.That(converter.Convert(1, null, null, null), Is.EqualTo(false)); 16 | }); 17 | } 18 | 19 | [Test] 20 | public void Convert_Should_Return_False_Null() 21 | { 22 | var converter = new IsNull(); 23 | 24 | Assert.That(converter.Convert(null, null, null, null), Is.EqualTo(true)); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows;net9.0-windows 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | #if NET5_0_OR_GREATER 5 | [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows7.0")] 6 | #endif 7 | 8 | // General Information about an assembly is controlled through the following 9 | // set of attributes. Change these attribute values to modify the information 10 | // associated with an assembly. 11 | [assembly: AssemblyTitle("MvvmScarletToolkit.Wpf.Tests")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The following GUID is for the ID of the typelib if this project is exposed to COM 17 | [assembly: Guid("b64b25eb-275b-4fbb-98a1-88c4f0035642")] 18 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/TestData/DerivedObjectViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | 3 | namespace MvvmScarletToolkit.Tests 4 | { 5 | internal sealed class DerivedObjectViewModelBase : ViewModelBase 6 | { 7 | public DerivedObjectViewModelBase(IScarletCommandBuilder commandBuilder, object model) 8 | : base(commandBuilder, model) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/TestData/DerivedViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using System; 3 | 4 | namespace MvvmScarletToolkit.Tests 5 | { 6 | internal sealed class DerivedViewModelBase : ViewModelBase 7 | { 8 | public DerivedViewModelBase(IScarletCommandBuilder commandBuilder) 9 | : base(commandBuilder) 10 | { 11 | } 12 | 13 | public void ValidateState(Action action) 14 | { 15 | using (BusyStack.GetToken()) 16 | { 17 | action?.Invoke(); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/TestData/DerivedViewModelListBase.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using System; 3 | 4 | namespace MvvmScarletToolkit.Tests 5 | { 6 | internal sealed class DerivedViewModelListBase : ViewModelListBase 7 | { 8 | public DerivedViewModelListBase(IScarletCommandBuilder commandBuilder) 9 | : base(commandBuilder) 10 | { 11 | } 12 | 13 | public void ValidateState(Action action) 14 | { 15 | using (BusyStack.GetToken()) 16 | { 17 | action?.Invoke(); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/Util/TestDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace MvvmScarletToolkit.Tests.Util 6 | { 7 | internal sealed class TestDispatcher : IScarletDispatcher 8 | { 9 | public Task Invoke(Action action, CancellationToken token) 10 | { 11 | action?.Invoke(); 12 | return Task.CompletedTask; 13 | } 14 | 15 | public Task Invoke(Func action, CancellationToken token) 16 | { 17 | return Task.FromResult(action.Invoke()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf.Tests/ViewModelBaseTests.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using MvvmScarletToolkit.Tests.Util; 3 | using NUnit.Framework; 4 | using System; 5 | 6 | namespace MvvmScarletToolkit.Tests 7 | { 8 | internal sealed class ViewModelBaseTests 9 | { 10 | [Test] 11 | public void Ctor_DoesNotAcceptNullArgument() 12 | { 13 | Assert.Throws(() => new DerivedViewModelBase(null)); 14 | } 15 | 16 | [Test] 17 | public void Ctor_DoesNotThrow() 18 | { 19 | new DerivedViewModelBase(Utils.GetTestCommandBuilder()); 20 | } 21 | 22 | [Test] 23 | public void Ctor_DoesNotThrowForNullModel() 24 | { 25 | new DerivedObjectViewModelBase(Utils.GetTestCommandBuilder(), null); 26 | } 27 | 28 | [Test] 29 | public void ShouldBeBusyWhenUsingBusyStack() 30 | { 31 | var dispatcher = new TestDispatcher(); 32 | var commandBuilder = new ScarletCommandBuilder(dispatcher, Utils.GetTestCommandManager(), Utils.GetTestExceptionHandler(), Utils.GetTestMessenger(), Utils.GetTestExitService(), Utils.GetTestEventManager(), (lambda) => new BusyStack(lambda)); 33 | 34 | var vm = new DerivedViewModelBase(commandBuilder); 35 | 36 | Assert.That(vm.IsBusy, Is.EqualTo(false)); 37 | vm.ValidateState(() => Assert.That(vm.IsBusy, Is.EqualTo(true))); 38 | Assert.That(vm.IsBusy, Is.EqualTo(false)); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Behaviors/SmoothProgressBehavior.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xaml.Behaviors; 2 | using System; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Media.Animation; 6 | 7 | namespace MvvmScarletToolkit.Wpf 8 | { 9 | /// 10 | /// Behavior that smoothes out values changes of a 11 | /// 12 | // usage: 13 | // 14 | // 15 | // 16 | public sealed class SmoothProgressBehavior : Behavior 17 | { 18 | public double Percentage 19 | { 20 | get { return (double)GetValue(PercentageProperty); } 21 | set { SetValue(PercentageProperty, value); } 22 | } 23 | 24 | public static readonly DependencyProperty PercentageProperty = DependencyProperty.Register( 25 | nameof(Percentage), 26 | typeof(double), 27 | typeof(SmoothProgressBehavior), 28 | new PropertyMetadata(0d, OnPercentageChanged)); 29 | 30 | private static void OnPercentageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 31 | { 32 | if (d is not SmoothProgressBehavior behavior) 33 | { 34 | return; 35 | } 36 | 37 | if (behavior.AssociatedObject is null) 38 | { 39 | return; 40 | } 41 | 42 | behavior.OnPercentageChanged((double)e.OldValue, (double)e.NewValue); 43 | } 44 | 45 | private void OnPercentageChanged(double oldValue, double newValue) 46 | { 47 | var animation = new DoubleAnimation(oldValue, newValue, new TimeSpan(0, 0, 0, 0, 250)) 48 | { 49 | EasingFunction = new QuadraticEase() 50 | { 51 | EasingMode = EasingMode.EaseInOut, 52 | } 53 | }; 54 | 55 | AssociatedObject.BeginAnimation(System.Windows.Controls.Primitives.RangeBase.ValueProperty, animation, HandoffBehavior.SnapshotAndReplace); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/BindingProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | // Source: http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ 6 | public sealed class BindingProxy : Freezable 7 | { 8 | /// Identifies the dependency property. 9 | public static readonly DependencyProperty DataProperty = DependencyProperty.Register( 10 | nameof(Data), 11 | typeof(object), 12 | typeof(BindingProxy), 13 | new UIPropertyMetadata(default)); 14 | 15 | protected override Freezable CreateInstanceCore() 16 | { 17 | return new BindingProxy(); 18 | } 19 | 20 | public object? Data 21 | { 22 | get { return GetValue(DataProperty); } 23 | set { SetValue(DataProperty, value); } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/DataTemplateSelectionManagement/DataTemplateCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Diagnostics; 4 | using System.Windows; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | /// 9 | /// Collection of DataTemplates, limited to one entry per 10 | /// 11 | public sealed class DataTemplateCollection : Collection 12 | { 13 | public DataTemplate? Find(Type type) 14 | { 15 | foreach (var candidate in Items) 16 | { 17 | if (Equals(candidate.DataType, type)) 18 | { 19 | return candidate; 20 | } 21 | } 22 | 23 | return null; 24 | } 25 | 26 | protected override void InsertItem(int index, DataTemplate item) 27 | { 28 | ValidateItem(item); 29 | base.InsertItem(index, item); 30 | } 31 | 32 | protected override void SetItem(int index, DataTemplate item) 33 | { 34 | ValidateItem(item); 35 | base.SetItem(index, item); 36 | } 37 | 38 | private void ValidateItem(DataTemplate item) 39 | { 40 | ArgumentNullException.ThrowIfNull(item, nameof(item)); 41 | 42 | if (item.DataType is null) 43 | { 44 | throw new InvalidOperationException("DataTemplate.DataType cannot be null."); 45 | } 46 | 47 | if (item.DataType is Type type) 48 | { 49 | if (Find(type) != null) 50 | { 51 | throw new InvalidOperationException("Template already added for type."); 52 | } 53 | } 54 | else 55 | { 56 | Debug.WriteLine($"{item.DataType.GetType().Name} is not supported in a {nameof(DataTemplateCollection)}"); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/DataTemplateSelectionManagement/TypeDataTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// A that will check for a fitting DataTemplate before falling back to WPFs default resource lookup system 9 | /// 10 | public sealed class TypeDataTemplateSelector : DataTemplateSelector 11 | { 12 | private DataTemplateCollection? _templates; 13 | public DataTemplateCollection Templates => _templates ??= new DataTemplateCollection(); 14 | 15 | public override DataTemplate? SelectTemplate(object item, DependencyObject container) 16 | { 17 | var type = item.GetType(); 18 | 19 | return Templates.FirstOrDefault(p => p.DataType.Equals(type)) ?? container.FindDataTemplateFor(type); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/Interfaces/IFileSystemChild.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public interface IFileSystemChild : IFileSystemInfo 6 | { 7 | DateTime? LastWriteTimeUtc { get; } 8 | DateTime? LastAccessTimeUtc { get; } 9 | DateTime? CreationTimeUtc { get; } 10 | bool IsHidden { get; } 11 | IFileSystemParent? Parent { get; } 12 | bool Exists { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/Interfaces/IFileSystemDirectory.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public interface IFileSystemDirectory : IFileSystemParent, IFileSystemChild; 4 | } 5 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/Interfaces/IFileSystemDrive.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public interface IFileSystemDrive : IFileSystemParent 6 | { 7 | string? DriveFormat { get; } 8 | DriveType DriveType { get; } 9 | bool IsReady { get; } 10 | long AvailableFreeSpace { get; } 11 | long TotalFreeSpace { get; } 12 | long TotalSize { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/Interfaces/IFileSystemFile.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public interface IFileSystemFile : IFileSystemInfo, IFileSystemChild; 4 | } 5 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/Interfaces/IFileSystemInfo.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public interface IFileSystemInfo : IVirtualizationViewModel 4 | { 5 | string Name { get; } 6 | string FullName { get; } 7 | 8 | bool IsSelected { get; set; } 9 | 10 | bool IsBusy { get; } 11 | bool IsContainer { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/Interfaces/IFileSystemParent.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public interface IFileSystemParent : IBusinessViewModelListBase, IFileSystemInfo; 4 | } 5 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/Interfaces/IFileSystemViewModelFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public interface IFileSystemViewModelFactory 8 | { 9 | Task> GetDrives(IReadOnlyCollection types, IReadOnlyCollection fileAttributes, IReadOnlyCollection folderAttributes); 10 | 11 | Task> GetDirectories(IFileSystemParent parent, IReadOnlyCollection fileAttributes, IReadOnlyCollection folderAttributes); 12 | 13 | Task> GetFiles(IFileSystemParent parent, IReadOnlyCollection fileAttributes); 14 | 15 | Task IsEmpty(IFileSystemParent parent); 16 | 17 | Task CanAccess(IFileSystemChild child); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/Interfaces/IMimeTypeResolver.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit 2 | { 3 | public interface IMimeTypeResolver 4 | { 5 | string? Get(IFileSystemFile fileInfo); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/MimeTypeResolvers/RegistryFileExtensionMimeTypeResolver.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System.IO; 3 | 4 | namespace MvvmScarletToolkit.Wpf.FileSystemBrowser 5 | { 6 | public sealed class RegistryFileExtensionMimeTypeResolver : IMimeTypeResolver 7 | { 8 | public string? Get(IFileSystemFile fileInfo) 9 | { 10 | if (!fileInfo.Exists) 11 | { 12 | return null; 13 | } 14 | 15 | var extension = Path.GetExtension(fileInfo.Name); 16 | 17 | using (var key = Registry.ClassesRoot.OpenSubKey(extension)) 18 | { 19 | if (key is null) 20 | { 21 | return null; 22 | } 23 | 24 | if (key.GetValue("Content Type") is null) 25 | { 26 | return null; 27 | } 28 | 29 | return key.GetValue("Content Type")?.ToString(); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/MimeTypeResolvers/UrlMonMimeTypeResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace MvvmScarletToolkit.Wpf.FileSystemBrowser 6 | { 7 | public sealed class UrlMonMimeTypeResolver : IMimeTypeResolver 8 | { 9 | public string? Get(IFileSystemFile fileInfo) 10 | { 11 | if (!fileInfo.Exists) 12 | { 13 | return null; 14 | } 15 | 16 | var byteBuffer = new byte[256]; 17 | 18 | using (var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) 19 | { 20 | if (fileStream.Length >= 256) 21 | { 22 | fileStream.ReadExactly(byteBuffer); 23 | } 24 | else 25 | { 26 | fileStream.Read(byteBuffer, 0, (int)fileStream.Length); 27 | } 28 | } 29 | 30 | FindMimeFromData(0, null, byteBuffer, 256, null, 0, out var MimeTypeNum, 0); 31 | 32 | var mimeTypePtr = new IntPtr(MimeTypeNum); 33 | var mimeTypeFromFile = Marshal.PtrToStringUni(mimeTypePtr); 34 | 35 | Marshal.FreeCoTaskMem(mimeTypePtr); 36 | 37 | if (string.IsNullOrEmpty(mimeTypeFromFile) 38 | || mimeTypeFromFile == "text/plain" 39 | || mimeTypeFromFile == "application/octet-stream") 40 | { 41 | return null; 42 | } 43 | 44 | return mimeTypeFromFile; 45 | } 46 | 47 | [DllImport("urlmon.dll", CharSet = CharSet.Auto)] 48 | private static extern uint FindMimeFromData( 49 | uint pBC, 50 | [MarshalAs(UnmanagedType.LPStr)] string? pwzUrl, 51 | [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer, 52 | uint cbSize, 53 | [MarshalAs(UnmanagedType.LPStr)] string? pwzMimeProposed, 54 | uint dwMimeFlags, 55 | out uint ppwzMimeOut, 56 | uint dwReserverd); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/ViewModels/FileSystemOptionsViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace MvvmScarletToolkit.Wpf.FileSystemBrowser 7 | { 8 | public sealed class FileSystemOptionsViewModel : ObservableObject 9 | { 10 | private static readonly Lazy _default; 11 | 12 | public static FileSystemOptionsViewModel Default => _default.Value; 13 | 14 | static FileSystemOptionsViewModel() 15 | { 16 | var driveTypes = new[] 17 | { 18 | DriveType.Fixed 19 | }; 20 | 21 | var fileAttributes = new[] 22 | { 23 | System.IO.FileAttributes.Archive, 24 | System.IO.FileAttributes.Archive | System.IO.FileAttributes.Normal, 25 | System.IO.FileAttributes.Archive | System.IO.FileAttributes.Compressed 26 | }; 27 | 28 | var folderAttributes = new[] 29 | { 30 | System.IO.FileAttributes.Directory, 31 | }; 32 | 33 | _default = new Lazy(() => new FileSystemOptionsViewModel(driveTypes, fileAttributes, folderAttributes)); 34 | } 35 | 36 | public IReadOnlyCollection DriveTypes { get; } 37 | public IReadOnlyCollection FileAttributes { get; } 38 | public IReadOnlyCollection FolderAttributes { get; } 39 | 40 | public FileSystemOptionsViewModel(IReadOnlyCollection driveTypes, IReadOnlyCollection fileAttributes, IReadOnlyCollection folderAttributes) 41 | { 42 | DriveTypes = driveTypes ?? throw new ArgumentNullException(nameof(driveTypes)); 43 | FileAttributes = fileAttributes ?? throw new ArgumentNullException(nameof(fileAttributes)); 44 | FolderAttributes = folderAttributes ?? throw new ArgumentNullException(nameof(folderAttributes)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/ViewModels/FileSystemViewModel.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace MvvmScarletToolkit.Wpf.FileSystemBrowser 8 | { 9 | public sealed class FileSystemViewModel : BusinessViewModelListBase 10 | { 11 | private readonly IFileSystemViewModelFactory _factory; 12 | 13 | [Bindable(true, BindingDirection.OneWay)] 14 | public FileSystemOptionsViewModel Options { get; } 15 | 16 | public FileSystemViewModel(IScarletCommandBuilder commandBuilder, IFileSystemViewModelFactory factory, FileSystemOptionsViewModel options) 17 | : base(commandBuilder) 18 | { 19 | _factory = factory ?? throw new ArgumentNullException(nameof(factory)); 20 | Options = options ?? throw new ArgumentNullException(nameof(options)); 21 | } 22 | 23 | protected override async Task RefreshInternal(CancellationToken token) 24 | { 25 | var drives = await _factory.GetDrives(Options.DriveTypes, Options.FileAttributes, Options.FolderAttributes).ConfigureAwait(false); 26 | 27 | await AddRange(drives, token).ConfigureAwait(false); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/FileSystemBrowser/readme.md: -------------------------------------------------------------------------------- 1 | # MvvmScarletToolkit.Wpf.FileSystemBrowser 2 | 3 | ## Goals 4 | 5 | This library aims to provide an easy to use and extend browser control for FileSystemAccess. 6 | 7 | ## Contents 8 | 9 | |class|summary|code| 10 | |---|---|---| 11 | |FileSystemBrowser|TODO|TODO| 12 | |FileSystemViewModel|TODO|TODO| 13 | |Generic.xaml|TODO|TODO| 14 | 15 | ## Licensing 16 | 17 | The FileSystemBrowser uses some icons from the [Font Awesome](https://fontawesome.com/) icon set. 18 | 19 | You can find the License [here](https://fontawesome.com/license). 20 | 21 | The icons being used are: 22 | 23 | - [File icon](https://fontawesome.com/icons/file?style=regular) 24 | - [Folder icon](https://fontawesome.com/icons/folder?style=regular) 25 | - [Drive icon](https://fontawesome.com/icons/hdd?style=regular) 26 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/GridViewColumnSizing/FixedColumn.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | public sealed class FixedColumn : LayoutColumn 7 | { 8 | public static readonly DependencyProperty WidthProperty = DependencyProperty.RegisterAttached( 9 | "Width", 10 | typeof(double), 11 | typeof(FixedColumn)); 12 | 13 | private FixedColumn() 14 | { 15 | } 16 | 17 | /// Helper for getting from . 18 | /// to read from. 19 | /// Width property value. 20 | [AttachedPropertyBrowsableForType(typeof(DependencyObject))] 21 | public static double GetWidth(DependencyObject obj) 22 | { 23 | return (double)obj.GetValue(WidthProperty); 24 | } 25 | 26 | /// Helper for setting on . 27 | /// to set on. 28 | /// Width property value. 29 | public static void SetWidth(DependencyObject obj, double width) 30 | { 31 | obj.SetValue(WidthProperty, width); 32 | } 33 | 34 | public static bool IsFixedColumn(GridViewColumn? column) 35 | { 36 | if (column is null) 37 | { 38 | return false; 39 | } 40 | 41 | return HasPropertyValue(column, WidthProperty); 42 | } 43 | 44 | public static double? GetFixedWidth(GridViewColumn column) 45 | { 46 | return GetColumnWidth(column, WidthProperty); 47 | } 48 | 49 | public static GridViewColumn ApplyWidth(GridViewColumn gridViewColumn, double width) 50 | { 51 | SetWidth(gridViewColumn, width); 52 | return gridViewColumn; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/GridViewColumnSizing/LayoutColumn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public abstract class LayoutColumn 8 | { 9 | protected static bool HasPropertyValue(GridViewColumn column, DependencyProperty dp) 10 | { 11 | ArgumentNullException.ThrowIfNull(column, nameof(column)); 12 | 13 | var value = column.ReadLocalValue(dp); 14 | return value != null && value.GetType() == dp.PropertyType; 15 | } 16 | 17 | protected static double? GetColumnWidth(GridViewColumn column, DependencyProperty dp) 18 | { 19 | ArgumentNullException.ThrowIfNull(column, nameof(column)); 20 | 21 | var value = column.ReadLocalValue(dp); 22 | if (value != null && value.GetType() == dp.PropertyType) 23 | { 24 | return (double)value; 25 | } 26 | 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/GroupManagement/GroupViewModel.cs: -------------------------------------------------------------------------------- 1 | using MvvmScarletToolkit.Observables; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using System.Windows.Data; 6 | 7 | namespace MvvmScarletToolkit 8 | { 9 | [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")] 10 | public sealed class GroupViewModel : ViewModelBase 11 | { 12 | private string _name; 13 | 14 | public string Name 15 | { 16 | get { return _name; } 17 | private set { SetProperty(ref _name, value); } 18 | } 19 | 20 | public PropertyGroupDescription GroupDescription { get; } 21 | 22 | public GroupViewModel(IScarletCommandBuilder commandBuilder, PropertyInfo propertyInfo) 23 | : base(commandBuilder) 24 | { 25 | ArgumentNullException.ThrowIfNull(propertyInfo, nameof(propertyInfo)); 26 | 27 | _name = propertyInfo.Name; 28 | GroupDescription = new PropertyGroupDescription(propertyInfo.Name); 29 | } 30 | 31 | private string GetDebuggerDisplay() 32 | { 33 | return $"{nameof(GroupViewModel)}: {Name}"; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/GroupManagement/GroupsViewModelRemoved.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging.Messages; 2 | 3 | namespace MvvmScarletToolkit 4 | { 5 | public sealed class GroupsViewModelRemoved : ValueChangedMessage 6 | { 7 | public GroupsViewModelRemoved(GroupsViewModel content) 8 | : base(content) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/Enums/CharType.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit.Wpf 2 | { 3 | internal enum CharType 4 | { 5 | Space, 6 | Text, 7 | Symbol, 8 | Id, 9 | Class, 10 | Style, 11 | Dash 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/Enums/HTMLFlag.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit.Wpf 2 | { 3 | internal enum HTMLFlag 4 | { 5 | TextFormat, 6 | Element, 7 | Dynamic, 8 | Table, 9 | Controls, 10 | Search, 11 | Xml, 12 | Region, 13 | Variable, 14 | None 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/Enums/SymbolType.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit.Wpf 2 | { 3 | internal enum SymbolType 4 | { 5 | Reserved, 6 | European, 7 | Symbol, 8 | Scientific, 9 | Shape 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/HtmlHighlightTextBlock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | 4 | namespace MvvmScarletToolkit.Wpf 5 | { 6 | public class HtmlHighlightTextBlock : HtmlTextBlock 7 | { 8 | /// Identifies the dependency property. 9 | public static readonly DependencyProperty HighlightProperty = DependencyProperty.Register( 10 | nameof(Highlight), 11 | typeof(string), 12 | typeof(HtmlHighlightTextBlock), 13 | new UIPropertyMetadata(string.Empty)); 14 | 15 | public string Highlight 16 | { 17 | get { return (string)GetValue(HighlightProperty); } 18 | set { SetValue(HighlightProperty, value); } 19 | } 20 | 21 | public override void OnApplyTemplate() 22 | { 23 | base.OnApplyTemplate(); 24 | Parse(Html); 25 | } 26 | 27 | protected override void Parse(string html) 28 | { 29 | Inlines.Clear(); 30 | 31 | if (!string.IsNullOrEmpty(Highlight)) 32 | { 33 | var index = html.IndexOf(Highlight, StringComparison.InvariantCultureIgnoreCase); 34 | while (index != -1) 35 | { 36 | html = string.Format("{0}[b]{1}[/b]{2}", html.Substring(0, index), html.Substring(index, Highlight.Length), html.Substring(index + Highlight.Length)); 37 | index = html.IndexOf(Highlight, index + 7 + Highlight.Length, StringComparison.InvariantCultureIgnoreCase); 38 | } 39 | } 40 | 41 | this.UpdateWith(html); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/HtmlTextBlock.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace MvvmScarletToolkit.Wpf 5 | { 6 | public class HtmlTextBlock : TextBlock 7 | { 8 | /// Identifies the dependency property. 9 | public static readonly DependencyProperty HtmlProperty = DependencyProperty.Register( 10 | nameof(Html), 11 | typeof(string), 12 | typeof(HtmlTextBlock), 13 | new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnHtmlChanged))); 14 | 15 | public string Html 16 | { 17 | get { return (string)GetValue(HtmlProperty); } 18 | set { SetValue(HtmlProperty, value); } 19 | } 20 | 21 | /// When overridden in a derived class, is invoked whenever application code or internal processes call . 22 | public override void OnApplyTemplate() 23 | { 24 | base.OnApplyTemplate(); 25 | Parse(Html); 26 | } 27 | 28 | protected virtual void Parse(string html) 29 | { 30 | Inlines.Clear(); 31 | if (string.IsNullOrEmpty(html)) 32 | { 33 | return; 34 | } 35 | 36 | this.UpdateWith(html); 37 | } 38 | 39 | private static void OnHtmlChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 40 | { 41 | if (o is HtmlTextBlock textBlock && e.NewValue is string text) 42 | { 43 | textBlock.Parse(text); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/Interfaces/IParamParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MvvmScarletToolkit.Wpf 4 | { 5 | internal interface IParamParser 6 | { 7 | /// 8 | /// Uses to serialize the properties to string. 9 | /// 10 | IPropertySerializer Serializer { get; } 11 | 12 | /// 13 | /// Convert a dictionary contining key value pairs to ParamString. 14 | /// 15 | /// An array of key and value pairs 16 | /// Param String containing both key and value, e.g. key:"value" 17 | string DictionaryToString(Dictionary dictionary); 18 | 19 | /// 20 | /// Take a Paramstring and return a dictionary containing 21 | /// the paramstring's key and value. 22 | /// 23 | Dictionary StringToDictionary(string paramString); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/Interfaces/IPropertySerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MvvmScarletToolkit.Wpf 5 | { 6 | internal interface IPropertySerializer 7 | { 8 | /// 9 | /// Convert a dictionary contining key value pairs to property string. 10 | /// 11 | /// An array of key and value pairs 12 | /// Param String containing both key and value, e.g. key:"value" 13 | string PropertyToString(IEnumerable> properties); 14 | 15 | /// 16 | /// Take a property string and return a list of tuple containing 17 | /// the its key and value. 18 | /// 19 | IEnumerable> StringToProperty(string propertyString); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/Models/HTMLTagInfo.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmScarletToolkit.Wpf 2 | { 3 | internal sealed class HTMLTagInfo 4 | { 5 | public string Html { get; } 6 | public HTMLFlag Flags { get; } 7 | public short TagLevel { get; } 8 | 9 | public HTMLTagInfo(string html, HTMLFlag flags, short tagLevel) 10 | { 11 | Html = html; 12 | Flags = flags; 13 | TagLevel = tagLevel; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/HtmlTextBlock/ParamParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace MvvmScarletToolkit.Wpf 6 | { 7 | internal sealed class ParamParser : IParamParser 8 | { 9 | public IPropertySerializer Serializer { get; } 10 | 11 | public ParamParser(IPropertySerializer serializer) 12 | { 13 | Serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); 14 | } 15 | 16 | public string DictionaryToString(Dictionary dictionary) 17 | { 18 | return Serializer.PropertyToString(dictionary.Keys.Select(p => new Tuple(p, dictionary[p]))); 19 | } 20 | 21 | public Dictionary StringToDictionary(string @string) 22 | { 23 | var result = new Dictionary(); 24 | 25 | foreach (var tup in Serializer.StringToProperty(@string)) 26 | { 27 | result[tup.Item1] = tup.Item2; 28 | } 29 | 30 | return result; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/ToastNotification/IToast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace MvvmScarletToolkit.Wpf 5 | { 6 | public interface IToast : INotifyPropertyChanged 7 | { 8 | /// 9 | /// The title of the toast. 10 | /// 11 | string Title { get; } 12 | 13 | /// 14 | /// The message to display. 15 | /// 16 | string Body { get; } 17 | 18 | /// 19 | /// If true, the toast will remain visible until the user closes it. 20 | /// 21 | bool IsPersistent { get; } 22 | 23 | /// 24 | /// This is set to true immediately when the alloted display time runs out, but the toast is not removed immediately from the toast collection, 25 | /// so that there is time to animate its removal 26 | /// 27 | bool IsRemoving { get; set; } 28 | 29 | /// 30 | /// The toast type to be displayed. 31 | /// 32 | Enum ToastType { get; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/ToastNotification/IToastService.cs: -------------------------------------------------------------------------------- 1 | using DynamicData.Binding; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Windows.Input; 5 | 6 | namespace MvvmScarletToolkit.Wpf 7 | { 8 | public interface IToastService : INotifyPropertyChanged, IDisposable 9 | { 10 | IObservableCollection Items { get; } 11 | 12 | /// 13 | /// how long to wait, before finally removing toasts from the toast collection 14 | /// 15 | TimeSpan ToastCloseDelay { get; } 16 | 17 | /// 18 | /// Show a toast notification according to the service configuration 19 | /// 20 | void Show(IToast toast); 21 | 22 | /// 23 | /// will close and remove a toast immediately 24 | /// 25 | ICommand DismissCommand { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/ToastNotification/ToastServiceConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | 4 | namespace MvvmScarletToolkit.Wpf 5 | { 6 | public sealed class ToastServiceConfiguration 7 | { 8 | private const int DefaultIsVisibleForInSeconds = 3; 9 | 10 | /// 11 | /// the window style to look for 12 | /// 13 | public string WindowStyleKey { get; set; } = "DefaultToastWindowStyle"; 14 | 15 | /// 16 | /// how far away the notification window is supposed to be from the screens edges 17 | /// 18 | public int WindowOffset { get; set; } = 12; 19 | 20 | /// 21 | /// how long to wait, before closing the notification window 22 | /// 23 | public TimeSpan WindowCloseDelay { get; set; } = TimeSpan.FromSeconds(DefaultIsVisibleForInSeconds); 24 | 25 | /// 26 | /// how long to wait, before finally removing toasts from the toast collection 27 | /// 28 | public TimeSpan ToastCloseDelay { get; set; } = TimeSpan.FromSeconds(1); 29 | 30 | /// 31 | /// The container to display the toast within. Leave this as null to use the primary monitor. 32 | /// 33 | public Rect? Origin { get; set; } 34 | 35 | /// 36 | /// The duration to show the toast for. 37 | /// 38 | public TimeSpan ToastVisibleFor { get; set; } = TimeSpan.FromSeconds(DefaultIsVisibleForInSeconds); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/ToastNotification/ToastServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit.Wpf 4 | { 5 | public static class ToastServiceExtensions 6 | { 7 | /// 8 | /// Show a toast notification for 5 seconds. 9 | /// 10 | /// The title of the toast. 11 | /// The message to display. 12 | /// The toast type to be displayed. 13 | public static void Show(this IToastService toastService, string title, string body, ToastType toastType) 14 | { 15 | toastService.Show(title, body, toastType, false); 16 | } 17 | 18 | /// 19 | /// Show a toast notification for a given time frame. 20 | /// 21 | /// The title of the toast. 22 | /// The message to display. 23 | /// The toast type to be displayed. 24 | /// If true, the toast will remain visible until the user closes it. 25 | public static void Show(this IToastService toastService, string title, string body, Enum type, bool isPersistent) 26 | { 27 | toastService.Show(new ToastViewModel(title, body, type, isPersistent)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/ToastNotification/ToastType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MvvmScarletToolkit.Wpf 4 | { 5 | [Flags] 6 | public enum ToastType 7 | { 8 | None = 0, 9 | Success = 1 << 0, 10 | Error = 1 << 1, 11 | Warning = 1 << 2, 12 | Information = 1 << 3 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Features/ToastNotification/ToastViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System; 3 | 4 | namespace MvvmScarletToolkit.Wpf 5 | { 6 | public partial class ToastViewModel : ObservableObject, IToast 7 | { 8 | /// 9 | /// This is set to true immediately when the alloted display time runs out, but the toast is not removed immediately from the toast collection, 10 | /// so that there is time to animate its removal 11 | /// 12 | [ObservableProperty] 13 | private bool _isRemoving; 14 | 15 | public string Title { get; } 16 | 17 | public string Body { get; } 18 | 19 | public Enum ToastType { get; } 20 | 21 | /// 22 | /// Whether the toast has to be closed by the user 23 | /// 24 | public bool IsPersistent { get; } 25 | 26 | public ToastViewModel(string title, string body, Enum toastType, bool isPersistent) 27 | { 28 | Title = title; 29 | Body = body; 30 | ToastType = toastType; 31 | IsPersistent = isPersistent; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Implementations/ScarletCommandManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace MvvmScarletToolkit 5 | { 6 | /// 7 | /// simple wrapper around 8 | /// 9 | public sealed class ScarletCommandManager : IScarletCommandManager 10 | { 11 | private static readonly Lazy _default = new Lazy(() => new ScarletCommandManager()); 12 | 13 | public static IScarletCommandManager Default => _default.Value; 14 | 15 | public event EventHandler RequerySuggested 16 | { 17 | add { CommandManager.RequerySuggested += value; } 18 | remove { CommandManager.RequerySuggested -= value; } 19 | } 20 | 21 | public ScarletCommandManager() 22 | { 23 | } 24 | 25 | public void InvalidateRequerySuggested() 26 | { 27 | CommandManager.InvalidateRequerySuggested(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Implementations/ScarletLocalizationProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Linq; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public sealed class ScarletLocalizationProvider : ILocalizationProvider 8 | { 9 | public IEnumerable Languages { get; } 10 | 11 | public ScarletLocalizationProvider() 12 | { 13 | Languages = Enumerable.Empty(); 14 | } 15 | 16 | public string Translate(string key, CultureInfo culture) 17 | { 18 | return key; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Implementations/ScarletWeakEventManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public sealed class ScarletWeakEventManager : IScarletEventManager 8 | { 9 | private static readonly Lazy _default = new Lazy(() => new ScarletWeakEventManager()); 10 | 11 | public static IScarletEventManager Default => _default.Value; 12 | 13 | public ScarletWeakEventManager() 14 | { 15 | } 16 | 17 | public void AddHandler(INotifyPropertyChanged source, string eventName, EventHandler handler) 18 | { 19 | WeakEventManager.AddHandler(source, eventName, handler); 20 | } 21 | 22 | public void RemoveHandler(INotifyPropertyChanged source, string eventName, EventHandler handler) 23 | { 24 | WeakEventManager.RemoveHandler(source, eventName, handler); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/ConverterMarkupExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | using System.Windows.Markup; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | public abstract class ConverterMarkupExtension : MarkupExtension, IValueConverter 9 | where T : class, IValueConverter, new() 10 | { 11 | private static T? _converter; 12 | 13 | public override object ProvideValue(IServiceProvider serviceProvider) 14 | { 15 | return _converter ??= new T(); 16 | } 17 | 18 | public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture); 19 | 20 | public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 21 | { 22 | // According to https://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.convertback(v=vs.110).aspx#Anchor_1 23 | // (kudos Scott Chamberlain), if you do not support a conversion 24 | // back you should return a Binding.DoNothing or a DependencyProperty.UnSetProperty 25 | return Binding.DoNothing; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/DebugConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.Windows.Data; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | /// 9 | /// log bound values and value changes to the Debug.Listener 10 | /// 11 | /// 12 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 13 | /// 14 | [ValueConversion(typeof(object), typeof(object))] 15 | public sealed class DebugConverter : ConverterMarkupExtension 16 | { 17 | private readonly Action Logger = (message) => Debug.WriteLine(message); 18 | 19 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | if (parameter is null) 22 | { 23 | Logger(string.Format(culture, "Convert: '{0}' to '{1}'", value, targetType)); 24 | } 25 | else 26 | { 27 | Logger(string.Format(culture, "Convert: '{0}' to '{1}' with Parameter = '{2}' of type: = '{3}'", value, targetType, parameter, parameter.GetType().Name)); 28 | } 29 | 30 | return value; 31 | } 32 | 33 | public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 34 | { 35 | if (parameter is null) 36 | { 37 | Logger(string.Format(culture, "ConvertBack: '{0}' to '{1}'", value, targetType)); 38 | } 39 | else 40 | { 41 | Logger(string.Format(culture, "ConvertBack: '{0}' to '{1}' with Parameter = '{2}' of type: = '{3}'", value, targetType, parameter, parameter.GetType().Name)); 42 | } 43 | 44 | return value; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/Flatten.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Windows.Data; 6 | using System.Windows.Markup; 7 | 8 | namespace MvvmScarletToolkit 9 | { 10 | /// 11 | /// turns an Enumerable of strings into a single string 12 | /// 13 | /// 14 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 15 | /// 16 | [ValueConversion(typeof(string[]), typeof(string))] 17 | public sealed class Flatten : ConverterMarkupExtension 18 | { 19 | [ConstructorArgument("separator")] 20 | public string Separator { get; set; } = ", "; 21 | 22 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | if (value is IEnumerable strings) 25 | { 26 | return string.Join(Separator, strings.Where(s => !string.IsNullOrWhiteSpace(s))); 27 | } 28 | 29 | return Binding.DoNothing; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/IgnoreNullOrEmptyStrings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | /// 9 | /// Filter/ignore strings that are null or empty 10 | /// 11 | /// 12 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 13 | /// 14 | [ValueConversion(typeof(string), typeof(object))] 15 | public sealed class IgnoreNullOrEmptyStrings : ConverterMarkupExtension 16 | { 17 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | switch (value) 20 | { 21 | case null: 22 | return DependencyProperty.UnsetValue; 23 | 24 | default: 25 | if (string.IsNullOrEmpty(value as string)) 26 | { 27 | return DependencyProperty.UnsetValue; 28 | } 29 | 30 | return value; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/InvertBooleanToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | using System.Windows.Markup; 6 | 7 | namespace MvvmScarletToolkit 8 | { 9 | /// 10 | /// Convert a boolean value to corresponding inverted value 11 | /// 12 | /// 13 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 14 | /// 15 | [ValueConversion(typeof(bool), typeof(Visibility))] 16 | public sealed class InvertBooleanToVisibilityConverter : ConverterMarkupExtension 17 | { 18 | [ConstructorArgument("visibility")] 19 | public Visibility Visibility { get; set; } 20 | 21 | public InvertBooleanToVisibilityConverter() 22 | { 23 | Visibility = Visibility.Hidden; 24 | } 25 | 26 | public InvertBooleanToVisibilityConverter(Visibility visibility) 27 | { 28 | Visibility = visibility; 29 | } 30 | 31 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | if (value is bool toggle && toggle) 34 | { 35 | return Visibility; 36 | } 37 | 38 | return Visibility.Visible; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/IsNot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// negate a boolean value 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(bool), typeof(bool))] 14 | public sealed class IsNot : ConverterMarkupExtension 15 | { 16 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if (value is bool boolean) 19 | { 20 | return !boolean; 21 | } 22 | 23 | return false; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/IsNotNull.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// whether something is not null 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(bool), typeof(object))] 14 | public sealed class IsNotNull : ConverterMarkupExtension 15 | { 16 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | return value is not null; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/IsNotNullOrEmpty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// whether a string is not null or empty 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(string), typeof(bool))] 14 | public sealed class IsNotNullOrEmpty : ConverterMarkupExtension 15 | { 16 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if (value is null) 19 | { 20 | return false; 21 | } 22 | 23 | if (value is string text) 24 | { 25 | return !string.IsNullOrEmpty(text); 26 | } 27 | 28 | return true; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/IsNotNullOrWhiteSpace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// whether a string is not null or whitespace 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(string), typeof(bool))] 14 | public sealed class IsNotNullOrWhiteSpace : ConverterMarkupExtension 15 | { 16 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if (value is null) 19 | { 20 | return false; 21 | } 22 | 23 | if (value is string text) 24 | { 25 | return !string.IsNullOrWhiteSpace(text); 26 | } 27 | 28 | return false; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/IsNull.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// whether something is null 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(bool), typeof(object))] 14 | public sealed class IsNull : ConverterMarkupExtension 15 | { 16 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | return value is null; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/IsNullOrEmpty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// whether a string is null or empty 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(string), typeof(bool))] 14 | public sealed class IsNullOrEmpty : ConverterMarkupExtension 15 | { 16 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if (value is null) 19 | { 20 | return true; 21 | } 22 | 23 | if (value is string text) 24 | { 25 | return string.IsNullOrEmpty(text); 26 | } 27 | 28 | return false; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/IsNullOrWhiteSpace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// whether a string is null or whitespace 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(string), typeof(bool))] 14 | public sealed class IsNullOrWhiteSpace : ConverterMarkupExtension 15 | { 16 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if (value is null) 19 | { 20 | return true; 21 | } 22 | 23 | if (value is string text) 24 | { 25 | return string.IsNullOrWhiteSpace(text); 26 | } 27 | 28 | return false; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/MultiBooleanAndConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// combines all conditions with logical and 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(bool), typeof(bool))] 14 | public sealed class MultiBooleanAndConverter : MultiConverterMarkupExtension 15 | { 16 | public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | var result = true; 19 | for (var i = 0; i < values.Length; i++) 20 | { 21 | if (values[i] is bool newValue) 22 | { 23 | result &= newValue; 24 | } 25 | else 26 | { 27 | return false; 28 | } 29 | } 30 | 31 | return result; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/RadioButtonCheckedConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | /// 8 | /// Compare the value with the value and return whether they are 9 | /// 10 | /// 11 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | [ValueConversion(typeof(object), typeof(bool))] 14 | public sealed class RadioButtonCheckedConverter : ConverterMarkupExtension 15 | { 16 | /// 17 | /// A converted value. If the method returns null, the valid null value is used. 18 | /// 19 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | return value.Equals(parameter); 22 | } 23 | 24 | /// 25 | /// A converted value. If the method returns null, the valid null value is used. 26 | /// 27 | public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 28 | { 29 | return value.Equals(true) ? parameter : false; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/Converters/ToCase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Controls; 4 | using System.Windows.Data; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | /// 9 | /// alter the casing for a string 10 | /// 11 | /// 12 | /// xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 13 | /// 14 | [ValueConversion(typeof(string), typeof(string))] 15 | public sealed class ToCase : ConverterMarkupExtension 16 | { 17 | public CharacterCasing Casing { get; set; } 18 | 19 | public ToCase() 20 | { 21 | Casing = CharacterCasing.Upper; 22 | } 23 | 24 | public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | if (value is string str) 27 | { 28 | var casing = Casing; 29 | if (parameter is CharacterCasing argCasing) 30 | { 31 | casing = argCasing; 32 | } 33 | 34 | return Convert(str, parameter, casing); 35 | } 36 | 37 | return value; 38 | } 39 | 40 | private static object Convert(string value, object parameter, CharacterCasing fallBackCasing) 41 | { 42 | if (parameter is CharacterCasing characterCasing) 43 | { 44 | return LocalConvert(characterCasing); 45 | } 46 | else 47 | { 48 | return LocalConvert(fallBackCasing); 49 | } 50 | 51 | object LocalConvert(CharacterCasing casing) 52 | { 53 | return casing switch 54 | { 55 | CharacterCasing.Lower => value.ToLowerInvariant(), 56 | CharacterCasing.Normal => value, 57 | CharacterCasing.Upper => value.ToUpperInvariant(), 58 | _ => value, 59 | }; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/MultiConverterMarkupExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | using System.Windows.Markup; 5 | 6 | namespace MvvmScarletToolkit 7 | { 8 | public abstract class MultiConverterMarkupExtension : MarkupExtension, IMultiValueConverter 9 | where T : class, IMultiValueConverter, new() 10 | { 11 | private static T? _converter; 12 | 13 | public override object ProvideValue(IServiceProvider serviceProvider) 14 | { 15 | return _converter ??= new T(); 16 | } 17 | 18 | public abstract object Convert(object[] values, Type targetType, object parameter, CultureInfo culture); 19 | 20 | public virtual object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) 21 | { 22 | // According to https://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.convertback(v=vs.110).aspx#Anchor_1 23 | // (kudos Scott Chamberlain), if you do not support a conversion 24 | // back you should return a Binding.DoNothing or a DependencyProperty.UnSetProperty 25 | return new[] { Binding.DoNothing }; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MarkupExtensions/StartProcessExtension.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Input; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | using System.Windows.Input; 6 | using System.Windows.Markup; 7 | 8 | namespace MvvmScarletToolkit 9 | { 10 | /// 11 | /// namespace: xmlns:mvvm="http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared" 12 | /// 13 | /// 14 | /// 15 | /// 16 | [MarkupExtensionReturnType(typeof(ICommand))] 17 | public sealed class StartProcessExtension : MarkupExtension 18 | { 19 | public ICommand? Command { get; private set; } 20 | 21 | [ConstructorArgument("url")] 22 | public string? Url { get; set; } 23 | 24 | public override object ProvideValue(IServiceProvider serviceProvider) 25 | { 26 | Command ??= new RelayCommand(StartProcess); 27 | 28 | return Command; 29 | } 30 | 31 | private void StartProcess() 32 | { 33 | if (string.IsNullOrEmpty(Url)) 34 | { 35 | return; 36 | } 37 | 38 | try 39 | { 40 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 41 | { 42 | using (Process.Start(new ProcessStartInfo("cmd", $"/c start {Url}"))) 43 | { } 44 | } 45 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 46 | { 47 | using (Process.Start("xdg-open", Url)) 48 | { } 49 | } 50 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 51 | { 52 | using (Process.Start("open", Url)) 53 | { } 54 | } 55 | } 56 | catch (Exception ex) 57 | { 58 | Debug.WriteLine(ex); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/MvvmScarletToolkit.Wpf.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows;net9.0-windows 4 | Library 5 | true 6 | enable 7 | MvvmScarletToolkit.Implementations is part of the MvvmScarletToolkit framework, containing concrete implementations for WPF that have been abstracted away in the MvvmScarletToolkit.Abstractions library. 8 | MvvmScarletToolkit,MVVM,C#,Toolkit,Scarlet,WPF,Library,.NET,OSS,OpenSource 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows.Markup; 4 | 5 | #if NET5_0_OR_GREATER 6 | [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows7.0")] 7 | #endif 8 | 9 | // General Information about an assembly is controlled through the following 10 | // set of attributes. Change these attribute values to modify the information 11 | // associated with an assembly. 12 | [assembly: AssemblyTitle("MvvmScarletToolkit.Implementations")] 13 | [assembly: AssemblyDescription("MvvmScarletToolkit.Implementations is part of the MvvmScarletToolkit framework, containing concrete implementations for WPF that have been abstracted away in the MvvmScarletToolkit.Abstractions library.")] 14 | [assembly: AssemblyConfiguration("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9bd9b759-8c12-4e08-971b-9ba34d8247ff")] 24 | 25 | // Set the recommended prefix for xaml namespaces 26 | [assembly: XmlnsPrefix("http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared", "mvvm")] 27 | 28 | // map clr namespaces to a single xaml namespace 29 | [assembly: XmlnsDefinition("http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared", "MvvmScarletToolkit")] 30 | [assembly: XmlnsDefinition("http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared", "MvvmScarletToolkit.Wpf")] 31 | [assembly: XmlnsDefinition("http://SoftThorn.MvvmScarletToolkit.com/winfx/xaml/shared", "MvvmScarletToolkit.Wpf.FileSystemBrowser")] 32 | -------------------------------------------------------------------------------- /src/MvvmScarletToolkit.Wpf/TriggerActions/ClearPasswordBoxAction.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xaml.Behaviors; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace MvvmScarletToolkit 6 | { 7 | public class ClearPasswordBoxAction : TriggerAction