├── .gitignore ├── .gitlab-ci.yml ├── .gitpod.yml ├── LICENSE ├── README.md ├── _config.yml └── src ├── JamSoft.AvaloniaUI.Dialogs.Sample ├── .gitignore ├── App.axaml ├── App.axaml.cs ├── Assets │ └── avalonia-logo.ico ├── BootStrapper.cs ├── JamSoft.AvaloniaUI.Dialogs.Sample.csproj ├── Models │ └── MyUserSettings.cs ├── Program.cs ├── Roots.xml ├── ViewModels │ ├── ComboBoxItemViewModel.cs │ ├── CustomBaseChildWindowViewModel.cs │ ├── MainWindowViewModel.cs │ ├── MyChildWindowViewModel.cs │ ├── MyDialogViewModel.cs │ ├── MyWizardViewModel.cs │ ├── SirNotAppearingInThisAppViewModel.cs │ └── ViewModelBase.cs ├── Views │ ├── CustomBaseChildWindowView.axaml │ ├── CustomBaseChildWindowView.axaml.cs │ ├── MainWindow.axaml │ ├── MainWindow.axaml.cs │ ├── MyChildWindowView.axaml │ ├── MyChildWindowView.axaml.cs │ ├── MyDialogView.axaml │ ├── MyDialogView.axaml.cs │ ├── MyWizardView.axaml │ └── MyWizardView.axaml.cs └── app.manifest ├── JamSoft.AvaloniaUI.Dialogs.sln ├── JamSoft.AvaloniaUI.Dialogs ├── Assets │ ├── CloseIcon │ │ ├── Android │ │ │ ├── icons8-close-120(-xxxhdpi).png │ │ │ ├── icons8-close-23(-ldpi).png │ │ │ ├── icons8-close-30(-mdpi).png │ │ │ ├── icons8-close-45(-hdpi).png │ │ │ ├── icons8-close-60(-xhdpi).png │ │ │ └── icons8-close-90(-xxhdpi).png │ │ ├── iOS │ │ │ ├── icons8-close-30(@1x).png │ │ │ ├── icons8-close-60(@2x).png │ │ │ └── icons8-close-90(@3x).png │ │ ├── icons8-close-120.png │ │ ├── icons8-close-240.png │ │ ├── icons8-close-30.png │ │ ├── icons8-close-480.png │ │ ├── icons8-close-60.png │ │ └── icons8-close-90.png │ ├── add.png │ ├── back-up.png │ ├── ban.png │ ├── battery-half.png │ ├── check-circle.png │ ├── cross-circle.png │ ├── customize.png │ ├── database.png │ ├── diamond-exclamation.png │ ├── exclamation.png │ ├── folder-open.png │ ├── info.png │ ├── interrogation.png │ └── wifi.png ├── Commands │ └── DelegateCommand.cs ├── Controls │ ├── Wizard.cs │ └── WizardStep.cs ├── DialogService.cs ├── DialogServiceConfiguration.cs ├── DialogServiceFactory.cs ├── Events │ ├── EventHandlerUtils.cs │ ├── IWeakEventHandler.cs │ ├── RequestCloseDialogEventArgs.cs │ ├── UnregisterCallback.cs │ └── WeakEventHandler.cs ├── Helpers │ ├── CommonFilters.cs │ └── CommonFiltersExtensionMethods.cs ├── IDialogService.cs ├── IMessageBoxService.cs ├── JamSoft.AvaloniaUI.Dialogs.csproj ├── JamSoftLogo-Splat513x513.png ├── MessageBoxService.cs ├── MsgBox │ ├── IconResolver.cs │ ├── MsgBoxButton.cs │ ├── MsgBoxButtonResult.cs │ ├── MsgBoxImage.cs │ └── MsgBoxResult.cs ├── Themes │ ├── ChildStyle.axaml │ ├── Default.axaml │ ├── ModalStyle.axaml │ ├── MsgBoxStyles.axaml │ ├── WizardStepStyle.axaml │ └── WizardStyle.axaml ├── ViewModels │ ├── ChildWindowViewModel.cs │ ├── DialogViewModel.cs │ ├── IChildWindowViewModel.cs │ ├── IDialogResultVmHelper.cs │ ├── IDialogViewModel.cs │ ├── IMsgBoxViewModel.cs │ ├── IWindowPositionAware.cs │ ├── IWizardViewModel.cs │ ├── MsgBoxViewModel.cs │ └── WizardViewModel.cs └── Views │ ├── ChildWindow.axaml │ ├── ChildWindow.axaml.cs │ ├── DialogWindow.axaml │ ├── DialogWindow.axaml.cs │ ├── MsgBoxView.axaml │ ├── MsgBoxView.axaml.cs │ ├── MsgBoxWindow.axaml │ └── MsgBoxWindow.axaml.cs └── img ├── JamSoftLogo-Splat513x513.png ├── basic-dialog.png ├── child-window.png ├── message-box-checkbox.png ├── message-box.png ├── sample-app.png └── wizard.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.*~ 3 | project.lock.json 4 | .DS_Store 5 | *.pyc 6 | nupkg/ 7 | 8 | # Visual Studio Code 9 | .vscode 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.userosscache 15 | *.sln.docstates 16 | *.snk 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Dd]ebugPublic/ 21 | [Rr]elease/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | build/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | [Oo]ut/ 30 | msbuild.log 31 | msbuild.err 32 | msbuild.wrn 33 | 34 | src/.idea/ 35 | .vs -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: mcr.microsoft.com/dotnet/core/sdk:latest 2 | 3 | stages: 4 | - build 5 | 6 | build: 7 | stage: build 8 | script: 9 | - "dotnet build src\\JamSoft.AvaloniaUI.Dialogs.sln" 10 | artifacts: 11 | paths: 12 | - bin/ -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: gitpod/workspace-dotnet 2 | 3 | tasks: 4 | - init: dotnet build 5 | command: dotnet run 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 James Green 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 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # Tye 66 | .tye/ 67 | 68 | # ASP.NET Scaffolding 69 | ScaffoldingReadMe.txt 70 | 71 | # StyleCop 72 | StyleCopReport.xml 73 | 74 | # Files built by Visual Studio 75 | *_i.c 76 | *_p.c 77 | *_h.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.iobj 82 | *.pch 83 | *.pdb 84 | *.ipdb 85 | *.pgc 86 | *.pgd 87 | *.rsp 88 | *.sbr 89 | *.tlb 90 | *.tli 91 | *.tlh 92 | *.tmp 93 | *.tmp_proj 94 | *_wpftmp.csproj 95 | *.log 96 | *.vspscc 97 | *.vssscc 98 | .builds 99 | *.pidb 100 | *.svclog 101 | *.scc 102 | 103 | # Chutzpah Test files 104 | _Chutzpah* 105 | 106 | # Visual C++ cache files 107 | ipch/ 108 | *.aps 109 | *.ncb 110 | *.opendb 111 | *.opensdf 112 | *.sdf 113 | *.cachefile 114 | *.VC.db 115 | *.VC.VC.opendb 116 | 117 | # Visual Studio profiler 118 | *.psess 119 | *.vsp 120 | *.vspx 121 | *.sap 122 | 123 | # Visual Studio Trace Files 124 | *.e2e 125 | 126 | # TFS 2012 Local Workspace 127 | $tf/ 128 | 129 | # Guidance Automation Toolkit 130 | *.gpState 131 | 132 | # ReSharper is a .NET coding add-in 133 | _ReSharper*/ 134 | *.[Rr]e[Ss]harper 135 | *.DotSettings.user 136 | 137 | # TeamCity is a build add-in 138 | _TeamCity* 139 | 140 | # DotCover is a Code Coverage Tool 141 | *.dotCover 142 | 143 | # AxoCover is a Code Coverage Tool 144 | .axoCover/* 145 | !.axoCover/settings.json 146 | 147 | # Coverlet is a free, cross platform Code Coverage Tool 148 | coverage*.json 149 | coverage*.xml 150 | coverage*.info 151 | 152 | # Visual Studio code coverage results 153 | *.coverage 154 | *.coveragexml 155 | 156 | # NCrunch 157 | _NCrunch_* 158 | .*crunch*.local.xml 159 | nCrunchTemp_* 160 | 161 | # MightyMoose 162 | *.mm.* 163 | AutoTest.Net/ 164 | 165 | # Web workbench (sass) 166 | .sass-cache/ 167 | 168 | # Installshield output folder 169 | [Ee]xpress/ 170 | 171 | # DocProject is a documentation generator add-in 172 | DocProject/buildhelp/ 173 | DocProject/Help/*.HxT 174 | DocProject/Help/*.HxC 175 | DocProject/Help/*.hhc 176 | DocProject/Help/*.hhk 177 | DocProject/Help/*.hhp 178 | DocProject/Help/Html2 179 | DocProject/Help/html 180 | 181 | # Click-Once directory 182 | publish/ 183 | 184 | # Publish Web Output 185 | *.[Pp]ublish.xml 186 | *.azurePubxml 187 | # Note: Comment the next line if you want to checkin your web deploy settings, 188 | # but database connection strings (with potential passwords) will be unencrypted 189 | *.pubxml 190 | *.publishproj 191 | 192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 193 | # checkin your Azure Web App publish settings, but sensitive information contained 194 | # in these scripts will be unencrypted 195 | PublishScripts/ 196 | 197 | # NuGet Packages 198 | *.nupkg 199 | # NuGet Symbol Packages 200 | *.snupkg 201 | # The packages folder can be ignored because of Package Restore 202 | **/[Pp]ackages/* 203 | # except build/, which is used as an MSBuild target. 204 | !**/[Pp]ackages/build/ 205 | # Uncomment if necessary however generally it will be regenerated when needed 206 | #!**/[Pp]ackages/repositories.config 207 | # NuGet v3's project.json files produces more ignorable files 208 | *.nuget.props 209 | *.nuget.targets 210 | 211 | # Microsoft Azure Build Output 212 | csx/ 213 | *.build.csdef 214 | 215 | # Microsoft Azure Emulator 216 | ecf/ 217 | rcf/ 218 | 219 | # Windows Store app package directories and files 220 | AppPackages/ 221 | BundleArtifacts/ 222 | Package.StoreAssociation.xml 223 | _pkginfo.txt 224 | *.appx 225 | *.appxbundle 226 | *.appxupload 227 | 228 | # Visual Studio cache files 229 | # files ending in .cache can be ignored 230 | *.[Cc]ache 231 | # but keep track of directories ending in .cache 232 | !?*.[Cc]ache/ 233 | 234 | # Others 235 | ClientBin/ 236 | ~$* 237 | *~ 238 | *.dbmdl 239 | *.dbproj.schemaview 240 | *.jfm 241 | *.pfx 242 | *.publishsettings 243 | orleans.codegen.cs 244 | 245 | # Including strong name files can present a security risk 246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 247 | #*.snk 248 | 249 | # Since there are multiple workflows, uncomment next line to ignore bower_components 250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 251 | #bower_components/ 252 | 253 | # RIA/Silverlight projects 254 | Generated_Code/ 255 | 256 | # Backup & report files from converting an old project file 257 | # to a newer Visual Studio version. Backup files are not needed, 258 | # because we have git ;-) 259 | _UpgradeReport_Files/ 260 | Backup*/ 261 | UpgradeLog*.XML 262 | UpgradeLog*.htm 263 | ServiceFabricBackup/ 264 | *.rptproj.bak 265 | 266 | # SQL Server files 267 | *.mdf 268 | *.ldf 269 | *.ndf 270 | 271 | # Business Intelligence projects 272 | *.rdl.data 273 | *.bim.layout 274 | *.bim_*.settings 275 | *.rptproj.rsuser 276 | *- [Bb]ackup.rdl 277 | *- [Bb]ackup ([0-9]).rdl 278 | *- [Bb]ackup ([0-9][0-9]).rdl 279 | 280 | # Microsoft Fakes 281 | FakesAssemblies/ 282 | 283 | # GhostDoc plugin setting file 284 | *.GhostDoc.xml 285 | 286 | # Node.js Tools for Visual Studio 287 | .ntvs_analysis.dat 288 | node_modules/ 289 | 290 | # Visual Studio 6 build log 291 | *.plg 292 | 293 | # Visual Studio 6 workspace options file 294 | *.opt 295 | 296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 297 | *.vbw 298 | 299 | # Visual Studio LightSwitch build output 300 | **/*.HTMLClient/GeneratedArtifacts 301 | **/*.DesktopClient/GeneratedArtifacts 302 | **/*.DesktopClient/ModelManifest.xml 303 | **/*.Server/GeneratedArtifacts 304 | **/*.Server/ModelManifest.xml 305 | _Pvt_Extensions 306 | 307 | # Paket dependency manager 308 | .paket/paket.exe 309 | paket-files/ 310 | 311 | # FAKE - F# Make 312 | .fake/ 313 | 314 | # CodeRush personal settings 315 | .cr/personal 316 | 317 | # Python Tools for Visual Studio (PTVS) 318 | __pycache__/ 319 | *.pyc 320 | 321 | # Cake - Uncomment if you are using it 322 | # tools/** 323 | # !tools/packages.config 324 | 325 | # Tabs Studio 326 | *.tss 327 | 328 | # Telerik's JustMock configuration file 329 | *.jmconfig 330 | 331 | # BizTalk build output 332 | *.btp.cs 333 | *.btm.cs 334 | *.odx.cs 335 | *.xsd.cs 336 | 337 | # OpenCover UI analysis results 338 | OpenCover/ 339 | 340 | # Azure Stream Analytics local run output 341 | ASALocalRun/ 342 | 343 | # MSBuild Binary and Structured Log 344 | *.binlog 345 | 346 | # NVidia Nsight GPU debugger configuration file 347 | *.nvuser 348 | 349 | # MFractors (Xamarin productivity tool) working folder 350 | .mfractor/ 351 | 352 | # Local History for Visual Studio 353 | .localhistory/ 354 | 355 | # BeatPulse healthcheck temp database 356 | healthchecksdb 357 | 358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 359 | MigrationBackup/ 360 | 361 | # Ionide (cross platform F# VS Code tools) working folder 362 | .ionide/ 363 | 364 | # Fody - auto-generated XML schema 365 | FodyWeavers.xsd 366 | 367 | ## 368 | ## Visual studio for Mac 369 | ## 370 | 371 | 372 | # globs 373 | Makefile.in 374 | *.userprefs 375 | *.usertasks 376 | config.make 377 | config.status 378 | aclocal.m4 379 | install-sh 380 | autom4te.cache/ 381 | *.tar.gz 382 | tarballs/ 383 | test-results/ 384 | 385 | # Mac bundle stuff 386 | *.dmg 387 | *.app 388 | 389 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 390 | # General 391 | .DS_Store 392 | .AppleDouble 393 | .LSOverride 394 | 395 | # Icon must end with two \r 396 | Icon 397 | 398 | 399 | # Thumbnails 400 | ._* 401 | 402 | # Files that might appear in the root of a volume 403 | .DocumentRevisions-V100 404 | .fseventsd 405 | .Spotlight-V100 406 | .TemporaryItems 407 | .Trashes 408 | .VolumeIcon.icns 409 | .com.apple.timemachine.donotpresent 410 | 411 | # Directories potentially created on remote AFP share 412 | .AppleDB 413 | .AppleDesktop 414 | Network Trash Folder 415 | Temporary Items 416 | .apdisk 417 | 418 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 419 | # Windows thumbnail cache files 420 | Thumbs.db 421 | ehthumbs.db 422 | ehthumbs_vista.db 423 | 424 | # Dump file 425 | *.stackdump 426 | 427 | # Folder config file 428 | [Dd]esktop.ini 429 | 430 | # Recycle Bin used on file shares 431 | $RECYCLE.BIN/ 432 | 433 | # Windows Installer files 434 | *.cab 435 | *.msi 436 | *.msix 437 | *.msm 438 | *.msp 439 | 440 | # Windows shortcuts 441 | *.lnk 442 | 443 | # JetBrains Rider 444 | .idea/ 445 | *.sln.iml 446 | 447 | ## 448 | ## Visual Studio Code 449 | ## 450 | .vscode/* 451 | !.vscode/settings.json 452 | !.vscode/tasks.json 453 | !.vscode/launch.json 454 | !.vscode/extensions.json 455 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/App.axaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | using JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 5 | using JamSoft.AvaloniaUI.Dialogs.Sample.Views; 6 | using Splat; 7 | 8 | namespace JamSoft.AvaloniaUI.Dialogs.Sample; 9 | 10 | public partial class App : Application 11 | { 12 | public override void Initialize() 13 | { 14 | AvaloniaXamlLoader.Load(this); 15 | } 16 | 17 | public override void OnFrameworkInitializationCompleted() 18 | { 19 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 20 | { 21 | desktop.MainWindow = new MainWindow 22 | { 23 | DataContext = Locator.Current.GetService() 24 | }; 25 | } 26 | 27 | base.OnFrameworkInitializationCompleted(); 28 | } 29 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Assets/avalonia-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs.Sample/Assets/avalonia-logo.ico -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/BootStrapper.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 3 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 4 | using Splat; 5 | 6 | namespace JamSoft.AvaloniaUI.Dialogs.Sample; 7 | 8 | public static class BootStrapper 9 | { 10 | public static void Register(IMutableDependencyResolver services, IReadonlyDependencyResolver resolver) 11 | { 12 | services.RegisterLazySingleton(() => DialogServiceFactory.Create(new DialogServiceConfiguration 13 | { 14 | ApplicationName = "Dialog Sample App", 15 | UseApplicationNameInTitle = true, 16 | ViewsAssemblyName = Assembly.GetExecutingAssembly().GetName().Name 17 | })); 18 | 19 | services.RegisterLazySingleton(DialogServiceFactory.CreateMessageBoxService); 20 | 21 | services.Register(() => new MainWindowViewModel(resolver.GetService()!, resolver.GetService()!)); 22 | services.Register(() => new MyDialogViewModel()); 23 | services.Register(() => new MyChildWindowViewModel()); 24 | services.Register(() => new CustomBaseChildWindowViewModel()); 25 | services.Register(() => new SirNotAppearingInThisAppViewModel()); // this has no matching view on purpose 26 | services.Register(() => new MyWizardViewModel()); 27 | } 28 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/JamSoft.AvaloniaUI.Dialogs.Sample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net8.0 5 | enable 6 | true 7 | app.manifest 8 | 12 9 | Assets\avalonia-logo.ico 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | MyWizardView.axaml 41 | Code 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Models/MyUserSettings.cs: -------------------------------------------------------------------------------- 1 | using JamSoft.Helpers.Configuration; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.Models; 4 | 5 | public class MyUserSettings : SettingsBase 6 | { 7 | public double Left { get; set; } 8 | public double Top { get; set; } 9 | public double Height { get; set; } 10 | public double Width { get; set; } 11 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.ReactiveUI; 3 | using System; 4 | using JamSoft.AvaloniaUI.Dialogs.Sample.Models; 5 | using Splat; 6 | 7 | namespace JamSoft.AvaloniaUI.Dialogs.Sample; 8 | 9 | class Program 10 | { 11 | // Initialization code. Don't use any Avalonia, third-party APIs or any 12 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 13 | // yet and stuff might break. 14 | [STAThread] 15 | public static void Main(string[] args) 16 | { 17 | RegisterDependencies(); 18 | 19 | MyUserSettings.Load(AppDomain.CurrentDomain.BaseDirectory); 20 | 21 | BuildAvaloniaApp() 22 | .StartWithClassicDesktopLifetime(args); 23 | } 24 | 25 | private static void RegisterDependencies() => 26 | BootStrapper.Register(Locator.CurrentMutable, Locator.Current); 27 | 28 | // Avalonia configuration, don't remove; also used by visual designer. 29 | public static AppBuilder BuildAvaloniaApp() 30 | => AppBuilder.Configure() 31 | .UsePlatformDetect() 32 | .LogToTrace() 33 | .UseReactiveUI(); 34 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Roots.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/ViewModels/ComboBoxItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using JamSoft.Helpers.AvaloniaUI.Patterns.Mvvm; 2 | using ReactiveUI; 3 | 4 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 5 | 6 | public class ComboBoxItemViewModel : AvaloniaViewModelBase 7 | { 8 | private string? _name; 9 | private object? _value; 10 | 11 | public string? Name 12 | { 13 | get => _name; 14 | set => this.RaiseAndSetIfChanged(ref _name, value); 15 | } 16 | 17 | public object? Value 18 | { 19 | get => _value; 20 | set => this.RaiseAndSetIfChanged(ref _value, value); 21 | } 22 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/ViewModels/CustomBaseChildWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows.Input; 4 | using Avalonia.Controls; 5 | using Avalonia.Media; 6 | using Avalonia.Media.Imaging; 7 | using Avalonia.Platform; 8 | using JamSoft.AvaloniaUI.Dialogs.Commands; 9 | using JamSoft.AvaloniaUI.Dialogs.Events; 10 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 11 | 12 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 13 | 14 | public class CustomBaseChildWindowViewModel : IChildWindowViewModel 15 | { 16 | private ICommand _cancelCommand; 17 | 18 | public CustomBaseChildWindowViewModel() 19 | { 20 | AcceptCommand = new DelegateCommand(null); 21 | CancelCommand = new DelegateCommand(null); 22 | CloseIcon = new Bitmap(AssetLoader.Open(new Uri("avares://JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-30.png"))); 23 | 24 | _cancelCommand = new DelegateCommand(() => InvokeRequestCloseDialog(new RequestCloseDialogEventArgs(false)), CanCancel); 25 | CancelCommandText = "Cancel"; 26 | } 27 | 28 | private void InvokeRequestCloseDialog(RequestCloseDialogEventArgs e) 29 | { 30 | RequestCloseDialog?.Invoke(this, e); 31 | } 32 | 33 | public WindowStartupLocation Location { get; set; } 34 | public double RequestedTop { get; set; } 35 | public double RequestedLeft { get; set; } 36 | public string? ChildWindowTitle { get; set; } 37 | public double RequestedWidth { get; set; } 38 | public double RequestedHeight { get; set; } 39 | public IImage CloseIcon { get; set; } 40 | 41 | public event EventHandler? RequestCloseDialog = delegate { }; 42 | public ICommand AcceptCommand { get; set; } 43 | 44 | public ICommand CancelCommand 45 | { 46 | get => _cancelCommand; 47 | set => _cancelCommand = value; 48 | } 49 | 50 | public event PropertyChangedEventHandler? PropertyChanged = delegate { }; 51 | 52 | public string? AcceptCommandText { get; set; } 53 | public string? CancelCommandText { get; set; } 54 | public bool CanAccept() 55 | { 56 | return true; 57 | } 58 | 59 | public bool CanCancel() 60 | { 61 | return true; 62 | } 63 | 64 | public bool HideCancelButton { get; set; } 65 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Input; 4 | using Avalonia.Controls; 5 | using Avalonia.Platform.Storage; 6 | using JamSoft.AvaloniaUI.Dialogs.Commands; 7 | using JamSoft.AvaloniaUI.Dialogs.Helpers; 8 | using JamSoft.AvaloniaUI.Dialogs.MsgBox; 9 | using JamSoft.AvaloniaUI.Dialogs.Sample.Models; 10 | using JamSoft.AvaloniaUI.Dialogs.Sample.Views; 11 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 12 | using ReactiveUI; 13 | using Splat; 14 | 15 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 16 | 17 | public class MainWindowViewModel : ViewModelBase 18 | { 19 | private readonly IDialogService _dialogService; 20 | private readonly IMessageBoxService _messageBoxService; 21 | private ICommand? _openFileCommand; 22 | private ICommand? _openFolderCommand; 23 | private ICommand? _openWordFileCommand; 24 | private ICommand? _saveFileCommand; 25 | private ICommand? _saveFileWithNameCommand; 26 | private ICommand? _saveWordFileCommand; 27 | private ICommand? _openFilesCommand; 28 | private ICommand? _showDialogCommand; 29 | private ICommand? _showCustomizedDialogCommand; 30 | private ICommand? _showChildWindowCommand; 31 | private ICommand? _showDialogAutoFindViewCommand; 32 | private ICommand? _showChildWindowAutoFindViewCommand; 33 | private ICommand? _showCustomChildWindowCommand; 34 | private ICommand? _childWindowRememberPositionCommand; 35 | private ICommand? _missingViewCommand; 36 | private ICommand? _wizardViewCommand; 37 | private ICommand? _showMessageBoxCommand; 38 | private string? _message; 39 | 40 | public MainWindowViewModel(IDialogService dialogService, IMessageBoxService messageBoxService) 41 | { 42 | _dialogService = dialogService; 43 | _messageBoxService = messageBoxService; 44 | 45 | Message = "Welcome to JamSoft Avalonia Dialogs!"; 46 | 47 | OpenFileCommand = new DelegateCommand(OpenFileCommandExecuted, () => true); 48 | OpenFolderCommand = new DelegateCommand(OpenFolderCommandExecuted, () => true); 49 | OpenWordFileCommand = new DelegateCommand(OpenWordFileCommandExecuted, () => true); 50 | OpenFilesCommand = new DelegateCommand(OpenFilesCommandExecuted, () => true); 51 | SaveFileCommand = new DelegateCommand(SaveFileCommandExecuted, () => true); 52 | SaveFileWithNameCommand = new DelegateCommand(SaveFileWithNameCommandExecuted, () => true); 53 | SaveWordFileCommand = new DelegateCommand(SaveWordFileCommandExecuted, () => true); 54 | ShowDialogCommand = new DelegateCommand(ShowDialogCommandExecuted, () => true); 55 | ShowDialogAutoFindViewCommand = new DelegateCommand(ShowDialogAutoFindViewCommandExecuted, () => true); 56 | ShowCustomizedDialogCommand = new DelegateCommand(ShowCustomizedDialogCommandExecuted, () => true); 57 | ShowChildWindowCommand = new DelegateCommand(ShowChildWindowCommandExecuted, () => true); 58 | ShowChildWindowAutoFindViewCommand = new DelegateCommand(ShowChildWindowAutoFindViewCommandExecuted, () => true); 59 | ShowCustomChildWindowCommand = new DelegateCommand(ShowCustomChildWindowAutoFindViewCommandExecuted, () => true); 60 | ChildWindowRememberPositionCommand = new DelegateCommand(ChildWindowRememberPositionCommandExecuted, () => true); 61 | MissingViewCommand = new DelegateCommand(MissingViewCommandExecuted, () => true); 62 | WizardViewCommand = new DelegateCommand(WizardViewCommandExecuted, () => true); 63 | ShowMessageBoxCommand = new DelegateCommand(ShowMessageBoxCommandExecuted, () => true); 64 | } 65 | 66 | public ICommand? ShowMessageBoxCommand 67 | { 68 | get => _showMessageBoxCommand; 69 | set => this.RaiseAndSetIfChanged(ref _showMessageBoxCommand, value); 70 | } 71 | 72 | public ICommand? OpenFolderCommand 73 | { 74 | get => _openFolderCommand; 75 | set => this.RaiseAndSetIfChanged(ref _openFolderCommand, value); 76 | } 77 | 78 | public ICommand? WizardViewCommand 79 | { 80 | get => _wizardViewCommand; 81 | set => this.RaiseAndSetIfChanged(ref _wizardViewCommand, value); 82 | } 83 | 84 | public ICommand? MissingViewCommand 85 | { 86 | get => _missingViewCommand; 87 | set => this.RaiseAndSetIfChanged(ref _missingViewCommand, value); 88 | } 89 | 90 | public ICommand? ChildWindowRememberPositionCommand 91 | { 92 | get => _childWindowRememberPositionCommand; 93 | set => this.RaiseAndSetIfChanged(ref _childWindowRememberPositionCommand, value); 94 | } 95 | 96 | public ICommand? ShowCustomChildWindowCommand 97 | { 98 | get => _showCustomChildWindowCommand; 99 | set => this.RaiseAndSetIfChanged(ref _showCustomChildWindowCommand, value); 100 | } 101 | 102 | public ICommand? ShowChildWindowAutoFindViewCommand 103 | { 104 | get => _showChildWindowAutoFindViewCommand; 105 | set => this.RaiseAndSetIfChanged(ref _showChildWindowAutoFindViewCommand, value); 106 | } 107 | 108 | public ICommand? ShowDialogAutoFindViewCommand 109 | { 110 | get => _showDialogAutoFindViewCommand; 111 | set => this.RaiseAndSetIfChanged(ref _showDialogAutoFindViewCommand, value); 112 | } 113 | 114 | public ICommand? ShowChildWindowCommand 115 | { 116 | get => _showChildWindowCommand; 117 | set => this.RaiseAndSetIfChanged(ref _showChildWindowCommand, value); 118 | } 119 | 120 | public ICommand? ShowDialogCommand 121 | { 122 | get => _showDialogCommand; 123 | set => this.RaiseAndSetIfChanged(ref _showDialogCommand, value); 124 | } 125 | 126 | public ICommand? ShowCustomizedDialogCommand 127 | { 128 | get => _showCustomizedDialogCommand; 129 | set => this.RaiseAndSetIfChanged(ref _showCustomizedDialogCommand, value); 130 | } 131 | 132 | public ICommand? OpenFilesCommand 133 | { 134 | get => _openFilesCommand; 135 | set => this.RaiseAndSetIfChanged(ref _openFilesCommand, value); 136 | } 137 | 138 | public ICommand? SaveFileCommand 139 | { 140 | get => _saveFileCommand; 141 | set => this.RaiseAndSetIfChanged(ref _saveFileCommand, value); 142 | } 143 | 144 | public ICommand? SaveFileWithNameCommand 145 | { 146 | get => _saveFileWithNameCommand; 147 | set => this.RaiseAndSetIfChanged(ref _saveFileWithNameCommand, value); 148 | } 149 | 150 | public ICommand? SaveWordFileCommand 151 | { 152 | get => _saveWordFileCommand; 153 | set => this.RaiseAndSetIfChanged(ref _saveWordFileCommand, value); 154 | } 155 | 156 | public ICommand? OpenFileCommand 157 | { 158 | get => _openFileCommand; 159 | set => this.RaiseAndSetIfChanged(ref _openFileCommand, value); 160 | } 161 | 162 | public ICommand? OpenWordFileCommand 163 | { 164 | get => _openWordFileCommand; 165 | set => this.RaiseAndSetIfChanged(ref _openWordFileCommand, value); 166 | } 167 | 168 | public string? Message 169 | { 170 | get => _message; 171 | set => this.RaiseAndSetIfChanged(ref _message, value); 172 | } 173 | 174 | private async void OpenFileCommandExecuted() 175 | { 176 | Message = await _dialogService.OpenFile("Open Any File"); 177 | } 178 | 179 | private async void OpenFolderCommandExecuted() 180 | { 181 | Message = await _dialogService.OpenFolder("Open Any Folder"); 182 | } 183 | 184 | private async void OpenWordFileCommandExecuted() 185 | { 186 | Message = await _dialogService.OpenFile("Open Word File", new List 187 | { 188 | CommonFilters.WordFilter 189 | }); 190 | } 191 | 192 | private async void OpenFilesCommandExecuted() 193 | { 194 | Message = string.Join(Environment.NewLine, await _dialogService.OpenFiles("Open Multiple Files") ?? []); 195 | } 196 | 197 | private async void SaveFileCommandExecuted() 198 | { 199 | Message = await _dialogService.SaveFile("Save Any File"); 200 | } 201 | 202 | private async void SaveFileWithNameCommandExecuted() 203 | { 204 | Message = await _dialogService.SaveFile("Save Any File", suggestedFileName:"Suggested Name"); 205 | } 206 | 207 | private async void SaveWordFileCommandExecuted() 208 | { 209 | Message = await _dialogService.SaveFile("Save Word File", new List 210 | { 211 | CommonFilters.WordFilter 212 | }); 213 | } 214 | 215 | private void ShowDialogCommandExecuted() 216 | { 217 | var vm = Locator.Current.GetService(); 218 | if (vm != null) _dialogService.ShowDialog(new MyDialogView(), vm, DialogCallback); 219 | } 220 | 221 | private void ShowDialogAutoFindViewCommandExecuted() 222 | { 223 | var vm = Locator.Current.GetService(); 224 | if (vm != null) _dialogService.ShowDialog(vm, DialogCallback); 225 | } 226 | 227 | private void ShowCustomizedDialogCommandExecuted() 228 | { 229 | var vm = Locator.Current.GetService(); 230 | if (vm != null) 231 | { 232 | vm.AcceptCommandText = "Accept"; 233 | vm.CancelCommandText = "Oh No!"; 234 | 235 | _dialogService.ShowDialog(new MyDialogView(), vm, DialogCallback); 236 | } 237 | } 238 | 239 | private void DialogCallback(MyDialogViewModel? obj) 240 | { 241 | Message = obj?.DialogMessage; 242 | } 243 | 244 | private void ShowChildWindowCommandExecuted() 245 | { 246 | var vm = Locator.Current.GetService(); 247 | if (vm != null) 248 | { 249 | vm.RequestedLeft = 50; 250 | vm.RequestedTop = 50; 251 | vm.RequestedHeight = 500; 252 | vm.RequestedWidth = 600; 253 | // these values can be stored in user settings and loaded at runtime etc. 254 | vm.ChildMessage = "Child Message Value"; 255 | vm.ChildWindowTitle = "My Child Window Title"; 256 | 257 | _dialogService.ShowChildWindow(new MyChildWindowView(), vm, 258 | model => { Message = $"Child Closed - {model.ChildMessage}"; }); 259 | } 260 | } 261 | 262 | private void ShowChildWindowAutoFindViewCommandExecuted() 263 | { 264 | var vm = Locator.Current.GetService(); 265 | if (vm != null) 266 | { 267 | vm.RequestedLeft = 50; 268 | vm.RequestedTop = 50; 269 | vm.RequestedHeight = 500; 270 | vm.RequestedWidth = 600; 271 | vm.ChildMessage = "Child Message Value"; 272 | vm.ChildWindowTitle = "My Child Window Title Auto Find"; 273 | vm.Location = WindowStartupLocation.CenterScreen; 274 | 275 | _dialogService.ShowChildWindow(vm, model => { Message = $"Child Closed - {model.ChildMessage}"; }); 276 | } 277 | } 278 | 279 | private void ShowCustomChildWindowAutoFindViewCommandExecuted() 280 | { 281 | var vm = Locator.Current.GetService(); 282 | if (vm != null) 283 | { 284 | vm.RequestedLeft = 50; 285 | vm.RequestedTop = 50; 286 | vm.RequestedHeight = 500; 287 | vm.RequestedWidth = 600; 288 | 289 | vm.ChildWindowTitle = "My Custom Child Window Title Auto Find"; 290 | 291 | _dialogService.ShowChildWindow(vm, 292 | model => { Message = $"Custom Child View Model Closed - {model.GetType()}"; }); 293 | } 294 | } 295 | 296 | private void ChildWindowRememberPositionCommandExecuted() 297 | { 298 | var vm = Locator.Current.GetService(); 299 | if (vm != null) 300 | { 301 | // these values can be stored in user settings and loaded at runtime etc. 302 | vm.RequestedLeft = MyUserSettings.Instance.Left; 303 | vm.RequestedTop = MyUserSettings.Instance.Top; 304 | vm.RequestedHeight = MyUserSettings.Instance.Height; 305 | vm.RequestedWidth = MyUserSettings.Instance.Width; 306 | 307 | vm.ChildWindowTitle = "My Custom Child Window Title Auto Find"; 308 | 309 | _dialogService.ShowChildWindow(vm, 310 | model => { Message = $"Child Remember Position Closed - {model.GetType()}"; }); 311 | } 312 | } 313 | 314 | private void MissingViewCommandExecuted() 315 | { 316 | var vm = Locator.Current.GetService(); 317 | 318 | try 319 | { 320 | if (vm != null) _dialogService.ShowChildWindow(vm, model => { }); 321 | } 322 | catch (Exception ex) 323 | { 324 | Message = ex.Message; 325 | } 326 | } 327 | 328 | private void WizardViewCommandExecuted() 329 | { 330 | var vm = Locator.Current.GetService(); 331 | if (vm != null) 332 | { 333 | vm.RequestedLeft = MyUserSettings.Instance.Left; 334 | vm.RequestedTop = MyUserSettings.Instance.Top; 335 | vm.RequestedHeight = MyUserSettings.Instance.Height; 336 | vm.RequestedWidth = MyUserSettings.Instance.Width; 337 | 338 | vm.ChildWindowTitle = "My Wizard"; 339 | 340 | _dialogService.StartWizard(vm, model => { Message = $"Wizard Closed - {model.GetType()}"; }); 341 | } 342 | } 343 | 344 | private async void ShowMessageBoxCommandExecuted() 345 | { 346 | var resultOkCancel = await _messageBoxService.Show("OK Cancel", "Do you want to carry on?", MsgBoxButton.OkCancel, MsgBoxImage.Error); 347 | Message = $"{resultOkCancel.ButtonResult} clicked"; 348 | 349 | var resultOk = await _messageBoxService.Show("Ok", "Do you want to carry on?", MsgBoxButton.Ok, MsgBoxImage.Information); 350 | Message = $"{resultOk.ButtonResult} clicked"; 351 | 352 | var resultYesNo = await _messageBoxService.Show("Yes No", "Do you want to carry on?", MsgBoxButton.YesNo, MsgBoxImage.Asterisk); 353 | Message = $"{resultYesNo.ButtonResult} clicked"; 354 | 355 | var resultYesNoCancel = await _messageBoxService.Show("Yes No Cancel", "Do you want to carry on?", MsgBoxButton.YesNoCancel, MsgBoxImage.Question); 356 | Message = $"{resultYesNoCancel.ButtonResult} clicked"; 357 | 358 | var resultYesNoWarning = await _messageBoxService.Show("Yes No", "Do you want to carry on?", MsgBoxButton.YesNo, MsgBoxImage.Warning); 359 | Message = $"{resultYesNoWarning.ButtonResult} clicked"; 360 | 361 | var resultYesNoNoIcon = await _messageBoxService.Show("Yes No Without Icon", "Do you want to carry on?", MsgBoxButton.YesNo); 362 | Message = $"{resultYesNoNoIcon.ButtonResult} clicked"; 363 | 364 | var viewModel = new MsgBoxViewModel("Yes No With Icon", "Do you want to carry on?", MsgBoxButton.YesNo, MsgBoxImage.Forbidden); 365 | var resultVm = await _messageBoxService.Show(viewModel); 366 | Message = $"{resultVm.ButtonResult} clicked"; 367 | 368 | var resultYesNoCancelCustomButtonText = await _messageBoxService.Show("German Yes No Cancel", "Möchten Sie weitermachen?", MsgBoxButton.YesNoCancel, MsgBoxImage.Wifi, "Nein", "Ja", "Abbrechen"); 369 | Message = $"{resultYesNoCancelCustomButtonText.ButtonResult} clicked"; 370 | 371 | var msgBoxResult = await _messageBoxService.Show("OK Cancel With Checkbox", "Do you want to carry on?", MsgBoxButton.OkCancel, MsgBoxImage.Error, checkBoxText:"Don't ask me again"); 372 | Message = $"Button: {msgBoxResult.ButtonResult}, Checkbox: {msgBoxResult.CheckBoxResult}"; 373 | } 374 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/ViewModels/MyChildWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using JamSoft.AvaloniaUI.Dialogs.Events; 3 | using JamSoft.AvaloniaUI.Dialogs.Sample.Models; 4 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 5 | 6 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 7 | 8 | public class MyChildWindowViewModel : ChildWindowViewModel 9 | { 10 | private string? _childMessage; 11 | private ObservableCollection? _comboboxItems; 12 | private ComboBoxItemViewModel? _selectedItem; 13 | 14 | public MyChildWindowViewModel() 15 | { 16 | RequestCloseDialog += OnRequestCloseDialog; 17 | 18 | ComboboxItems = new ObservableCollection 19 | { 20 | new ComboBoxItemViewModel { Name = string.Empty, Value = null }, 21 | new ComboBoxItemViewModel { Name = "Item One", Value = 1 }, 22 | new ComboBoxItemViewModel { Name = "Item Two", Value = 2 }, 23 | new ComboBoxItemViewModel { Name = "Item Three", Value = 3 } 24 | }; 25 | } 26 | 27 | public string? ChildMessage 28 | { 29 | get => _childMessage; 30 | set => RaiseAndSetIfChanged(ref _childMessage, value); 31 | } 32 | 33 | public ObservableCollection? ComboboxItems 34 | { 35 | get => _comboboxItems; 36 | set => RaiseAndSetIfChanged(ref _comboboxItems, value); 37 | } 38 | 39 | public ComboBoxItemViewModel? SelectedItem 40 | { 41 | get => _selectedItem; 42 | set => RaiseAndSetIfChanged(ref _selectedItem, value); 43 | } 44 | 45 | private void OnRequestCloseDialog(object? sender, RequestCloseDialogEventArgs e) 46 | { 47 | MyUserSettings.Instance.Top = RequestedTop; 48 | MyUserSettings.Instance.Left = RequestedLeft; 49 | MyUserSettings.Instance.Width = RequestedWidth; 50 | MyUserSettings.Instance.Height = RequestedHeight; 51 | 52 | RequestCloseDialog -= OnRequestCloseDialog; 53 | } 54 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/ViewModels/MyDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 4 | 5 | public class MyDialogViewModel : DialogViewModel 6 | { 7 | private string? _dialogMessage; 8 | 9 | public string? DialogMessage 10 | { 11 | get => _dialogMessage; 12 | set => RaiseAndSetIfChanged(ref _dialogMessage , value); 13 | } 14 | 15 | public override bool CanAccept() 16 | { 17 | return !string.IsNullOrWhiteSpace(DialogMessage); 18 | } 19 | 20 | public override bool CanCancel() 21 | { 22 | return string.IsNullOrWhiteSpace(DialogMessage); 23 | } 24 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/ViewModels/MyWizardViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 3 | 4 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 5 | 6 | public class MyWizardViewModel : WizardViewModel 7 | { 8 | private string? _valueOne; 9 | private string? _valueTwo; 10 | private string? _valueThree; 11 | private string? _valueFour; 12 | private bool _wizardStepOneComplete; 13 | private bool _wizardStepTwoComplete; 14 | private bool _wizardStepThreeComplete; 15 | private bool _wizardStepFourComplete; 16 | private ObservableCollection? _comboboxItems; 17 | private ComboBoxItemViewModel? _selectedItem; 18 | 19 | public MyWizardViewModel() 20 | { 21 | ComboboxItems = new ObservableCollection 22 | { 23 | new ComboBoxItemViewModel { Name = string.Empty, Value = null }, 24 | new ComboBoxItemViewModel { Name = "Item One", Value = 1 }, 25 | new ComboBoxItemViewModel { Name = "Item Two", Value = 2 }, 26 | new ComboBoxItemViewModel { Name = "Item Three", Value = 3 } 27 | }; 28 | } 29 | 30 | public ObservableCollection? ComboboxItems 31 | { 32 | get => _comboboxItems; 33 | set => RaiseAndSetIfChanged(ref _comboboxItems, value); 34 | } 35 | 36 | public ComboBoxItemViewModel? SelectedItem 37 | { 38 | get => _selectedItem; 39 | set 40 | { 41 | RaiseAndSetIfChanged(ref _selectedItem, value); 42 | WizardStepOneComplete = !string.IsNullOrWhiteSpace(ValueOne) && value?.Value != null; 43 | } 44 | } 45 | 46 | public string? ValueOne 47 | { 48 | get => _valueOne; 49 | set 50 | { 51 | RaiseAndSetIfChanged(ref _valueOne, value); 52 | WizardStepOneComplete = !string.IsNullOrWhiteSpace(value) && SelectedItem?.Value != null; 53 | } 54 | } 55 | 56 | public string? ValueTwo 57 | { 58 | get => _valueTwo; 59 | set 60 | { 61 | RaiseAndSetIfChanged(ref _valueTwo, value); 62 | WizardStepTwoComplete = !string.IsNullOrWhiteSpace(value); 63 | } 64 | } 65 | 66 | public string? ValueThree 67 | { 68 | get => _valueThree; 69 | set 70 | { 71 | RaiseAndSetIfChanged(ref _valueThree, value); 72 | WizardStepThreeComplete = !string.IsNullOrWhiteSpace(value); 73 | } 74 | } 75 | 76 | public string? ValueFour 77 | { 78 | get => _valueFour; 79 | set 80 | { 81 | RaiseAndSetIfChanged(ref _valueFour, value); 82 | WizardStepFourComplete = !string.IsNullOrWhiteSpace(value); 83 | } 84 | } 85 | 86 | public bool WizardStepOneComplete 87 | { 88 | get => _wizardStepOneComplete; 89 | set => RaiseAndSetIfChanged(ref _wizardStepOneComplete, value); 90 | } 91 | 92 | public bool WizardStepTwoComplete 93 | { 94 | get => _wizardStepTwoComplete; 95 | set => RaiseAndSetIfChanged(ref _wizardStepTwoComplete, value); 96 | } 97 | 98 | public bool WizardStepThreeComplete 99 | { 100 | get => _wizardStepThreeComplete; 101 | set => RaiseAndSetIfChanged(ref _wizardStepThreeComplete, value); 102 | } 103 | 104 | public bool WizardStepFourComplete 105 | { 106 | get => _wizardStepFourComplete; 107 | set => RaiseAndSetIfChanged(ref _wizardStepFourComplete, value); 108 | } 109 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/ViewModels/SirNotAppearingInThisAppViewModel.cs: -------------------------------------------------------------------------------- 1 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 4 | 5 | public class SirNotAppearingInThisAppViewModel : ChildWindowViewModel 6 | { 7 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.ViewModels; 4 | 5 | public class ViewModelBase : ReactiveObject 6 | { 7 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/CustomBaseChildWindowView.axaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | CUSTOM CHILD VIEW 9 | This is a completely custom dialog. The view model implements IChildWindowViewModel and the accompanying view is auto discovered based on the ViewModel / View naming convention. 10 | A CustomBaseChildWindowViewModel is joined with a CustomBaseChildWindowView instance at runtime. 11 | The same approach can be taken with modal dialogs by implementing the IDialogViewModel interface and calling the ShowDialog method on the IDialogService. 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/CustomBaseChildWindowView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.Views; 6 | 7 | public partial class CustomBaseChildWindowView : UserControl 8 | { 9 | public CustomBaseChildWindowView() 10 | { 11 | InitializeComponent(); 12 | } 13 | 14 | private void InitializeComponent() 15 | { 16 | AvaloniaXamlLoader.Load(this); 17 | } 18 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Interactivity; 5 | using JamSoft.AvaloniaUI.Dialogs.Sample.Models; 6 | 7 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.Views; 8 | 9 | public partial class MainWindow : Window 10 | { 11 | public MainWindow() 12 | { 13 | InitializeComponent(); 14 | 15 | this.AttachDevTools(); 16 | } 17 | 18 | private void TopLevel_OnClosed(object? sender, EventArgs e) 19 | { 20 | MyUserSettings.Save(); 21 | } 22 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MyChildWindowView.axaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MyChildWindowView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.Views; 6 | 7 | public partial class MyChildWindowView : UserControl 8 | { 9 | public MyChildWindowView() 10 | { 11 | InitializeComponent(); 12 | } 13 | 14 | private void InitializeComponent() 15 | { 16 | AvaloniaXamlLoader.Load(this); 17 | } 18 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MyDialogView.axaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MyDialogView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.Views; 6 | 7 | public partial class MyDialogView : UserControl 8 | { 9 | public MyDialogView() 10 | { 11 | InitializeComponent(); 12 | } 13 | 14 | private void InitializeComponent() 15 | { 16 | AvaloniaXamlLoader.Load(this); 17 | } 18 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MyWizardView.axaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 13 | 14 | 15 | 16 | 17 | Page 1 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Page 2 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Page 3 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Final Step 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MyWizardView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace JamSoft.AvaloniaUI.Dialogs.Sample.Views; 5 | 6 | public partial class MyWizardView : UserControl 7 | { 8 | public MyWizardView() 9 | { 10 | InitializeComponent(); 11 | } 12 | 13 | private void InitializeComponent() 14 | { 15 | AvaloniaXamlLoader.Load(this); 16 | } 17 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.Sample/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JamSoft.AvaloniaUI.Dialogs.Sample", "JamSoft.AvaloniaUI.Dialogs.Sample\JamSoft.AvaloniaUI.Dialogs.Sample.csproj", "{E96BA9F0-28A7-424B-8B7B-3F36C7D97BAF}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JamSoft.AvaloniaUI.Dialogs", "JamSoft.AvaloniaUI.Dialogs\JamSoft.AvaloniaUI.Dialogs.csproj", "{C9D05C1A-6A85-4B87-A69E-D05D352BAB67}" 6 | EndProject 7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{644815C4-7C07-43AE-9FBC-01A67BE642C6}" 8 | ProjectSection(SolutionItems) = preProject 9 | ..\README.md = ..\README.md 10 | ..\.gitlab-ci.yml = ..\.gitlab-ci.yml 11 | ..\_config.yml = ..\_config.yml 12 | EndProjectSection 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {E96BA9F0-28A7-424B-8B7B-3F36C7D97BAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E96BA9F0-28A7-424B-8B7B-3F36C7D97BAF}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E96BA9F0-28A7-424B-8B7B-3F36C7D97BAF}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E96BA9F0-28A7-424B-8B7B-3F36C7D97BAF}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {C9D05C1A-6A85-4B87-A69E-D05D352BAB67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {C9D05C1A-6A85-4B87-A69E-D05D352BAB67}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {C9D05C1A-6A85-4B87-A69E-D05D352BAB67}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {C9D05C1A-6A85-4B87-A69E-D05D352BAB67}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-120(-xxxhdpi).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-120(-xxxhdpi).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-23(-ldpi).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-23(-ldpi).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-30(-mdpi).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-30(-mdpi).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-45(-hdpi).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-45(-hdpi).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-60(-xhdpi).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-60(-xhdpi).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-90(-xxhdpi).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-90(-xxhdpi).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/iOS/icons8-close-30(@1x).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/iOS/icons8-close-30(@1x).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/iOS/icons8-close-60(@2x).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/iOS/icons8-close-60(@2x).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/iOS/icons8-close-90(@3x).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/iOS/icons8-close-90(@3x).png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-120.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-240.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-30.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-480.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-480.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-60.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-90.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/add.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/back-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/back-up.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/ban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/ban.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/battery-half.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/battery-half.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/check-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/check-circle.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/cross-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/cross-circle.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/customize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/customize.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/database.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/diamond-exclamation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/diamond-exclamation.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/exclamation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/exclamation.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/folder-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/folder-open.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/info.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/interrogation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/interrogation.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Assets/wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/Assets/wifi.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Commands/DelegateCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.Commands; 4 | 5 | /// 6 | /// The default delegate command 7 | /// 8 | public class DelegateCommand : ICommand 9 | { 10 | private readonly Func? _canExecute; 11 | private readonly Action? _execute; 12 | 13 | /// 14 | /// The can execute changed handler 15 | /// 16 | public event EventHandler? CanExecuteChanged; 17 | 18 | /// 19 | /// The default constructor 20 | /// 21 | /// 22 | /// 23 | public DelegateCommand(Action? execute, Func? canExecute = null) 24 | { 25 | _execute = execute; 26 | _canExecute = canExecute; 27 | } 28 | 29 | /// 30 | /// The can execute method 31 | /// 32 | /// 33 | /// 34 | public bool CanExecute(object parameter) 35 | { 36 | if (_canExecute == null) return true; 37 | return _canExecute(); 38 | } 39 | 40 | /// 41 | /// The execute method 42 | /// 43 | /// 44 | public void Execute(object parameter) 45 | { 46 | if (_execute == null) return; 47 | _execute(); 48 | } 49 | 50 | /// 51 | /// Raises the can execute change event 52 | /// 53 | public void RaiseCanExecuteChanged() 54 | { 55 | if( CanExecuteChanged != null ) 56 | { 57 | CanExecuteChanged(this, EventArgs.Empty); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Controls/Wizard.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Controls.Metadata; 5 | using Avalonia.Controls.Presenters; 6 | using Avalonia.Controls.Primitives; 7 | using Avalonia.Controls.Templates; 8 | using Avalonia.Data; 9 | using Avalonia.Metadata; 10 | using JamSoft.AvaloniaUI.Dialogs.Commands; 11 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 12 | 13 | namespace JamSoft.AvaloniaUI.Dialogs.Controls 14 | { 15 | /// 16 | /// A wizard control that displays a series of elements in a workflow style progression. 17 | /// 18 | [TemplatePart("PART_StepsPresenter", typeof(ItemsControl))] 19 | [TemplatePart("PART_ButtonsPresenter", typeof(DockPanel))] 20 | public class Wizard : TemplatedControl 21 | { 22 | internal ContentPresenter? ContentPart; 23 | 24 | internal DockPanel? ButtonsPresenterPart { get; private set; } 25 | 26 | private Button? CompleteButton { get; set; } 27 | 28 | private Button? PreviousButton { get; set; } 29 | 30 | private Button? NextButton { get; set; } 31 | 32 | private ICommand MoveNextCommand => new DelegateCommand(MoveNextCommandExecuted); 33 | private ICommand MovePreviousCommand => new DelegateCommand(MovePreviousCommandExecuted); 34 | 35 | /// 36 | /// Gets or sets the selected item. 37 | /// 38 | public WizardStep? SelectedItem { get; set; } 39 | 40 | /// 41 | /// The index of the active WizardStep 42 | /// 43 | public int SelectedIndex { get; set; } 44 | 45 | /// 46 | /// Defines the property. 47 | /// 48 | public static readonly DirectProperty SelectedIndexProperty = 49 | AvaloniaProperty.RegisterDirect( 50 | nameof(SelectedIndex), 51 | o => o.SelectedIndex, 52 | (o, v) => o.SelectedIndex = v, 53 | unsetValue: 0, 54 | defaultBindingMode: BindingMode.OneWay); 55 | 56 | /// 57 | /// Defines the property. 58 | /// 59 | public static readonly DirectProperty SelectedItemProperty = 60 | AvaloniaProperty.RegisterDirect( 61 | nameof(SelectedItem), 62 | o => o.SelectedItem, 63 | (o, v) => o.SelectedItem = v, 64 | defaultBindingMode: BindingMode.OneWay, enableDataValidation: true); 65 | 66 | /// 67 | /// Defines the property. 68 | /// 69 | public static readonly StyledProperty ButtonPlacementProperty = 70 | AvaloniaProperty.Register(nameof(ButtonPlacement), defaultValue: Dock.Bottom); 71 | 72 | /// 73 | /// Defines the property. 74 | /// 75 | public static readonly StyledProperty ProgressPlacementProperty = 76 | AvaloniaProperty.Register(nameof(ProgressPlacement), defaultValue: Dock.Top); 77 | 78 | /// 79 | /// Defines the property. 80 | /// 81 | public static readonly StyledProperty ContentTemplateProperty = 82 | ContentControl.ContentTemplateProperty.AddOwner(); 83 | 84 | /// 85 | /// The selected content property 86 | /// 87 | public static readonly StyledProperty SelectedContentProperty = 88 | AvaloniaProperty.Register(nameof(SelectedContent)); 89 | 90 | /// 91 | /// The selected content template property 92 | /// 93 | public static readonly StyledProperty SelectedContentTemplateProperty = 94 | AvaloniaProperty.Register(nameof(SelectedContentTemplate)); 95 | 96 | /// 97 | /// Defines the property. 98 | /// 99 | public static readonly StyledProperty NextButtonContentProperty = 100 | AvaloniaProperty.Register(nameof(NextButtonContent), "Next"); 101 | 102 | /// 103 | /// Defines the property. 104 | /// 105 | public static readonly StyledProperty PreviousButtonContentProperty = 106 | AvaloniaProperty.Register(nameof(PreviousButtonContent), "Back"); 107 | 108 | /// 109 | /// Defines the property. 110 | /// 111 | public static readonly StyledProperty CompleteButtonContentProperty = 112 | AvaloniaProperty.Register(nameof(CompleteButtonContent), "Complete"); 113 | 114 | /// 115 | /// Defines the property. 116 | /// 117 | public static readonly StyledProperty?> StepsProperty = 118 | AvaloniaProperty.Register?>(nameof(Steps)); 119 | 120 | /// 121 | /// Gets or sets a collection used to generate the content of the . 122 | /// 123 | [Content] 124 | public IList? Steps 125 | { 126 | get => GetValue(StepsProperty); 127 | set => SetValue(StepsProperty, value); 128 | } 129 | 130 | /// 131 | /// Initializes static members of the class. 132 | /// 133 | public Wizard() 134 | { 135 | AffectsMeasure(ButtonPlacementProperty); 136 | DataContextProperty.Changed.AddClassHandler((x, _) => x.HandleDataContextChanged()); 137 | Steps = new List(); 138 | } 139 | 140 | /// 141 | /// Gets or sets the button placement of the Wizard. 142 | /// 143 | public Dock ButtonPlacement 144 | { 145 | get { return GetValue(ButtonPlacementProperty); } 146 | set { SetValue(ButtonPlacementProperty, value); } 147 | } 148 | 149 | /// 150 | /// Gets or sets the progress placement of the Wizard. 151 | /// 152 | public Dock ProgressPlacement 153 | { 154 | get { return GetValue(ProgressPlacementProperty); } 155 | set { SetValue(ProgressPlacementProperty, value); } 156 | } 157 | 158 | /// 159 | /// Gets or sets the default data template used to display the content of the selected WizardStep. 160 | /// 161 | public IDataTemplate ContentTemplate 162 | { 163 | get { return GetValue(ContentTemplateProperty)!; } 164 | set { SetValue(ContentTemplateProperty, value); } 165 | } 166 | 167 | /// 168 | /// Gets or sets the content of the selected WizardStep. 169 | /// 170 | /// 171 | /// The content of the selected WizardStep. 172 | /// 173 | public object? SelectedContent 174 | { 175 | get { return GetValue(SelectedContentProperty); } 176 | internal set { SetValue(SelectedContentProperty, value); } 177 | } 178 | 179 | /// 180 | /// Gets or sets the content template for the selected WizardStep. 181 | /// 182 | /// 183 | /// The content template of the selected WizardStep. 184 | /// 185 | public IDataTemplate? SelectedContentTemplate 186 | { 187 | get { return GetValue(SelectedContentTemplateProperty); } 188 | internal set { SetValue(SelectedContentTemplateProperty, value); } 189 | } 190 | 191 | /// 192 | /// Next button content 193 | /// 194 | public object? NextButtonContent 195 | { 196 | get { return GetValue(NextButtonContentProperty); } 197 | set { SetValue(NextButtonContentProperty, value); } 198 | } 199 | 200 | /// 201 | /// Previous button content 202 | /// 203 | public object? PreviousButtonContent 204 | { 205 | get { return GetValue(PreviousButtonContentProperty); } 206 | set { SetValue(PreviousButtonContentProperty, value); } 207 | } 208 | 209 | /// 210 | /// Complete button content 211 | /// 212 | public object? CompleteButtonContent 213 | { 214 | get { return GetValue(CompleteButtonContentProperty); } 215 | set { SetValue(CompleteButtonContentProperty, value); } 216 | } 217 | 218 | private void StepCompleted(AvaloniaPropertyChangedEventArgs args) 219 | { 220 | if (args.NewValue is bool && (bool)args.NewValue) 221 | { 222 | NextButton!.IsEnabled = true; 223 | } 224 | else 225 | { 226 | NextButton!.IsEnabled = false; 227 | } 228 | 229 | var list = Steps; 230 | if (args.Sender.Equals(list?.LastOrDefault()) && (args.NewValue is bool value && value)) 231 | { 232 | CompleteButton!.IsEnabled = true; 233 | } 234 | else 235 | { 236 | CompleteButton!.IsEnabled = false; 237 | } 238 | } 239 | 240 | private void UpdateSelectedContent() 241 | { 242 | if (ContentPart == null) return; 243 | 244 | if (SelectedItem != null && Steps?[SelectedIndex] != null) 245 | { 246 | SelectedItem.IsSelected = false; 247 | } 248 | 249 | SelectedItem = Steps?[SelectedIndex]; 250 | 251 | if (SelectedItem != null) 252 | { 253 | ContentPart.SetValue(ContentControl.ContentProperty, SelectedItem.Content); 254 | SelectedItem.IsSelected = true; 255 | } 256 | } 257 | 258 | private void HandleDataContextChanged() 259 | { 260 | WizardStep.StepCompleteProperty.Changed.AddClassHandler((_, args) => StepCompleted(args)); 261 | } 262 | 263 | /// 264 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e) 265 | { 266 | //ItemsPresenterPart = e.NameScope.Get("PART_StepsPresenter"); 267 | ButtonsPresenterPart = e.NameScope.Get("PART_ButtonsPresenter"); 268 | ContentPart = e.NameScope.Get("PART_SelectedContentHost"); 269 | 270 | PreviousButton = new Button 271 | { 272 | Content = PreviousButtonContent, 273 | Command = MovePreviousCommand 274 | }; 275 | ButtonsPresenterPart.Children.Add(PreviousButton); 276 | DockPanel.SetDock(PreviousButton, Dock.Left); 277 | 278 | NextButton = new Button 279 | { 280 | Content = NextButtonContent, 281 | Command = MoveNextCommand 282 | }; 283 | ButtonsPresenterPart.Children.Add(NextButton); 284 | DockPanel.SetDock(NextButton, Dock.Right); 285 | 286 | CompleteButton = new Button 287 | { 288 | Content = CompleteButtonContent, 289 | Command = (DataContext as IDialogViewModel)!.AcceptCommand 290 | }; 291 | ButtonsPresenterPart.Children.Add(CompleteButton); 292 | DockPanel.SetDock(CompleteButton, Dock.Right); 293 | 294 | CompleteButton.IsEnabled = false; 295 | CompleteButton.IsVisible = false; 296 | NextButton.IsEnabled = false; 297 | PreviousButton.IsEnabled = false; 298 | 299 | UpdateSelectedContent(); 300 | } 301 | 302 | private void MoveNextCommandExecuted() 303 | { 304 | if (SelectedIndex == Steps?.Count - 1) 305 | { 306 | return; 307 | } 308 | 309 | SelectedIndex++; 310 | UpdateSelectedContent(); 311 | SetButtons(); 312 | } 313 | 314 | private void MovePreviousCommandExecuted() 315 | { 316 | if (SelectedIndex == 0) 317 | { 318 | return; 319 | } 320 | 321 | SelectedIndex--; 322 | UpdateSelectedContent(); 323 | SetButtons(); 324 | } 325 | 326 | private void SetButtons() 327 | { 328 | NextButton!.IsEnabled = false; 329 | PreviousButton!.IsEnabled = true; 330 | 331 | var list = Steps; 332 | var selected = SelectedItem; 333 | 334 | if (selected!.Equals(list?.LastOrDefault())) 335 | { 336 | NextButton!.IsVisible = false; 337 | CompleteButton!.IsVisible = true; 338 | } 339 | else 340 | { 341 | NextButton!.IsVisible = true; 342 | CompleteButton!.IsVisible = false; 343 | } 344 | 345 | NextButton.IsEnabled = CompleteButton.IsEnabled = selected.StepComplete; 346 | } 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Controls/WizardStep.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Metadata; 4 | using Avalonia.Controls.Mixins; 5 | using Avalonia.Controls.Primitives; 6 | using Avalonia.Data; 7 | 8 | namespace JamSoft.AvaloniaUI.Dialogs.Controls; 9 | 10 | /// 11 | /// Defines a step in a Wizard control 12 | /// 13 | [PseudoClasses(":pressed", ":selected", ":complete")] 14 | public class WizardStep : HeaderedContentControl, ISelectable 15 | { 16 | /// 17 | /// Defines the property. 18 | /// 19 | public static readonly StyledProperty ProgressPlacementProperty = 20 | Wizard.ProgressPlacementProperty.AddOwner(); 21 | 22 | /// 23 | /// Defines the property. 24 | /// 25 | public static readonly StyledProperty IsSelectedProperty = 26 | ListBoxItem.IsSelectedProperty.AddOwner(); 27 | 28 | /// 29 | /// Defines the property 30 | /// 31 | public static readonly StyledProperty StepCompleteProperty = AvaloniaProperty.Register( 32 | nameof(StepComplete), defaultBindingMode: BindingMode.OneWay); 33 | 34 | /// 35 | /// The step complete property 36 | /// 37 | public bool StepComplete 38 | { 39 | get { return GetValue(StepCompleteProperty); } 40 | set 41 | { 42 | SetValue(StepCompleteProperty, value); 43 | } 44 | } 45 | 46 | static WizardStep() 47 | { 48 | SelectableMixin.Attach(IsSelectedProperty); 49 | FocusableProperty.OverrideDefaultValue(typeof(WizardStep), true); 50 | DataContextProperty.Changed.AddClassHandler((x, e) => x.UpdateHeader(e)); 51 | StepCompleteProperty.Changed.AddClassHandler((x, _) => x.SetStepComplete()); 52 | } 53 | 54 | private void SetStepComplete() 55 | { 56 | PseudoClasses.Set(":complete", StepComplete); 57 | } 58 | 59 | /// 60 | /// Gets the tab strip placement. 61 | /// 62 | /// 63 | /// The tab strip placement. 64 | /// 65 | public Dock ProgressPlacement 66 | { 67 | get { return GetValue(ProgressPlacementProperty); } 68 | } 69 | 70 | /// 71 | /// Gets or sets the selection state of the item. 72 | /// 73 | public bool IsSelected 74 | { 75 | get { return GetValue(IsSelectedProperty); } 76 | set { SetValue(IsSelectedProperty, value); } 77 | } 78 | 79 | private void UpdateHeader(AvaloniaPropertyChangedEventArgs obj) 80 | { 81 | if (Header == null) 82 | { 83 | if (obj.NewValue is HeaderedContentControl headered) 84 | { 85 | if (Header != headered.Header) 86 | { 87 | Header = headered.Header; 88 | } 89 | } 90 | else 91 | { 92 | if (!(obj.NewValue is Control)) 93 | { 94 | Header = obj.NewValue; 95 | } 96 | } 97 | } 98 | else 99 | { 100 | if (Header == obj.OldValue) 101 | { 102 | Header = obj.NewValue; 103 | } 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/DialogService.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.ApplicationLifetimes; 4 | using Avalonia.Platform.Storage; 5 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 6 | using JamSoft.AvaloniaUI.Dialogs.Views; 7 | 8 | namespace JamSoft.AvaloniaUI.Dialogs; 9 | 10 | /// 11 | /// The dialog service 12 | /// 13 | internal class DialogService : IDialogService 14 | { 15 | private readonly DialogServiceConfiguration _config; 16 | private string? _lastDirectorySelected; 17 | private readonly HashSet _openChildren = new(); 18 | 19 | /// 20 | /// Default constructor 21 | /// 22 | /// 23 | public DialogService(DialogServiceConfiguration config) 24 | { 25 | _config = config; 26 | } 27 | 28 | /// 29 | /// Shows a dialog with a callback to return the view model based on the result of the dialog. 30 | /// 31 | /// The type of the view model. 32 | /// The view model. 33 | /// The callback. 34 | public void ShowDialog(TViewModel viewModel, Action callback) 35 | where TViewModel : IDialogViewModel 36 | { 37 | var viewName = GetViewName(viewModel); 38 | var viewType = Type.GetType(viewName); 39 | if (viewType != null) 40 | { 41 | Control viewInstance = CreateViewInstance(viewType, viewName); 42 | ShowDialog(viewInstance, viewModel, callback); 43 | } 44 | else 45 | { 46 | throw new ArgumentNullException($"Could not find type {viewName}"); 47 | } 48 | } 49 | 50 | /// 51 | /// Shows a dialog with a callback to return the view model based on the result of the dialog. 52 | /// 53 | /// The type of the view model. 54 | /// The type of the view. 55 | /// The view. 56 | /// The view model. 57 | /// The callback. 58 | public async void ShowDialog(TView view, TViewModel viewModel, Action callback) 59 | where TView : Control where TViewModel : IDialogViewModel 60 | { 61 | var win = new DialogWindow 62 | { 63 | WindowStartupLocation = WindowStartupLocation.CenterOwner 64 | }; 65 | 66 | var contentControl = win.FindControl("Host"); 67 | contentControl!.Content = view; 68 | win.DataContext = viewModel; 69 | 70 | var accept = await win.ShowDialog(GetActiveWindowOrMainWindow()); 71 | if (accept) 72 | { 73 | callback(viewModel); 74 | } 75 | } 76 | 77 | /// 78 | /// Shows a child window. 79 | /// 80 | /// The type of the view model. 81 | /// The view model. 82 | /// the callback to received the view model instance on close 83 | public void ShowChildWindow(TViewModel viewModel, Action? callback) 84 | where TViewModel : class, IChildWindowViewModel 85 | { 86 | var viewName = GetViewName(viewModel); 87 | var viewType = Type.GetType(viewName); 88 | if (viewType != null) 89 | { 90 | Control viewInstance = CreateViewInstance(viewType, viewName); 91 | ShowChildWindow(viewInstance, viewModel, callback); 92 | } 93 | else 94 | { 95 | throw new TypeLoadException($"Could not find type {viewName}"); 96 | } 97 | } 98 | 99 | /// 100 | /// Shows a child window. 101 | /// 102 | /// The type of the view model. 103 | /// The type of the view. 104 | /// The view. 105 | /// The view model. 106 | /// the callback to receive the view model instance on close 107 | public void ShowChildWindow(TView view, TViewModel viewModel, Action? callback) 108 | where TView : Control where TViewModel : class, IChildWindowViewModel 109 | { 110 | // prevent multiple instances of the same child window 111 | if (_openChildren.FirstOrDefault(x => x?.GetType() == typeof(TViewModel)) != null) 112 | return; 113 | 114 | var win = new ChildWindow(); 115 | 116 | viewModel.ChildWindowTitle = CreateTitle(viewModel.ChildWindowTitle); 117 | 118 | var contentControl = win.FindControl("Host"); 119 | contentControl!.Content = view; 120 | win.DataContext = viewModel; 121 | 122 | _openChildren.Add(viewModel); 123 | win.Closing += (sender, _) => 124 | { 125 | if (sender is ChildWindow) 126 | { 127 | _openChildren.Clear(); 128 | } 129 | 130 | if (callback != null) 131 | callback(viewModel); 132 | }; 133 | win.Show(); 134 | } 135 | 136 | public void StartWizard(TViewModel viewModel, Action? callback) where TViewModel : class, IWizardViewModel 137 | { 138 | // prevent multiple instances of the same child window 139 | if (_openChildren.FirstOrDefault(x => x?.GetType() == typeof(TViewModel)) != null) 140 | return; 141 | 142 | var win = new ChildWindow(); 143 | 144 | viewModel.ChildWindowTitle = CreateTitle(viewModel.ChildWindowTitle); 145 | 146 | var contentControl = win.FindControl("Host"); 147 | 148 | var viewName = GetViewName(viewModel); 149 | var viewType = Type.GetType(viewName); 150 | 151 | var viewInstance = CreateViewInstance(viewType!, viewName); 152 | 153 | win.DataContext = viewModel; 154 | contentControl!.Content = viewInstance; 155 | 156 | _openChildren.Add(viewModel); 157 | win.Closing += (sender, _) => 158 | { 159 | if (sender is ChildWindow) 160 | { 161 | _openChildren.Clear(); 162 | } 163 | 164 | if (callback != null) 165 | callback(viewModel); 166 | }; 167 | 168 | win.Show(); 169 | } 170 | 171 | /// 172 | /// Launches a system folder dialog so the user can pick a system folder on disk. 173 | /// 174 | /// The dialog title 175 | /// the root directory to browse 176 | /// the selected folder path or null if the dialog was cancelled 177 | public async Task OpenFolder(string? title, string? startDirectory = null) 178 | { 179 | var storageProvider = GetStorageProvider(); 180 | var folder = await storageProvider.TryGetFolderFromPathAsync((startDirectory ?? _lastDirectorySelected)!); 181 | var path = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions 182 | { 183 | SuggestedStartLocation = folder, 184 | Title = CreateTitle(title) 185 | }); 186 | 187 | if (path.Count < 1) 188 | return null; 189 | 190 | _lastDirectorySelected = path[0].Path.LocalPath; 191 | 192 | return _lastDirectorySelected; 193 | } 194 | 195 | /// 196 | /// Gets a path for a new file 197 | /// 198 | /// The dialog title 199 | /// The file extension filters 200 | /// The default file extension 201 | /// The name used to pre-populate the save dialog 202 | /// the selected file path or null if the dialog was cancelled 203 | public async Task SaveFile(string title, IEnumerable? filters = null, string? defaultExtension = null, string? suggestedFileName = null) 204 | { 205 | var storageProvider = GetStorageProvider(); 206 | var folder = await storageProvider.TryGetFolderFromPathAsync(_lastDirectorySelected!); 207 | var fd = await storageProvider.SaveFilePickerAsync(new FilePickerSaveOptions 208 | { 209 | Title = CreateTitle(title), 210 | FileTypeChoices = filters?.ToList(), 211 | SuggestedStartLocation = folder, 212 | DefaultExtension = defaultExtension, 213 | SuggestedFileName = suggestedFileName 214 | }); 215 | 216 | return fd?.Path.LocalPath; 217 | } 218 | 219 | /// 220 | /// The individual file path 221 | /// 222 | /// The dialog title 223 | /// The file extension filters 224 | /// the selected file path or null if the dialog was cancelled 225 | public async Task OpenFile(string title, IEnumerable? filters = null) 226 | { 227 | var paths = await OpenFile(title, false, filters); 228 | if (paths != null && paths.Any()) 229 | { 230 | return paths[0]; 231 | } 232 | 233 | return null; 234 | } 235 | 236 | /// 237 | /// Returns multiple existing file paths 238 | /// 239 | /// The dialog title 240 | /// The file extension filters 241 | /// the selected file paths or null if the dialog was cancelled 242 | public async Task OpenFiles(string title, IEnumerable? filters = null) 243 | { 244 | var paths = await OpenFile(title, true, filters); 245 | if (paths != null && paths.Any()) 246 | { 247 | return paths; 248 | } 249 | 250 | return null; 251 | } 252 | 253 | private async Task OpenFile(string title, bool allowMultiFileSelection, IEnumerable? filters = null) 254 | { 255 | var storageProvider = GetStorageProvider(); 256 | var folder = await storageProvider.TryGetFolderFromPathAsync(_lastDirectorySelected!); 257 | var fd = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions 258 | { 259 | Title = CreateTitle(title), 260 | AllowMultiple = allowMultiFileSelection, 261 | FileTypeFilter = filters?.ToList(), 262 | SuggestedStartLocation = folder 263 | }); 264 | 265 | return fd.Select(x => x.Path.LocalPath).ToArray(); 266 | } 267 | 268 | private IStorageProvider GetStorageProvider() 269 | { 270 | var topLevel = TopLevel.GetTopLevel(GetActiveWindowOrMainWindow()); 271 | if (topLevel != null) 272 | { 273 | return topLevel.StorageProvider; 274 | } 275 | 276 | throw new InvalidOperationException("Cannot find a StorageProvider when TopLevel is null"); 277 | } 278 | 279 | private Window GetActiveWindowOrMainWindow() 280 | { 281 | if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 282 | { 283 | return (desktop.Windows.SingleOrDefault(x => x.IsActive) ?? desktop.MainWindow)!; 284 | } 285 | 286 | throw new InvalidOperationException("Cannot find a Window when ApplicationLifetime is not ClassicDesktopStyleApplicationLifetime"); 287 | } 288 | 289 | private string? CreateTitle(string? title) 290 | { 291 | if (_config.UseApplicationNameInTitle) 292 | return $"{_config.ApplicationName}-{title}"; 293 | 294 | return title; 295 | } 296 | 297 | private string GetViewName(TViewModel viewModel) where TViewModel : IDialogViewModel 298 | { 299 | if (viewModel == null) 300 | throw new ArgumentNullException(nameof(viewModel)); 301 | 302 | if (string.IsNullOrWhiteSpace(_config.ViewsAssemblyName)) 303 | throw new ArgumentNullException(nameof(_config.ViewsAssemblyName), 304 | "You must set the assembly name containing your views in the DialogServiceConfiguration instance"); 305 | 306 | var viewName = !string.IsNullOrWhiteSpace(_config.ViewsAssemblyName) 307 | ? $"{viewModel.GetType().FullName!.Replace("ViewModel", "View")},{_config.ViewsAssemblyName}" 308 | : ""; 309 | return viewName; 310 | } 311 | 312 | private static Control CreateViewInstance(Type viewType, string viewName) 313 | { 314 | Control? viewInstance; 315 | try 316 | { 317 | viewInstance = (Control)Activator.CreateInstance(viewType)!; 318 | } 319 | catch (Exception ex) 320 | { 321 | throw new TypeInitializationException(viewName, ex); 322 | } 323 | 324 | return viewInstance; 325 | } 326 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/DialogServiceConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs; 2 | 3 | /// 4 | /// Provides startup configuration details 5 | /// 6 | public class DialogServiceConfiguration 7 | { 8 | /// 9 | /// Set the application name in titles 10 | /// 11 | public bool UseApplicationNameInTitle { get; set; } 12 | 13 | /// 14 | /// The application name to display in titles 15 | /// 16 | public string? ApplicationName { get; set; } 17 | 18 | /// 19 | /// All 20 | /// 21 | public string? ViewsAssemblyName { get; set; } 22 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/DialogServiceFactory.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs; 2 | 3 | /// 4 | /// The dialog service factory 5 | /// 6 | public static class DialogServiceFactory 7 | { 8 | /// 9 | /// Creates a new instance of the dialog service using the provided configuration object 10 | /// 11 | /// The configuration object instance 12 | /// a new instance of 13 | public static IDialogService Create(DialogServiceConfiguration config) 14 | { 15 | return new DialogService(config); 16 | } 17 | 18 | /// 19 | /// Creates a new instance of the messagebox service 20 | /// 21 | /// a new instance of 22 | public static IMessageBoxService CreateMessageBoxService() 23 | { 24 | return new MessageBoxService(); 25 | } 26 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Events/EventHandlerUtils.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.Events; 2 | 3 | /// 4 | /// Event handler extensions 5 | /// 6 | public static class EventHandlerUtils 7 | { 8 | /// 9 | /// Makes a weak handler 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | public static EventHandler? MakeWeak(this EventHandler eventHandler, UnregisterCallback unregister) where TE : EventArgs 16 | { 17 | if (eventHandler == null) 18 | { 19 | throw new ArgumentNullException(nameof(eventHandler)); 20 | } 21 | 22 | if (eventHandler.Method.IsStatic || eventHandler.Target == null) 23 | { 24 | throw new ArgumentException(@"Only instance methods are supported.", nameof(eventHandler)); 25 | } 26 | 27 | if (eventHandler.Method.DeclaringType != null) 28 | { 29 | var wehType = typeof(WeakEventHandler<,>).MakeGenericType(eventHandler.Method.DeclaringType, typeof(TE)); 30 | 31 | var wehConstructor = wehType.GetConstructor(new Type[] 32 | { 33 | typeof(EventHandler), typeof(UnregisterCallback) 34 | }); 35 | 36 | if (wehConstructor != null) 37 | { 38 | IWeakEventHandler weh = (IWeakEventHandler)wehConstructor.Invoke(new object[] { eventHandler, unregister }); 39 | 40 | return weh.Handler; 41 | } 42 | } 43 | 44 | return null; 45 | } 46 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Events/IWeakEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.Events; 2 | 3 | /// 4 | /// The weak event handler interface 5 | /// 6 | /// 7 | public interface IWeakEventHandler where TE : EventArgs 8 | { 9 | /// 10 | /// The event handler 11 | /// 12 | EventHandler Handler { get; } 13 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Events/RequestCloseDialogEventArgs.cs: -------------------------------------------------------------------------------- 1 | using JamSoft.AvaloniaUI.Dialogs.MsgBox; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.Events; 4 | 5 | /// 6 | /// The request close dialog event 7 | /// 8 | public class RequestCloseDialogEventArgs : EventArgs 9 | { 10 | /// 11 | /// The dialog result 12 | /// 13 | public bool DialogResult { get; set; } 14 | 15 | /// 16 | /// Default constructor 17 | /// 18 | /// the dialog result instance 19 | public RequestCloseDialogEventArgs(bool dialogresult) 20 | { 21 | DialogResult = dialogresult; 22 | } 23 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Events/UnregisterCallback.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.Events; 2 | 3 | /// 4 | /// The unregister delegate 5 | /// 6 | /// 7 | public delegate void UnregisterCallback(EventHandler eventHandler) where TE : EventArgs; -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Events/WeakEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.Events; 2 | 3 | /// 4 | /// The weak event handler 5 | /// 6 | /// 7 | /// 8 | public class WeakEventHandler : IWeakEventHandler where T : class where TE : EventArgs 9 | { 10 | private delegate void OpenEventHandler(T @this, object sender, TE e); 11 | 12 | private readonly WeakReference _mTargetRef; 13 | private readonly OpenEventHandler _mOpenHandler; 14 | private readonly EventHandler _mHandler; 15 | // ReSharper disable once NotAccessedField.Local 16 | private UnregisterCallback _mUnregister; 17 | 18 | /// 19 | /// The default constructor 20 | /// 21 | /// 22 | /// 23 | public WeakEventHandler(EventHandler eventHandler, UnregisterCallback unregister) 24 | { 25 | _mTargetRef = new WeakReference(eventHandler.Target); 26 | 27 | _mOpenHandler = (OpenEventHandler)Delegate.CreateDelegate( 28 | typeof(OpenEventHandler), null, eventHandler.Method); 29 | 30 | _mHandler = Invoke; 31 | _mUnregister = unregister; 32 | } 33 | 34 | /// 35 | /// Invokes the event 36 | /// 37 | /// 38 | /// 39 | public void Invoke(object? sender, TE e) 40 | { 41 | T target = (T)_mTargetRef.Target!; 42 | 43 | if (sender != null) _mOpenHandler.Invoke(target, sender, e); 44 | } 45 | 46 | /// 47 | /// The event handler 48 | /// 49 | public EventHandler Handler 50 | { 51 | get { return _mHandler; } 52 | } 53 | 54 | /// 55 | /// The implicit operator 56 | /// 57 | /// 58 | /// 59 | public static implicit operator EventHandler(WeakEventHandler weh) 60 | { 61 | return weh._mHandler; 62 | } 63 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Helpers/CommonFilters.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Platform.Storage; 3 | 4 | namespace JamSoft.AvaloniaUI.Dialogs.Helpers; 5 | 6 | /// 7 | /// A class of common file filters 8 | /// 9 | // https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html 10 | public static class CommonFilters 11 | { 12 | /// 13 | /// Word files 14 | /// 15 | public static FilePickerFileType WordFilter = BuildFilter( 16 | "Word Files", 17 | new[] { "*.docx", "*.doc" }, 18 | new []{ "com.microsoft.word.doc", "org.openxmlformats.wordprocessingml.document" }, 19 | new []{ "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); 20 | 21 | /// 22 | /// Excel files 23 | /// 24 | public static FilePickerFileType ExcelFilter = BuildFilter( 25 | "Excel Files", 26 | new[] { "*.xlsx", "*.xls", "*.csv" }, 27 | new []{"com.microsoft.excel.xls", "org.openxmlformats.spreadsheetml.sheet", "public.comma-separated-values-text"}, 28 | new []{"application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}); 29 | 30 | /// 31 | /// HTML files 32 | /// 33 | public static FilePickerFileType HtmlFilter = BuildFilter( 34 | "HTML Files", 35 | new[] { "*.html", "*.htm" }, 36 | new []{"public.html"}, 37 | new []{"text/html"}); 38 | 39 | /// 40 | /// Txt Files 41 | /// 42 | public static FilePickerFileType TxtFilter = BuildFilter( 43 | "Text File", 44 | new[] { "*.txt" }, 45 | new []{"public.plain-text"}, 46 | new []{"text/plain"}); 47 | 48 | /// 49 | /// CSV Files 50 | /// 51 | public static FilePickerFileType CsvFilter = BuildFilter( 52 | "CSV File", 53 | new[] { "*.csv" }, 54 | new []{"public.comma-separated-values-text"}, 55 | new []{"text/csv"}); 56 | 57 | /// 58 | /// ZIP archive files 59 | /// 60 | public static FilePickerFileType ZipFilter = BuildFilter( 61 | "Zip File", 62 | new[] { "*.zip" }, 63 | new []{"public.zip-archive"}, 64 | new []{"application/zip"}); 65 | 66 | /// 67 | /// Rar Files 68 | /// 69 | public static FilePickerFileType RarFilter = BuildFilter( 70 | "Rar File", 71 | new[] { "*.rar" }, 72 | new []{"com.rar-archive"}, 73 | new []{"application/x-rar-compressed"}); 74 | 75 | /// 76 | /// Rpm Files 77 | /// 78 | public static FilePickerFileType RpmFilter = BuildFilter( 79 | "Rpm File", 80 | new[] { "*.rpm" }, 81 | new []{"com.redhat.package-archive"}, 82 | new []{"application/x-rpm"}); 83 | 84 | /// 85 | /// DMG Files 86 | /// 87 | public static FilePickerFileType DmgFilter = BuildFilter( 88 | "Dmg File", 89 | new[] { "*.dmg" }, 90 | new []{"com.apple.disk-image"}, 91 | new []{"application/x-apple-diskimage"}); 92 | 93 | /// 94 | /// Tarball Files 95 | /// 96 | public static FilePickerFileType TarFilter = BuildFilter( 97 | "Tarball File", 98 | new[] { "*.tar.gz" }, 99 | new []{"public.tar-archive"}, 100 | new []{"application/x-tar"}); 101 | 102 | /// 103 | /// Iso Files 104 | /// 105 | public static FilePickerFileType IsoFilter = BuildFilter( 106 | "Iso File", 107 | new[] { "*.iso" }, 108 | new []{"public.iso-image"}, 109 | new []{"application/x-iso9660-image"}); 110 | 111 | /// 112 | /// Wav Files 113 | /// 114 | public static FilePickerFileType WavFilter = BuildFilter( 115 | "Wav File", 116 | new[] { "*.wav" }, 117 | new []{"com.microsoft.waveform-audio"}, 118 | new []{ "audio/wav", "audio/wave" }); 119 | 120 | /// 121 | /// MP3 Files 122 | /// 123 | public static FilePickerFileType Mp3Filter = BuildFilter( 124 | "Mp3 File", new[] { "*.mp3" }, 125 | new []{"public.mp3"}, 126 | new []{"audio/mpeg", "audio/mpeg3", "audio/mpg", "audio/mp3", "audio/x-mpeg", "audio/x-mpeg3", " audio/x-mpg", "audio/x-mp3"}); 127 | 128 | /// 129 | /// DLL Files 130 | /// 131 | public static FilePickerFileType DllFilter = BuildFilter( 132 | "Dll File", 133 | new[] { "dll" }, 134 | new []{"com.microsoft.windows-dynamic-link-library"}, 135 | new []{"application/x-msdownload"}); 136 | 137 | /// 138 | /// XML Files 139 | /// 140 | public static FilePickerFileType XmlFilter = BuildFilter( 141 | "XML File", 142 | new[] { "*.xml" }, 143 | new []{"public.xml"}, 144 | new []{"text/xml", "application/xml"}); 145 | 146 | /// 147 | /// Email Files 148 | /// 149 | public static FilePickerFileType EmailFilter = BuildFilter( 150 | "Email Files", 151 | new[] { "*.eml", "*.email", "*.msg", "*.oft", "*.ost", "*.pst" }, 152 | new []{"com.microsoft.outlook.email-message"}, 153 | new []{"message/rfc822"}); 154 | 155 | /// 156 | /// EXE Files 157 | /// 158 | public static FilePickerFileType ExeFilter = BuildFilter( 159 | "Windows Executable", 160 | new[] { "*.exe" }, 161 | new []{"com.microsoft.windows-executable", "application/vnd.microsoft.portable-executable" }, 162 | new []{"application/x-msdownload"}); 163 | 164 | /// 165 | /// Batch Files 166 | /// 167 | public static FilePickerFileType BatchFilter = BuildFilter( 168 | "Windows Batch File", 169 | new[] { "*.bat" }, 170 | new []{"com.microsoft.windows-batch"}, 171 | new []{"application/x-msdownload"}); 172 | 173 | /// 174 | /// TTF Font Files 175 | /// 176 | public static FilePickerFileType TtfFontFilter = BuildFilter( 177 | "TrueType Font File", 178 | new[] { "*.ttf" }, 179 | new []{"public.truetype-ttf-font"}, 180 | new []{"application/x-font-ttf"}); 181 | 182 | /// 183 | /// Illustrator Files 184 | /// 185 | public static FilePickerFileType IllustratorFilter = BuildFilter( 186 | "Adobe Illustrator File", 187 | new[] { "*.ai" }, 188 | new []{"com.adobe.illustrator.ai-image"}, 189 | new []{"application/illustrator"}); 190 | 191 | /// 192 | /// Bitmap Files 193 | /// 194 | public static FilePickerFileType BitmapFilter = BuildFilter( 195 | "Bitmap Image", 196 | new[] { "*.bmp" }, 197 | new []{"com.microsoft.bmp"}, 198 | new []{"image/bmp"} ); 199 | 200 | /// 201 | /// GIF files 202 | /// 203 | public static FilePickerFileType GifFilter = BuildFilter( 204 | "GIF Image", 205 | new[] { "*.gif" }, 206 | new []{"com.compuserve.gif"}, 207 | new []{"image/gif"}); 208 | 209 | /// 210 | /// ICO Files 211 | /// 212 | public static FilePickerFileType IcoFilter = BuildFilter( 213 | "Icon File", 214 | new[] { "*.ico" }, 215 | new []{"com.microsoft.ico"}, 216 | new []{"image/x-icon"}); 217 | 218 | /// 219 | /// JPEG Files 220 | /// 221 | public static FilePickerFileType JpegFilter = BuildFilter( 222 | "JPEG Image", 223 | new[] { "*.jpg", "*.jpeg" }, 224 | new []{"public.jpeg"}, 225 | new []{"image/jpeg"} ); 226 | 227 | /// 228 | /// PNG Files 229 | /// 230 | public static FilePickerFileType PngFilter = BuildFilter( 231 | "PNG Image", 232 | new[] { "*.png" }, 233 | new []{"public.png"}, 234 | new []{"image/png"}); 235 | 236 | /// 237 | /// PostScript Files 238 | /// 239 | public static FilePickerFileType PostScriptFilter = BuildFilter( 240 | "PostScript File", 241 | new[] { "*.ps" }, 242 | new []{"com.adobe.postscript"}, 243 | new []{"application/postscript"}); 244 | 245 | /// 246 | /// PSD Files 247 | /// 248 | public static FilePickerFileType PsdFilter = BuildFilter( 249 | "Photoshop Image", 250 | new[] { "*.psd" }, 251 | new []{"com.adobe.photoshop-image"}, 252 | new []{"image/vnd.adobe.photoshop"} ); 253 | 254 | /// 255 | /// SVG Files 256 | /// 257 | public static FilePickerFileType SvgFilter = BuildFilter( 258 | "Scalable Vector Graphics File", 259 | new[] { "*.svg" }, 260 | new []{"public.svg-image"}, 261 | new []{"image/svg+xml"} ); 262 | 263 | /// 264 | /// TIFF Files 265 | /// 266 | public static FilePickerFileType TiffFilter = BuildFilter( 267 | "TIFF Image", 268 | new[] { "*.tif", "*.tiff" }, 269 | new []{"public.tiff"}, 270 | new []{"image/tiff"} ); 271 | 272 | /// 273 | /// WebP Files 274 | /// 275 | public static FilePickerFileType WebpFilter = BuildFilter( 276 | "WebP Image", 277 | new[] { "*.webp" }, 278 | new []{"public.webp"}, 279 | new []{"image/webp"}); 280 | 281 | /// 282 | /// Apple Keynote Files 283 | /// 284 | public static FilePickerFileType KeynoteFilter = BuildFilter( 285 | "Keynote Presentation", 286 | new[] { "*.key" }, 287 | new []{"com.apple.keynote.key"}, 288 | new []{"application/x-iwork-keynote-sffkey"}); 289 | 290 | /// 291 | /// OpenOffice Impress Files 292 | /// 293 | public static FilePickerFileType OpenOfficeImpressFilter = BuildFilter( 294 | "OpenOffice Impress Presentation File", 295 | new[] { "*.odp" }, 296 | new []{"org.openoffice.presentation"}, 297 | new []{"application/vnd.oasis.opendocument.presentation"}); 298 | 299 | /// 300 | /// Powerpoint Slide Show Files 301 | /// 302 | public static FilePickerFileType PowerPointFilter = BuildFilter( 303 | "PowerPoint Slide Show", 304 | new[]{ "*.pps" }, 305 | new []{"com.microsoft.powerpoint.pps"}, 306 | new []{"application/vnd.ms-powerpoint" }); 307 | 308 | /// 309 | /// Powerpoint Presentation Files 310 | /// 311 | public static FilePickerFileType PowerpointFilter = BuildFilter( 312 | "PowerPoint Presentation", 313 | new[] { "*.pptx", "*.ppt" }, 314 | new []{"com.microsoft.powerpoint.ppt", "org.openxmlformats.presentationml.presentation"}, 315 | new []{"application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}); 316 | 317 | /// 318 | /// AVI Files 319 | /// 320 | public static FilePickerFileType AviFilter = BuildFilter( 321 | "AVI Video File", 322 | new[] { "*.avi" }, 323 | new []{"public.avi"}, 324 | new []{"video/avi"} ); 325 | 326 | /// 327 | /// MP4 Files 328 | /// 329 | public static FilePickerFileType Mp4Filter = BuildFilter( 330 | "MPEG4 Video File", 331 | new[] { "*.mp4" }, 332 | new []{"public.mpeg-4"}, 333 | new []{"video/mp4"} ); 334 | 335 | /// 336 | /// MPEG Files 337 | /// 338 | public static FilePickerFileType MpegFilter = BuildFilter( 339 | "MPEG Video File", 340 | new[] { "mpg", "mpeg" }, 341 | new []{"public.mpeg"}, 342 | new []{"video/mpeg"}); 343 | 344 | /// 345 | /// PDF Files 346 | /// 347 | public static FilePickerFileType PdfFilter = BuildFilter( 348 | "PDF File", 349 | new[] { "*.pdf" }, 350 | new []{"com.adobe.pdf"}, 351 | new []{"application/pdf"}); 352 | 353 | /// 354 | /// Rich Text Files 355 | /// 356 | public static FilePickerFileType RichTextFilter = BuildFilter( 357 | "Rich Text Format", 358 | new[] { "*.rtf" }, 359 | new []{"public.rtf"}, 360 | new []{"text/rtf"} ); 361 | 362 | /// 363 | /// ODT Files 364 | /// 365 | public static FilePickerFileType OdtFilter = BuildFilter( 366 | "OpenOffice Writer Document File", 367 | new[] { "*.odt" }, 368 | new []{"org.openoffice.text"}, 369 | new []{"application/vnd.oasis.opendocument.text"}); 370 | 371 | /// 372 | /// Builds a filter instance 373 | /// 374 | /// The name of the filter 375 | /// the array of file extensions 376 | /// the array of apple UTIs 377 | /// the array of mime types 378 | /// a instance 379 | // ReSharper disable once MemberCanBePrivate.Global 380 | public static FilePickerFileType BuildFilter(string name, string[] extensions, string[] appleIds, string[] mimeTypes) 381 | { 382 | return new FilePickerFileType(name) 383 | { 384 | Patterns = new List(extensions), 385 | AppleUniformTypeIdentifiers = new List(appleIds), 386 | MimeTypes = new List(mimeTypes) 387 | }; 388 | } 389 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Helpers/CommonFiltersExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Platform.Storage; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.Helpers; 4 | 5 | /// 6 | /// A class containing extension methods for to allow for easier merging of file types. 7 | /// 8 | public static class CommonFiltersExtensionMethods 9 | { 10 | /// 11 | /// Merges the specified file type with the other file types. 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | public static FilePickerFileType MergeWith(this FilePickerFileType fileType, FilePickerFileType[] others, string? name) 18 | { 19 | if (!string.IsNullOrWhiteSpace(name)) 20 | { 21 | for(int i = 0;i< others.Length;i++) 22 | { 23 | if (i < others.Length - 1) 24 | { 25 | fileType.MergeWith(others[i], name); 26 | } 27 | else 28 | { 29 | fileType.MergeWith(others[i]); 30 | } 31 | } 32 | } 33 | 34 | foreach (var ft in others) 35 | { 36 | fileType.MergeWith(ft); 37 | } 38 | 39 | return fileType; 40 | } 41 | 42 | /// 43 | /// merges the specified file type with the other file type. 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | public static FilePickerFileType MergeWith(this FilePickerFileType fileType, FilePickerFileType? other, string? name = null) 50 | { 51 | if (other == null) 52 | return fileType; 53 | 54 | if (fileType.Patterns != null) 55 | { 56 | if (other.Patterns != null) 57 | { 58 | fileType.Patterns = fileType.Patterns.Concat(other.Patterns).ToArray(); 59 | } 60 | } 61 | else 62 | { 63 | fileType.Patterns = other.Patterns; 64 | } 65 | 66 | if (fileType.AppleUniformTypeIdentifiers != null) 67 | { 68 | if (other.AppleUniformTypeIdentifiers != null) 69 | { 70 | fileType.AppleUniformTypeIdentifiers = fileType.AppleUniformTypeIdentifiers.Concat(other.AppleUniformTypeIdentifiers).ToArray(); 71 | } 72 | } 73 | else 74 | { 75 | fileType.AppleUniformTypeIdentifiers = other.AppleUniformTypeIdentifiers; 76 | } 77 | 78 | if (fileType.MimeTypes != null) 79 | { 80 | if (other.MimeTypes != null) 81 | { 82 | fileType.MimeTypes = fileType.MimeTypes.Concat(other.MimeTypes).ToArray(); 83 | } 84 | } 85 | else 86 | { 87 | fileType.MimeTypes = other.MimeTypes; 88 | } 89 | 90 | if (!string.IsNullOrWhiteSpace(name)) 91 | { 92 | return new FilePickerFileType(name) 93 | { 94 | Patterns = fileType.Patterns, 95 | AppleUniformTypeIdentifiers = fileType.AppleUniformTypeIdentifiers, 96 | MimeTypes = fileType.MimeTypes 97 | }; 98 | } 99 | 100 | return fileType; 101 | } 102 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/IDialogService.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Platform.Storage; 3 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 4 | 5 | namespace JamSoft.AvaloniaUI.Dialogs; 6 | 7 | /// 8 | /// The dialog service interface 9 | /// 10 | public interface IDialogService 11 | { 12 | /// 13 | /// Shows a dialog with a callback to return the view model based on the result of the dialog. 14 | /// 15 | /// The type of the view model. 16 | /// The view model. 17 | /// The callback. 18 | void ShowDialog(TViewModel viewModel, Action callback) where TViewModel : IDialogViewModel; 19 | 20 | /// 21 | /// Shows a dialog with a callback to return the view model based on the result of the dialog. 22 | /// 23 | /// The type of the view model. 24 | /// The type of the view. 25 | /// The view. 26 | /// The view model. 27 | /// The callback. 28 | void ShowDialog(TView view, TViewModel viewModel, Action callback) where TView : Control where TViewModel : IDialogViewModel; 29 | 30 | /// 31 | /// Shows a child window. 32 | /// 33 | /// The type of the view model. 34 | /// The view model. 35 | /// the callback to received the view model instance on close 36 | void ShowChildWindow(TViewModel viewModel, Action? callback) where TViewModel : class, IChildWindowViewModel; 37 | 38 | /// 39 | /// Shows a child window. 40 | /// 41 | /// The type of the view model. 42 | /// The type of the view. 43 | /// The view. 44 | /// The view model. 45 | /// the callback to received the view model instance on close 46 | void ShowChildWindow(TView view, TViewModel viewModel, Action? callback = null) where TView : Control where TViewModel : class, IChildWindowViewModel; 47 | 48 | /// 49 | /// Shows a wizard view 50 | /// 51 | /// The view model. 52 | /// the callback to received the view model instance on close 53 | /// The type of the view model. 54 | void StartWizard(TViewModel viewModel, Action? callback) where TViewModel : class, IWizardViewModel; 55 | 56 | /// 57 | /// Launches a system folder dialog so the user can pick a system folder on disk. 58 | /// 59 | /// The dialog title 60 | /// the root directory to browse 61 | /// the selected folder path or null if the dialog was cancelled 62 | Task OpenFolder(string title, string? startDirectory = null); 63 | 64 | /// 65 | /// Gets a path for a new file 66 | /// 67 | /// The dialog title 68 | /// The file extension filters 69 | /// The default file extension 70 | /// The name used to pre-populate the save dialog 71 | /// the selected file path or null if the dialog was cancelled 72 | Task SaveFile(string title, IEnumerable? filters = null, string? defaultExtension = null, string? suggestedFileName = null); 73 | 74 | /// 75 | /// Gets an individual file path 76 | /// 77 | /// The dialog title 78 | /// The file extension filters 79 | /// the selected file path or null if the dialog was cancelled 80 | Task OpenFile(string title, IEnumerable? filters = null); 81 | 82 | /// 83 | /// Returns multiple existing file paths 84 | /// 85 | /// The dialog title 86 | /// The file extension filters 87 | /// the selected file paths or null if the dialog was cancelled 88 | Task OpenFiles(string title, IEnumerable? filters = null); 89 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/IMessageBoxService.cs: -------------------------------------------------------------------------------- 1 | using JamSoft.AvaloniaUI.Dialogs.MsgBox; 2 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 3 | 4 | namespace JamSoft.AvaloniaUI.Dialogs; 5 | 6 | /// 7 | /// The message box service interface 8 | /// 9 | public interface IMessageBoxService 10 | { 11 | /// 12 | /// Show a message box with the specified parameters 13 | /// 14 | /// the messagebox caption 15 | /// the message text 16 | /// the button configuration 17 | /// the icon enum 18 | /// the negative response button text value 19 | /// the positive response button text used for Yes/OK buttons 20 | /// the cancel response button text 21 | /// The checkbox text 22 | /// the response 23 | Task Show(string caption, string messageBoxText, MsgBoxButton button, MsgBoxImage icon = MsgBoxImage.None, string? noButtonText = null, string? yesButtonText = null, string? cancelButtonText = null, string? checkBoxText = null); 24 | 25 | /// 26 | /// Show a message box with the specified view model 27 | /// 28 | /// the view model instance 29 | /// the response 30 | Task Show(IMsgBoxViewModel viewModel); 31 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/JamSoft.AvaloniaUI.Dialogs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | enable 6 | enable 7 | 10 8 | true 9 | JamSoft AvaloniaUI Dialogs 10 | JamSoft Solution Ltd 11 | JamSoft AvaloniaUI Dialog Library 12 | James Green 13 | A standardised service for managing dialogs and child windows in Avalonia UI. 14 | JamSoft Solutions Ltd 2025 15 | https://github.com/jamsoft/JamSoft.AvaloniaUI.Dialogs 16 | https://github.com/jamsoft/JamSoft.AvaloniaUI.Dialogs 17 | git 18 | avalonia, avaloniaui, dialogs, mvvm, wizard, messagebox 19 | true 20 | JamSoftLogo-Splat513x513.png 21 | true 22 | JamSoft.AssemblyKeyFile.snk 23 | true 24 | 1.4.2 25 | 1.4.2.0 26 | 1.4.2.0 27 | MIT 28 | 1.4.2.0-rel 29 | true 30 | README.md 31 | 32 | 33 | 34 | true 35 | 36 | 37 | 38 | bin\Release\JamSoft.AvaloniaUI.Dialogs.xml 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | True 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | MsgBoxWindow.axaml 67 | Code 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/JamSoftLogo-Splat513x513.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/ef3c8958c583960d6c843fc78f36c71c27bdf330/src/JamSoft.AvaloniaUI.Dialogs/JamSoftLogo-Splat513x513.png -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/MessageBoxService.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.ApplicationLifetimes; 4 | using JamSoft.AvaloniaUI.Dialogs.MsgBox; 5 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 6 | using JamSoft.AvaloniaUI.Dialogs.Views; 7 | 8 | namespace JamSoft.AvaloniaUI.Dialogs; 9 | 10 | internal class MessageBoxService : IMessageBoxService 11 | { 12 | public Task Show(string caption, string messageBoxText, MsgBoxButton button, MsgBoxImage icon = MsgBoxImage.None, 13 | string? noButtonText = null, string? yesButtonText = null, string? cancelButtonText = null, string? checkBoxText = null) 14 | { 15 | return Show(new MsgBoxViewModel(caption, messageBoxText, button, icon, noButtonText, yesButtonText, cancelButtonText, checkBoxText)); 16 | } 17 | 18 | public Task Show(IMsgBoxViewModel? viewModel) 19 | { 20 | if (viewModel == null) 21 | return Task.FromResult(MsgBoxResult.CreateResult(false, MsgBoxButtonResult.None)); 22 | 23 | if (Application.Current != null && 24 | Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop.MainWindow != null) 25 | { 26 | return ShowWindow(desktop.MainWindow, viewModel); 27 | } 28 | 29 | if (Application.Current != null && 30 | Application.Current.ApplicationLifetime is ISingleViewApplicationLifetime lifetime) 31 | { 32 | return ShowPopup(lifetime.MainView as ContentControl); 33 | } 34 | 35 | throw new NotSupportedException("ApplicationLifetime is not supported"); 36 | } 37 | 38 | private Task ShowWindow(Window owner, IMsgBoxViewModel viewModel) 39 | { 40 | var view = new MsgBoxView(); 41 | var win = new MsgBoxWindow(); 42 | 43 | var contentControl = win.FindControl("Host"); 44 | contentControl!.Content = view; 45 | win.DataContext = viewModel; 46 | win.WindowStartupLocation = viewModel.WindowStartupLocation; 47 | win.Topmost = viewModel.Topmost; 48 | 49 | var tcs = new TaskCompletionSource(); 50 | win.Closing += (_, _) => 51 | { 52 | tcs.TrySetResult(MsgBoxResult.CreateResult(viewModel.CheckBoxResult, viewModel.Result)); 53 | }; 54 | 55 | win.Show(owner); 56 | return tcs.Task; 57 | } 58 | 59 | /// 60 | /// Show messagebox as popup 61 | /// 62 | /// 63 | /// 64 | public Task ShowPopup(ContentControl? owner) 65 | { 66 | var tcs = new TaskCompletionSource(); 67 | return tcs.Task; 68 | } 69 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/MsgBox/IconResolver.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media.Imaging; 2 | using Avalonia.Platform; 3 | 4 | namespace JamSoft.AvaloniaUI.Dialogs.MsgBox; 5 | 6 | /// 7 | /// Resolves the icon for the message box 8 | /// 9 | public static class IconResolver 10 | { 11 | private static Dictionary _icons = new() 12 | { 13 | { MsgBoxImage.Asterisk, "exclamation.png" }, 14 | { MsgBoxImage.Exclamation, "diamond-exclamation.png" }, 15 | { MsgBoxImage.Hand, "cross-circle.png" }, 16 | { MsgBoxImage.Stop, "cross-circle.png" }, 17 | { MsgBoxImage.Error, "cross-circle.png" }, 18 | { MsgBoxImage.Question, "interrogation.png" }, 19 | { MsgBoxImage.Warning, "diamond-exclamation.png" }, 20 | { MsgBoxImage.Information, "info.png" }, 21 | { MsgBoxImage.Custom, "Custom" }, 22 | { MsgBoxImage.Success, "check-circle.png" }, 23 | { MsgBoxImage.Battery, "battery-half.png" }, 24 | { MsgBoxImage.Database, "database.png" }, 25 | { MsgBoxImage.Folder, "folder-open.png" }, 26 | { MsgBoxImage.Forbidden, "ban.png" }, 27 | { MsgBoxImage.Plus, "add.png" }, 28 | { MsgBoxImage.Setting, "customize.png" }, 29 | { MsgBoxImage.Wifi, "wifi.png" } 30 | }; 31 | 32 | /// 33 | /// Resolves the icon for the message box 34 | /// 35 | /// 36 | /// 37 | public static Bitmap? Resolve(MsgBoxImage icon) 38 | { 39 | if (_icons.TryGetValue(icon, out var iconName)) 40 | { 41 | return new Bitmap(AssetLoader.Open(new Uri($"avares://JamSoft.AvaloniaUI.Dialogs/Assets/{iconName}"))); 42 | } 43 | 44 | return null; 45 | } 46 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/MsgBox/MsgBoxButton.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.MsgBox; 2 | 3 | /// 4 | /// The message box button enum 5 | /// 6 | public enum MsgBoxButton 7 | { 8 | /// 9 | /// Show the OK button 10 | /// 11 | Ok, 12 | 13 | /// 14 | /// Show the OK and Cancel buttons 15 | /// 16 | OkCancel, 17 | 18 | /// 19 | /// Show the Yes and No buttons 20 | /// 21 | YesNo, 22 | 23 | /// 24 | /// Show the Yes, No and Cancel buttons 25 | /// 26 | YesNoCancel 27 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/MsgBox/MsgBoxButtonResult.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.MsgBox; 2 | 3 | /// 4 | /// The message box result enum 5 | /// 6 | public enum MsgBoxButtonResult 7 | { 8 | /// 9 | /// The default value 10 | /// 11 | None, 12 | 13 | /// 14 | /// The OK value 15 | /// 16 | Ok, 17 | 18 | /// 19 | /// The Cancel value 20 | /// 21 | Cancel, 22 | 23 | /// 24 | /// The Yes value 25 | /// 26 | Yes, 27 | 28 | /// 29 | /// The No value 30 | /// 31 | No 32 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/MsgBox/MsgBoxImage.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.MsgBox; 2 | 3 | /// 4 | /// Specifies the icon to display in a message box 5 | /// 6 | public enum MsgBoxImage 7 | { 8 | /// 9 | /// Show no icon 10 | /// 11 | None, 12 | 13 | /// 14 | /// Show the Asterisk icon 15 | /// 16 | Asterisk, 17 | 18 | /// 19 | /// Show the Exclamation icon 20 | /// 21 | Exclamation, 22 | 23 | /// 24 | /// Show the Hand icon 25 | /// 26 | Hand, 27 | 28 | /// 29 | /// Show the Stop icon 30 | /// 31 | Stop, 32 | 33 | /// 34 | /// Show the Error icon 35 | /// 36 | Error, 37 | 38 | /// 39 | /// Show the Question icon 40 | /// 41 | Question, 42 | 43 | /// 44 | /// Show the Warning icon 45 | /// 46 | Warning, 47 | 48 | /// 49 | /// Show the Information icon 50 | /// 51 | Information, 52 | 53 | /// 54 | /// Show a custom icon 55 | /// 56 | Custom, 57 | 58 | /// 59 | /// Show the Success icon 60 | /// 61 | Success, 62 | 63 | /// 64 | /// Show the Battery icon 65 | /// 66 | Battery, 67 | 68 | /// 69 | /// Show the Database icon 70 | /// 71 | Database, 72 | 73 | /// 74 | /// Show the Folder icon 75 | /// 76 | Folder, 77 | 78 | /// 79 | /// Show the Forbidden icon 80 | /// 81 | Forbidden, 82 | 83 | /// 84 | /// Show the Plus icon 85 | /// 86 | Plus, 87 | 88 | /// 89 | /// Show the Setting icon 90 | /// 91 | Setting, 92 | 93 | /// 94 | /// Show the Wifi icon 95 | /// 96 | Wifi 97 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/MsgBox/MsgBoxResult.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.MsgBox; 2 | 3 | /// 4 | /// The message box result enum 5 | /// 6 | public sealed class MsgBoxResult 7 | { 8 | /// 9 | /// The message box button result enum 10 | /// 11 | public MsgBoxButtonResult ButtonResult { get; } 12 | 13 | /// 14 | /// The message box check box result 15 | /// 16 | public bool CheckBoxResult { get; } 17 | 18 | private MsgBoxResult(bool checkBoxResult, MsgBoxButtonResult buttonResult) 19 | { 20 | CheckBoxResult = checkBoxResult; 21 | ButtonResult = buttonResult; 22 | } 23 | 24 | /// 25 | /// Creates a new message box result instance 26 | /// 27 | /// 28 | /// 29 | /// 30 | public static MsgBoxResult CreateResult(bool checkBoxChecked, MsgBoxButtonResult buttonResult) => new(checkBoxChecked, buttonResult); 31 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Themes/ChildStyle.axaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 13 | 14 | 43 | 44 | 47 | 48 | 52 | 53 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Themes/Default.axaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Themes/ModalStyle.axaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Themes/MsgBoxStyles.axaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Themes/WizardStepStyle.axaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 48 16 | 24 17 | 2 18 | 19 | 20 | 43 | 44 | 45 | 48 | 51 | 52 | 55 | 56 | 57 | 60 | 61 | 65 | 66 | 69 | 70 | 71 | 75 | 76 | 77 | 81 | 82 | 83 | 90 | 93 | 94 | 100 | 101 | 108 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Themes/WizardStyle.axaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 0 0 0 2 23 | 24 | 25 | 65 | 66 | 69 | 70 | 73 | 74 | 77 | 78 | 81 | 82 | 85 | 86 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/ChildWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Media; 3 | using Avalonia.Media.Imaging; 4 | using Avalonia.Platform; 5 | 6 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 7 | 8 | /// 9 | /// The default child window view model 10 | /// 11 | public abstract class ChildWindowViewModel : DialogViewModel, IChildWindowViewModel 12 | { 13 | private double _requestedTop; 14 | private double _requestedLeft; 15 | private double _requestedWidth; 16 | private double _requestedHeight; 17 | private string? _childWindowTitle; 18 | private IImage _closeIcon; 19 | 20 | /// 21 | /// Default ctor 22 | /// 23 | protected ChildWindowViewModel() 24 | { 25 | _closeIcon = new Bitmap(AssetLoader.Open(new Uri("avares://JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-30.png"))); 26 | } 27 | 28 | /// 29 | /// The child window title 30 | /// 31 | public string? ChildWindowTitle 32 | { 33 | get => _childWindowTitle; 34 | set => RaiseAndSetIfChanged(ref _childWindowTitle, value); 35 | } 36 | 37 | /// 38 | /// The child window requested top value 39 | /// 40 | public double RequestedTop 41 | { 42 | get => _requestedTop; 43 | set => RaiseAndSetIfChanged(ref _requestedTop, value); 44 | } 45 | 46 | /// 47 | /// The child window requested left value 48 | /// 49 | public double RequestedLeft 50 | { 51 | get => _requestedLeft; 52 | set => RaiseAndSetIfChanged(ref _requestedLeft, value); 53 | } 54 | 55 | /// 56 | /// The child window width 57 | /// 58 | public double RequestedWidth 59 | { 60 | get => _requestedWidth; 61 | set => RaiseAndSetIfChanged(ref _requestedWidth, value); 62 | } 63 | 64 | /// 65 | /// The child window height 66 | /// 67 | public double RequestedHeight 68 | { 69 | get => _requestedHeight; 70 | set => RaiseAndSetIfChanged(ref _requestedHeight, value); 71 | } 72 | 73 | /// 74 | /// The child window startup location 75 | /// 76 | public WindowStartupLocation Location { get; set; } 77 | 78 | /// 79 | /// The close icon 80 | /// 81 | public IImage CloseIcon 82 | { 83 | get => _closeIcon; 84 | set => RaiseAndSetIfChanged(ref _closeIcon, value); 85 | } 86 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/DialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Runtime.CompilerServices; 3 | using System.Windows.Input; 4 | using JamSoft.AvaloniaUI.Dialogs.Commands; 5 | using JamSoft.AvaloniaUI.Dialogs.Events; 6 | 7 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 8 | 9 | /// 10 | /// The default dialog view model 11 | /// 12 | public class DialogViewModel : IDialogViewModel 13 | { 14 | private ICommand _acceptCommand; 15 | 16 | /// 17 | /// The dialog accept command 18 | /// 19 | public ICommand AcceptCommand 20 | { 21 | get => _acceptCommand; 22 | set => RaiseAndSetIfChanged(ref _acceptCommand, value); 23 | } 24 | 25 | private string? _acceptCommandText; 26 | 27 | /// 28 | /// The dialog cancel command 29 | /// 30 | public string? AcceptCommandText 31 | { 32 | get => _acceptCommandText; 33 | set => RaiseAndSetIfChanged(ref _acceptCommandText, value); 34 | } 35 | 36 | private ICommand _cancelCommand; 37 | 38 | /// 39 | /// Gets or sets the cancel command. 40 | /// 41 | /// 42 | /// The cancel command. 43 | /// 44 | public ICommand CancelCommand 45 | { 46 | get => _cancelCommand; 47 | set => RaiseAndSetIfChanged(ref _cancelCommand, value); 48 | } 49 | 50 | private string? _cancelCommandText; 51 | 52 | /// 53 | /// The dialog cancel command text 54 | /// 55 | public string? CancelCommandText 56 | { 57 | get => _cancelCommandText; 58 | set => RaiseAndSetIfChanged(ref _cancelCommandText, value); 59 | } 60 | 61 | /// 62 | /// Occurs when [request close dialog] event is fired. 63 | /// 64 | public event EventHandler? RequestCloseDialog; 65 | 66 | /// 67 | /// The default constructor 68 | /// 69 | protected DialogViewModel() 70 | { 71 | _acceptCommand = new DelegateCommand(() => InvokeRequestCloseDialog(new RequestCloseDialogEventArgs(true)), CanAccept); 72 | AcceptCommandText = "OK"; 73 | _cancelCommand = new DelegateCommand(() => InvokeRequestCloseDialog(new RequestCloseDialogEventArgs(false)), CanCancel); 74 | CancelCommandText = "Cancel"; 75 | } 76 | 77 | /// 78 | /// Invokes the request close dialog event 79 | /// 80 | /// 81 | protected void InvokeRequestCloseDialog(RequestCloseDialogEventArgs e) 82 | { 83 | RequestCloseDialog?.Invoke(this, e); 84 | } 85 | 86 | /// 87 | /// Implements the logic that controls the accept command execution validation 88 | /// 89 | /// 90 | public virtual bool CanAccept() 91 | { 92 | return true; 93 | } 94 | 95 | /// 96 | /// Implements the logic that controls the cancel command execution validation 97 | /// 98 | /// 99 | public virtual bool CanCancel() 100 | { 101 | return true; 102 | } 103 | 104 | private bool _hideCancelButton; 105 | 106 | /// 107 | /// Optionally hides the cancel button 108 | /// 109 | public bool HideCancelButton 110 | { 111 | get => _hideCancelButton; 112 | set => RaiseAndSetIfChanged(ref _hideCancelButton, value); 113 | } 114 | 115 | /// 116 | /// Sets the property value only if the provided value is different from the stored value. 117 | /// If set to a new value, then the event will be fired 118 | /// 119 | /// 120 | /// The storage. 121 | /// The value. 122 | /// Name of the property. 123 | /// 124 | protected virtual bool RaiseAndSetIfChanged(ref T storage, T value, [CallerMemberName] string propertyName = "") 125 | { 126 | if (EqualityComparer.Default.Equals(storage, value)) 127 | { 128 | return false; 129 | } 130 | 131 | storage = value; 132 | OnPropertyChanged(propertyName); 133 | 134 | (AcceptCommand as DelegateCommand)?.RaiseCanExecuteChanged(); 135 | (CancelCommand as DelegateCommand)?.RaiseCanExecuteChanged(); 136 | 137 | return true; 138 | } 139 | 140 | /// 141 | /// Raises the event. Allows the use of a specific PropertyChangedEventArgs object. 142 | /// Is the most performant implementation 143 | /// 144 | /// The instance containing the event data. 145 | protected virtual void OnPropertyChanged(PropertyChangedEventArgs prop) 146 | { 147 | PropertyChanged?.Invoke(this, prop); 148 | } 149 | 150 | /// 151 | /// Fires the property changed event using either the or the provided string property name. 152 | /// 153 | /// Name of the property. 154 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 155 | { 156 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 157 | } 158 | 159 | /// 160 | /// The property changed event 161 | /// 162 | public event PropertyChangedEventHandler? PropertyChanged; 163 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/IChildWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 4 | 5 | /// 6 | /// The child window view model interface 7 | /// 8 | public interface IChildWindowViewModel : IDialogViewModel, IWindowPositionAware 9 | { 10 | /// 11 | /// The child window title 12 | /// 13 | string? ChildWindowTitle { get; set; } 14 | 15 | /// 16 | /// The child window width 17 | /// 18 | double RequestedWidth { get; set; } 19 | 20 | /// 21 | /// The child window height 22 | /// 23 | double RequestedHeight { get; set; } 24 | 25 | /// 26 | /// The close icon 27 | /// 28 | IImage CloseIcon { get; set; } 29 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/IDialogResultVmHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | using JamSoft.AvaloniaUI.Dialogs.Commands; 3 | using JamSoft.AvaloniaUI.Dialogs.Events; 4 | 5 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 6 | 7 | /// 8 | /// The dialog result helper 9 | /// 10 | public interface IDialogResultVmHelper 11 | { 12 | /// 13 | /// Occurs when [request close dialog] event is fired. 14 | /// 15 | event EventHandler RequestCloseDialog; 16 | 17 | /// 18 | /// Gets or sets the accept command. 19 | /// 20 | /// 21 | /// The accept command. 22 | /// 23 | ICommand AcceptCommand { get; set; } 24 | 25 | /// 26 | /// Gets or sets the cancel command. 27 | /// 28 | /// 29 | /// The cancel command. 30 | /// 31 | ICommand CancelCommand { get; set; } 32 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/IDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 4 | 5 | /// 6 | /// The dialog view model interface 7 | /// 8 | public interface IDialogViewModel : INotifyPropertyChanged, IDialogResultVmHelper 9 | { 10 | /// 11 | /// The dialog accept command text 12 | /// 13 | string? AcceptCommandText { get; set; } 14 | 15 | /// 16 | /// The dialog cancel command text 17 | /// 18 | string? CancelCommandText { get; set; } 19 | 20 | /// 21 | /// Implements the logic that controls the accept command execution validation 22 | /// 23 | /// 24 | bool CanAccept(); 25 | 26 | /// 27 | /// Implements the logic that controls the cancel command execution validation 28 | /// 29 | /// 30 | bool CanCancel(); 31 | 32 | /// 33 | /// If true, the cancel button will not be shown 34 | /// 35 | bool HideCancelButton { get; set; } 36 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/IMsgBoxViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows.Input; 3 | using Avalonia.Controls; 4 | using Avalonia.Media.Imaging; 5 | using JamSoft.AvaloniaUI.Dialogs.MsgBox; 6 | 7 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 8 | 9 | /// 10 | /// The message box view model interface 11 | /// 12 | public interface IMsgBoxViewModel : INotifyPropertyChanged, IDialogResultVmHelper 13 | { 14 | /// 15 | /// The CheckBoxResult 16 | /// 17 | public bool CheckBoxResult { get; set; } 18 | 19 | /// 20 | /// The CheckBoxText value 21 | /// 22 | public string? CheckBoxText { get; set; } 23 | 24 | /// 25 | /// Determines if the dialog has an icon to show 26 | /// 27 | bool HasIcon { get; } 28 | 29 | /// 30 | /// The dialog image enum 31 | /// 32 | MsgBoxImage MsgBoxImage { get; set; } 33 | 34 | /// 35 | /// The dialog result 36 | /// 37 | MsgBoxButtonResult Result { get; set; } 38 | 39 | /// 40 | /// The dialog buttons 41 | /// 42 | MsgBoxButton Buttons { get; set; } 43 | 44 | /// 45 | /// The dialog Startup location 46 | /// 47 | WindowStartupLocation WindowStartupLocation { get; set; } 48 | 49 | /// 50 | /// The dialog topmost flag 51 | /// 52 | bool Topmost { get; set; } 53 | 54 | /// 55 | /// The dialog icon 56 | /// 57 | Bitmap? Icon { get; set; } 58 | 59 | /// 60 | /// The dialog cancel command 61 | /// 62 | string? AcceptCommandText { get; set; } 63 | 64 | /// 65 | /// The dialog accept command 66 | /// 67 | ICommand NoCommand { get; set; } 68 | 69 | /// 70 | /// The dialog cancel command text 71 | /// 72 | string? CancelCommandText { get; set; } 73 | 74 | /// 75 | /// The message box message 76 | /// 77 | string? Message { get; set; } 78 | 79 | /// 80 | /// The message box title 81 | /// 82 | string? MsgBoxTitle { get; set; } 83 | 84 | /// 85 | /// The no button text 86 | /// 87 | string? NoCommandText { get; set; } 88 | 89 | /// 90 | /// Boolean flag to show the no button 91 | /// 92 | bool ShowNoButton { get; set; } 93 | 94 | /// 95 | /// Boolean flag to show the yes button 96 | /// 97 | bool ShowYesButton { get; set; } 98 | 99 | /// 100 | /// Boolean flag to show the ok button 101 | /// 102 | bool ShowOkButton { get; set; } 103 | 104 | /// 105 | /// Boolean flag to show the cancel button 106 | /// 107 | bool ShowCancelButton { get; set; } 108 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/IWindowPositionAware.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 4 | 5 | /// 6 | /// The window position aware interface 7 | /// 8 | public interface IWindowPositionAware 9 | { 10 | /// 11 | /// The child window startup location 12 | /// 13 | WindowStartupLocation Location { get; set; } 14 | 15 | /// 16 | /// The child window requested top value 17 | /// 18 | double RequestedTop { get; set; } 19 | 20 | /// 21 | /// The child window requested left value 22 | /// 23 | double RequestedLeft { get; set; } 24 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/IWizardViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 2 | 3 | /// 4 | /// Defines a contract for a Wizard view model 5 | /// 6 | public interface IWizardViewModel : IChildWindowViewModel { } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/MsgBoxViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Runtime.CompilerServices; 3 | using System.Windows.Input; 4 | using Avalonia.Controls; 5 | using Avalonia.Media.Imaging; 6 | using Avalonia.Platform; 7 | using JamSoft.AvaloniaUI.Dialogs.Commands; 8 | using JamSoft.AvaloniaUI.Dialogs.Events; 9 | using JamSoft.AvaloniaUI.Dialogs.MsgBox; 10 | 11 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 12 | 13 | /// 14 | /// The default view model for the message box dialog 15 | /// 16 | public class MsgBoxViewModel : IMsgBoxViewModel 17 | { 18 | private string? _message; 19 | private string? _msgBoxTitle; 20 | private string? _noCommandText; 21 | private bool _showNoButton; 22 | private bool _showYesButton; 23 | private bool _showOkButton; 24 | private bool _showCancelButton; 25 | private string? _acceptCommandText; 26 | private string? _cancelCommandText; 27 | private ICommand _cancelCommand = null!; 28 | private ICommand _noCommand = null!; 29 | private ICommand _acceptCommand = null!; 30 | private Bitmap? _icon; 31 | private MsgBoxImage _msgBoxImage; 32 | private string? _checkBoxText; 33 | private bool _checkBoxResult; 34 | 35 | /// 36 | /// The default constructor 37 | /// 38 | /// 39 | /// 40 | /// 41 | /// 42 | /// 43 | /// 44 | /// 45 | /// 46 | public MsgBoxViewModel(string caption, string message, MsgBoxButton buttons, MsgBoxImage icon = MsgBoxImage.None, string? noButtonText = null, string? yesButtonText = null, string? cancelButtonText = null, string? checkBoxText = null) 47 | { 48 | _msgBoxTitle = caption; 49 | _message = message; 50 | 51 | WindowStartupLocation = WindowStartupLocation.CenterOwner; 52 | NoCommandText = noButtonText; 53 | AcceptCommandText = yesButtonText; 54 | CancelCommandText = cancelButtonText; 55 | Buttons = buttons; 56 | MsgBoxImage = icon; 57 | Topmost = false; 58 | CheckBoxText = checkBoxText; 59 | 60 | SetupCommands(); 61 | SetupButtons(); 62 | } 63 | 64 | /// 65 | /// The CheckBoxResult value 66 | /// 67 | public bool CheckBoxResult 68 | { 69 | get => _checkBoxResult; 70 | set => RaiseAndSetIfChanged(ref _checkBoxResult, value); 71 | } 72 | 73 | /// 74 | /// The show check box flag 75 | /// 76 | public bool ShowCheckBox => CheckBoxText is not null; 77 | 78 | /// 79 | /// The CheckBoxText value 80 | /// 81 | public string? CheckBoxText 82 | { 83 | get => _checkBoxText; 84 | set { RaiseAndSetIfChanged(ref _checkBoxText, value); } 85 | } 86 | 87 | /// 88 | /// Determines if the dialog has an icon to show 89 | /// 90 | public bool HasIcon => Icon is not null; 91 | 92 | /// 93 | /// The dialog image enum 94 | /// 95 | public MsgBoxImage MsgBoxImage 96 | { 97 | get => _msgBoxImage; 98 | set 99 | { 100 | RaiseAndSetIfChanged(ref _msgBoxImage, value); 101 | SetImage(); 102 | } 103 | } 104 | 105 | /// 106 | /// Configures the icon for the dialog 107 | /// 108 | protected virtual void SetImage() 109 | { 110 | Icon = IconResolver.Resolve(MsgBoxImage); 111 | } 112 | 113 | /// 114 | /// The dialog result 115 | /// 116 | public MsgBoxButtonResult Result { get; set; } 117 | 118 | /// 119 | /// The dialog buttons 120 | /// 121 | public MsgBoxButton Buttons { get; set; } 122 | 123 | /// 124 | /// The dialog Startup location 125 | /// 126 | public WindowStartupLocation WindowStartupLocation { get; set; } 127 | 128 | /// 129 | /// The dialog topmost flag 130 | /// 131 | public bool Topmost { get; set; } 132 | 133 | /// 134 | /// The dialog icon 135 | /// 136 | public Bitmap? Icon 137 | { 138 | get => _icon; 139 | set => RaiseAndSetIfChanged(ref _icon, value); 140 | } 141 | /// 142 | /// The dialog accept command 143 | /// 144 | public ICommand AcceptCommand 145 | { 146 | get => _acceptCommand; 147 | set => RaiseAndSetIfChanged(ref _acceptCommand, value); 148 | } 149 | 150 | /// 151 | /// The dialog cancel command 152 | /// 153 | public string? AcceptCommandText 154 | { 155 | get => _acceptCommandText; 156 | set => RaiseAndSetIfChanged(ref _acceptCommandText, value); 157 | } 158 | 159 | /// 160 | /// Gets or sets the cancel command. 161 | /// 162 | /// 163 | /// The cancel command. 164 | /// 165 | public ICommand CancelCommand 166 | { 167 | get => _cancelCommand; 168 | set => RaiseAndSetIfChanged(ref _cancelCommand, value); 169 | } 170 | 171 | /// 172 | /// The dialog accept command 173 | /// 174 | public ICommand NoCommand 175 | { 176 | get => _noCommand; 177 | set => RaiseAndSetIfChanged(ref _noCommand, value); 178 | } 179 | 180 | /// 181 | /// The dialog cancel command text 182 | /// 183 | public string? CancelCommandText 184 | { 185 | get => _cancelCommandText; 186 | set => RaiseAndSetIfChanged(ref _cancelCommandText, value); 187 | } 188 | 189 | /// 190 | /// The PropertyChanged event 191 | /// 192 | public event PropertyChangedEventHandler? PropertyChanged; 193 | 194 | /// 195 | /// Occurs when [request close dialog] event is fired. 196 | /// 197 | public event EventHandler? RequestCloseDialog; 198 | 199 | /// 200 | /// The message box message 201 | /// 202 | public string? Message 203 | { 204 | get => _message; 205 | set => this.RaiseAndSetIfChanged(ref _message, value); 206 | } 207 | 208 | /// 209 | /// The message box title 210 | /// 211 | public string? MsgBoxTitle 212 | { 213 | get => _msgBoxTitle; 214 | set => this.RaiseAndSetIfChanged(ref _msgBoxTitle, value); 215 | } 216 | 217 | /// 218 | /// The no button text 219 | /// 220 | public string? NoCommandText 221 | { 222 | get => _noCommandText; 223 | set => this.RaiseAndSetIfChanged(ref _noCommandText, value); 224 | } 225 | 226 | /// 227 | /// Boolean flag to show the no button 228 | /// 229 | public bool ShowNoButton 230 | { 231 | get => _showNoButton; 232 | set => this.RaiseAndSetIfChanged(ref _showNoButton, value); 233 | } 234 | 235 | /// 236 | /// Boolean flag to show the yes button 237 | /// 238 | public bool ShowYesButton 239 | { 240 | get => _showYesButton; 241 | set => this.RaiseAndSetIfChanged(ref _showYesButton, value); 242 | } 243 | 244 | /// 245 | /// Boolean flag to show the ok button 246 | /// 247 | public bool ShowOkButton 248 | { 249 | get => _showOkButton; 250 | set => this.RaiseAndSetIfChanged(ref _showOkButton, value); 251 | } 252 | 253 | /// 254 | /// Boolean flag to show the cancel button 255 | /// 256 | public bool ShowCancelButton 257 | { 258 | get => _showCancelButton; 259 | set => this.RaiseAndSetIfChanged(ref _showCancelButton, value); 260 | } 261 | 262 | /// 263 | /// Sets up the various commands for the dialog 264 | /// 265 | private void SetupCommands() 266 | { 267 | _noCommand = new DelegateCommand(() => 268 | { 269 | Result = MsgBoxButtonResult.No; 270 | InvokeRequestCloseDialog(new RequestCloseDialogEventArgs(false)); 271 | }, CanNoAccept); 272 | 273 | _acceptCommand = new DelegateCommand(() => 274 | { 275 | if (Buttons == MsgBoxButton.YesNo || Buttons == MsgBoxButton.YesNoCancel) 276 | { 277 | Result = MsgBoxButtonResult.Yes; 278 | } 279 | 280 | if (Buttons == MsgBoxButton.Ok || Buttons == MsgBoxButton.OkCancel) 281 | { 282 | Result = MsgBoxButtonResult.Ok; 283 | } 284 | 285 | InvokeRequestCloseDialog(new RequestCloseDialogEventArgs(true)); 286 | }, CanAccept); 287 | 288 | _cancelCommand = new DelegateCommand(() => 289 | { 290 | Result = MsgBoxButtonResult.Cancel; 291 | InvokeRequestCloseDialog(new RequestCloseDialogEventArgs(false)); 292 | }, CanCancel); 293 | } 294 | 295 | /// 296 | /// Sets up the buttons for the dialog 297 | /// 298 | private void SetupButtons() 299 | { 300 | switch (Buttons) 301 | { 302 | case MsgBoxButton.Ok: 303 | AcceptCommandText ??= "OK"; 304 | ShowOkButton = true; 305 | break; 306 | case MsgBoxButton.OkCancel: 307 | AcceptCommandText ??= "OK"; 308 | CancelCommandText ??= "Cancel"; 309 | ShowOkButton = true; 310 | ShowCancelButton = true; 311 | break; 312 | case MsgBoxButton.YesNo: 313 | AcceptCommandText ??= "Yes"; 314 | NoCommandText ??= "No"; 315 | ShowYesButton = true; 316 | ShowNoButton = true; 317 | break; 318 | case MsgBoxButton.YesNoCancel: 319 | AcceptCommandText ??= "Yes"; 320 | CancelCommandText ??= "Cancel"; 321 | NoCommandText ??= "No"; 322 | ShowYesButton = true; 323 | ShowNoButton = true; 324 | ShowCancelButton = true; 325 | break; 326 | } 327 | } 328 | 329 | /// 330 | /// Implements the logic that controls the accept command execution validation 331 | /// 332 | /// 333 | protected virtual bool CanAccept() 334 | { 335 | return true; 336 | } 337 | 338 | /// 339 | /// Implements the logic that controls the cancel command execution validation 340 | /// 341 | /// 342 | protected virtual bool CanCancel() 343 | { 344 | return true; 345 | } 346 | 347 | /// 348 | /// Implements the logic that controls the accept command execution validation 349 | /// 350 | /// 351 | protected virtual bool CanNoAccept() 352 | { 353 | return true; 354 | } 355 | 356 | /// 357 | /// Invokes the request close dialog event 358 | /// 359 | /// 360 | protected virtual void InvokeRequestCloseDialog(RequestCloseDialogEventArgs e) 361 | { 362 | RequestCloseDialog?.Invoke(this, e); 363 | } 364 | 365 | /// 366 | /// Sets the property value only if the provided value is different from the stored value. 367 | /// If set to a new value, then the event will be fired 368 | /// 369 | /// 370 | /// The storage. 371 | /// The value. 372 | /// Name of the property. 373 | /// 374 | protected virtual bool RaiseAndSetIfChanged(ref T storage, T value, [CallerMemberName] string propertyName = "") 375 | { 376 | if (EqualityComparer.Default.Equals(storage, value)) 377 | { 378 | return false; 379 | } 380 | 381 | storage = value; 382 | OnPropertyChanged(propertyName); 383 | 384 | (AcceptCommand as DelegateCommand)?.RaiseCanExecuteChanged(); 385 | (CancelCommand as DelegateCommand)?.RaiseCanExecuteChanged(); 386 | 387 | return true; 388 | } 389 | 390 | /// 391 | /// Raises the event. Allows the use of a specific PropertyChangedEventArgs object. 392 | /// Is the most performant implementation 393 | /// 394 | /// The instance containing the event data. 395 | protected virtual void OnPropertyChanged(PropertyChangedEventArgs prop) 396 | { 397 | PropertyChanged?.Invoke(this, prop); 398 | } 399 | 400 | /// 401 | /// Fires the property changed event using either the or the provided string property name. 402 | /// 403 | /// Name of the property. 404 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 405 | { 406 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 407 | } 408 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/ViewModels/WizardViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | using JamSoft.AvaloniaUI.Dialogs.Commands; 3 | 4 | namespace JamSoft.AvaloniaUI.Dialogs.ViewModels; 5 | 6 | /// 7 | /// A base implementation of the IWizardViewModel interface 8 | /// 9 | public abstract class WizardViewModel : ChildWindowViewModel, IWizardViewModel { } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Views/ChildWindow.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | 30 | 31 | 35 | 36 | -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Views/ChildWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Input; 4 | using Avalonia.Markup.Xaml; 5 | using Avalonia.Reactive; 6 | using JamSoft.AvaloniaUI.Dialogs.Events; 7 | using JamSoft.AvaloniaUI.Dialogs.ViewModels; 8 | 9 | namespace JamSoft.AvaloniaUI.Dialogs.Views; 10 | 11 | /// 12 | /// The default child window 13 | /// 14 | public partial class ChildWindow : Window 15 | { 16 | private bool _isClosed; 17 | 18 | private IChildWindowViewModel? _vm; 19 | 20 | /// 21 | /// The default constructor 22 | /// 23 | public ChildWindow() 24 | { 25 | InitializeComponent(); 26 | #if DEBUG 27 | this.AttachDevTools(); 28 | #endif 29 | this.FindControl("ChromeDockPanel")!.PointerPressed += OnChromePointerPressed; 30 | this.FindControl("Host")!.DataContextChanged += DialogPresenterDataContextChanged; 31 | Closed += ChildWindowClosed; 32 | PositionChanged += OnPositionChanged; 33 | } 34 | 35 | private void OnPositionChanged(object? sender, PixelPointEventArgs e) 36 | { 37 | if (_vm == null) return; 38 | 39 | _vm.RequestedLeft = e.Point.X; 40 | _vm.RequestedTop = e.Point.Y; 41 | } 42 | 43 | private void OnChromePointerPressed(object? sender, PointerPressedEventArgs e) 44 | { 45 | if (_vm == null) return; 46 | 47 | var p = e.GetCurrentPoint(null); 48 | if (p.Properties.IsLeftButtonPressed) 49 | { 50 | this.GetObservable(ClientSizeProperty).Subscribe(new AnonymousObserver((_)=> 51 | { 52 | _vm.RequestedLeft = Position.X; 53 | _vm.RequestedTop = Position.Y; 54 | })); 55 | 56 | BeginMoveDrag(e); 57 | e.Handled = false; 58 | } 59 | } 60 | 61 | void ChildWindowClosed(object? sender, EventArgs e) 62 | { 63 | PointerPressed -= OnChromePointerPressed; 64 | PositionChanged -= OnPositionChanged; 65 | Closed -= ChildWindowClosed; 66 | _isClosed = true; 67 | } 68 | 69 | private void DialogPresenterDataContextChanged(object? sender, EventArgs e) 70 | { 71 | _vm = DataContext as IChildWindowViewModel; 72 | var d = DataContext as IDialogResultVmHelper; 73 | var windowPositionAware = DataContext as IWindowPositionAware; 74 | 75 | if (d == null) 76 | { 77 | return; 78 | } 79 | 80 | d.RequestCloseDialog += new EventHandler(DialogResultTrueEvent) 81 | .MakeWeak(eh => d.RequestCloseDialog -= eh); 82 | 83 | if (windowPositionAware == null) return; 84 | 85 | Position = new PixelPoint( 86 | Convert.ToInt32(windowPositionAware.RequestedLeft), 87 | Convert.ToInt32(windowPositionAware.RequestedTop)); 88 | } 89 | 90 | private void InitializeComponent() 91 | { 92 | AvaloniaXamlLoader.Load(this); 93 | } 94 | 95 | private void DialogResultTrueEvent(object? sender, RequestCloseDialogEventArgs e) 96 | { 97 | // Important: Do not set DialogResult for a closed window 98 | // GC clears windows anyway and with MakeWeak it 99 | // closes out with IDialogResultVMHelper 100 | if (_isClosed) 101 | { 102 | return; 103 | } 104 | 105 | Close(); 106 | } 107 | } -------------------------------------------------------------------------------- /src/JamSoft.AvaloniaUI.Dialogs/Views/DialogWindow.axaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |