├── _config.yml
├── src
├── img
│ ├── wizard.png
│ ├── basic-dialog.png
│ ├── child-window.png
│ ├── message-box.png
│ ├── sample-app.png
│ ├── message-box-checkbox.png
│ └── JamSoftLogo-Splat513x513.png
├── JamSoft.AvaloniaUI.Dialogs
│ ├── Assets
│ │ ├── add.png
│ │ ├── ban.png
│ │ ├── info.png
│ │ ├── wifi.png
│ │ ├── back-up.png
│ │ ├── customize.png
│ │ ├── database.png
│ │ ├── battery-half.png
│ │ ├── check-circle.png
│ │ ├── cross-circle.png
│ │ ├── exclamation.png
│ │ ├── folder-open.png
│ │ ├── interrogation.png
│ │ ├── diamond-exclamation.png
│ │ └── CloseIcon
│ │ │ ├── icons8-close-120.png
│ │ │ ├── icons8-close-240.png
│ │ │ ├── icons8-close-30.png
│ │ │ ├── icons8-close-480.png
│ │ │ ├── icons8-close-60.png
│ │ │ ├── icons8-close-90.png
│ │ │ ├── iOS
│ │ │ ├── icons8-close-30(@1x).png
│ │ │ ├── icons8-close-60(@2x).png
│ │ │ └── icons8-close-90(@3x).png
│ │ │ └── Android
│ │ │ ├── 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
│ │ │ └── icons8-close-120(-xxxhdpi).png
│ ├── JamSoftLogo-Splat513x513.png
│ ├── ViewModels
│ │ ├── IWizardViewModel.cs
│ │ ├── WizardViewModel.cs
│ │ ├── IWindowPositionAware.cs
│ │ ├── IChildWindowViewModel.cs
│ │ ├── IDialogResultVmHelper.cs
│ │ ├── IDialogViewModel.cs
│ │ ├── ChildWindowViewModel.cs
│ │ ├── IMsgBoxViewModel.cs
│ │ ├── DialogViewModel.cs
│ │ └── MsgBoxViewModel.cs
│ ├── Events
│ │ ├── UnregisterCallback.cs
│ │ ├── IWeakEventHandler.cs
│ │ ├── RequestCloseDialogEventArgs.cs
│ │ ├── EventHandlerUtils.cs
│ │ └── WeakEventHandler.cs
│ ├── Views
│ │ ├── MsgBoxView.axaml.cs
│ │ ├── ChildWindow.axaml
│ │ ├── MsgBoxView.axaml
│ │ ├── DialogWindow.axaml.cs
│ │ ├── MsgBoxWindow.axaml.cs
│ │ ├── DialogWindow.axaml
│ │ ├── ChildWindow.axaml.cs
│ │ └── MsgBoxWindow.axaml
│ ├── Themes
│ │ ├── MsgBoxStyles.axaml
│ │ ├── Default.axaml
│ │ ├── ModalStyle.axaml
│ │ ├── ChildStyle.axaml
│ │ ├── WizardStyle.axaml
│ │ └── WizardStepStyle.axaml
│ ├── MsgBox
│ │ ├── MsgBoxButton.cs
│ │ ├── MsgBoxButtonResult.cs
│ │ ├── MsgBoxResult.cs
│ │ ├── IconResolver.cs
│ │ └── MsgBoxImage.cs
│ ├── DialogServiceConfiguration.cs
│ ├── DialogServiceFactory.cs
│ ├── IMessageBoxService.cs
│ ├── Commands
│ │ └── DelegateCommand.cs
│ ├── MessageBoxService.cs
│ ├── Controls
│ │ ├── WizardStep.cs
│ │ └── Wizard.cs
│ ├── JamSoft.AvaloniaUI.Dialogs.csproj
│ ├── Helpers
│ │ ├── CommonFiltersExtensionMethods.cs
│ │ └── CommonFilters.cs
│ ├── IDialogService.cs
│ └── DialogService.cs
├── JamSoft.AvaloniaUI.Dialogs.Sample
│ ├── Assets
│ │ └── avalonia-logo.ico
│ ├── ViewModels
│ │ ├── ViewModelBase.cs
│ │ ├── SirNotAppearingInThisAppViewModel.cs
│ │ ├── ComboBoxItemViewModel.cs
│ │ ├── MyDialogViewModel.cs
│ │ ├── MyChildWindowViewModel.cs
│ │ ├── CustomBaseChildWindowViewModel.cs
│ │ ├── MyWizardViewModel.cs
│ │ └── MainWindowViewModel.cs
│ ├── Roots.xml
│ ├── Models
│ │ └── MyUserSettings.cs
│ ├── Views
│ │ ├── MyWizardView.axaml.cs
│ │ ├── MyDialogView.axaml.cs
│ │ ├── MyChildWindowView.axaml.cs
│ │ ├── CustomBaseChildWindowView.axaml.cs
│ │ ├── MainWindow.axaml.cs
│ │ ├── MyDialogView.axaml
│ │ ├── MyChildWindowView.axaml
│ │ ├── CustomBaseChildWindowView.axaml
│ │ ├── MyWizardView.axaml
│ │ └── MainWindow.axaml
│ ├── App.axaml.cs
│ ├── app.manifest
│ ├── Program.cs
│ ├── BootStrapper.cs
│ ├── App.axaml
│ ├── JamSoft.AvaloniaUI.Dialogs.Sample.csproj
│ └── .gitignore
└── JamSoft.AvaloniaUI.Dialogs.sln
├── .gitpod.yml
├── .gitlab-ci.yml
├── .gitignore
└── LICENSE
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/src/img/wizard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/img/wizard.png
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | image: gitpod/workspace-dotnet
2 |
3 | tasks:
4 | - init: dotnet build
5 | command: dotnet run
6 |
--------------------------------------------------------------------------------
/src/img/basic-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/img/basic-dialog.png
--------------------------------------------------------------------------------
/src/img/child-window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/img/child-window.png
--------------------------------------------------------------------------------
/src/img/message-box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/img/message-box.png
--------------------------------------------------------------------------------
/src/img/sample-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/img/sample-app.png
--------------------------------------------------------------------------------
/src/img/message-box-checkbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/img/message-box-checkbox.png
--------------------------------------------------------------------------------
/src/img/JamSoftLogo-Splat513x513.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/img/JamSoftLogo-Splat513x513.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/add.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/ban.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/ban.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/info.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/wifi.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/back-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/back-up.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/customize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/customize.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/database.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/database.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/battery-half.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/battery-half.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/check-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/check-circle.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/cross-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/cross-circle.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/exclamation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/exclamation.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/folder-open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/folder-open.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/interrogation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/interrogation.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/JamSoftLogo-Splat513x513.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/JamSoftLogo-Splat513x513.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/diamond-exclamation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/diamond-exclamation.png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs.Sample/Assets/avalonia-logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs.Sample/Assets/avalonia-logo.ico
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/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/HEAD/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/HEAD/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/HEAD/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/HEAD/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/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/icons8-close-90.png
--------------------------------------------------------------------------------
/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/Assets/CloseIcon/iOS/icons8-close-30(@1x).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/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/HEAD/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/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/iOS/icons8-close-90(@3x).png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-23(-ldpi).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/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/HEAD/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/HEAD/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/HEAD/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/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-90(-xxhdpi).png
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-120(-xxxhdpi).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamsoft/JamSoft.AvaloniaUI.Dialogs/HEAD/src/JamSoft.AvaloniaUI.Dialogs/Assets/CloseIcon/Android/icons8-close-120(-xxxhdpi).png
--------------------------------------------------------------------------------
/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 { }
--------------------------------------------------------------------------------
/.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/
--------------------------------------------------------------------------------
/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/Roots.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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/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.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/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.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/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 | }
--------------------------------------------------------------------------------
/.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
--------------------------------------------------------------------------------
/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/Views/MsgBoxView.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace JamSoft.AvaloniaUI.Dialogs.Views;
6 |
7 | ///
8 | /// The message box view
9 | ///
10 | public partial class MsgBoxView : UserControl
11 | {
12 | ///
13 | /// The default constructor
14 | ///
15 | public MsgBoxView()
16 | {
17 | InitializeComponent();
18 | }
19 | }
--------------------------------------------------------------------------------
/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.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/Themes/MsgBoxStyles.axaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
8 |
11 |
12 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs.Sample/Views/MyDialogView.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/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/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/Themes/Default.axaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/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/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/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.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/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/Themes/ModalStyle.axaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/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.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/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/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.Sample/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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.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/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/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/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/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/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/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.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/Views/MsgBoxView.axaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
32 |
33 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/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/Views/DialogWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 | using JamSoft.AvaloniaUI.Dialogs.Events;
5 | using JamSoft.AvaloniaUI.Dialogs.ViewModels;
6 |
7 | namespace JamSoft.AvaloniaUI.Dialogs.Views;
8 |
9 | ///
10 | /// The default dialog window
11 | ///
12 | public partial class DialogWindow : Window
13 | {
14 | private bool _isClosed;
15 |
16 | ///
17 | /// The default constructor
18 | ///
19 | public DialogWindow()
20 | {
21 | InitializeComponent();
22 | #if DEBUG
23 | this.AttachDevTools();
24 | #endif
25 | this.FindControl("Host")!.DataContextChanged += DialogPresenterDataContextChanged;
26 | Closed += DialogWindowClosed;
27 | }
28 |
29 | void DialogWindowClosed(object? sender, EventArgs e)
30 | {
31 | Closed -= DialogWindowClosed;
32 | _isClosed = true;
33 | }
34 |
35 | private void DialogPresenterDataContextChanged(object? sender, EventArgs e)
36 | {
37 | var d = DataContext as IDialogResultVmHelper;
38 |
39 | if (d == null)
40 | {
41 | return;
42 | }
43 |
44 | d.RequestCloseDialog += new EventHandler(DialogResultTrueEvent)
45 | .MakeWeak(eh => d.RequestCloseDialog -= eh);
46 | }
47 |
48 | private void DialogResultTrueEvent(object? sender, RequestCloseDialogEventArgs eventargs)
49 | {
50 | // Important: Do not set DialogResult for a closed window
51 | // GC clears windows anyways and with MakeWeak it
52 | // closes out with IDialogResultVMHelper
53 | if (_isClosed)
54 | {
55 | return;
56 | }
57 |
58 | Close(eventargs.DialogResult);
59 | }
60 |
61 | private void InitializeComponent()
62 | {
63 | AvaloniaXamlLoader.Load(this);
64 | }
65 | }
--------------------------------------------------------------------------------
/src/JamSoft.AvaloniaUI.Dialogs/Views/MsgBoxWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 | using JamSoft.AvaloniaUI.Dialogs.Events;
5 | using JamSoft.AvaloniaUI.Dialogs.ViewModels;
6 |
7 | namespace JamSoft.AvaloniaUI.Dialogs.Views;
8 |
9 | ///
10 | /// The default dialog window
11 | ///
12 | public partial class MsgBoxWindow : Window
13 | {
14 | private bool _isClosed;
15 |
16 | ///
17 | /// The default constructor
18 | ///
19 | public MsgBoxWindow()
20 | {
21 | InitializeComponent();
22 | #if DEBUG
23 | this.AttachDevTools();
24 | #endif
25 | this.FindControl("Host")!.DataContextChanged += DialogPresenterDataContextChanged;
26 | Closed += DialogWindowClosed;
27 | }
28 |
29 | void DialogWindowClosed(object? sender, EventArgs e)
30 | {
31 | Closed -= DialogWindowClosed;
32 | _isClosed = true;
33 | }
34 |
35 | private void DialogPresenterDataContextChanged(object? sender, EventArgs e)
36 | {
37 | var d = DataContext as IDialogResultVmHelper;
38 |
39 | if (d == null)
40 | {
41 | return;
42 | }
43 |
44 | d.RequestCloseDialog += new EventHandler(DialogResultTrueEvent)
45 | .MakeWeak(eh => d.RequestCloseDialog -= eh);
46 | }
47 |
48 | private void DialogResultTrueEvent(object? sender, RequestCloseDialogEventArgs eventargs)
49 | {
50 | // Important: Do not set DialogResult for a closed window
51 | // GC clears windows anyways and with MakeWeak it
52 | // closes out with IDialogResultVMHelper
53 | if (_isClosed)
54 | {
55 | return;
56 | }
57 |
58 | Close(eventargs.DialogResult);
59 | }
60 |
61 | private void InitializeComponent()
62 | {
63 | AvaloniaXamlLoader.Load(this);
64 | }
65 | }
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
38 |
39 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/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/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/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/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/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.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/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.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/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/Themes/ChildStyle.axaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
13 |
14 |
43 |
44 |
47 |
48 |
52 |
53 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/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.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/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/Views/MsgBoxWindow.axaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
67 |
68 |
75 |
76 |
77 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/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/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/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.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/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/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/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