diff --git a/MahApps.Metro/Controls/Dialogs/DialogCoordinator.cs b/MahApps.Metro/Controls/Dialogs/DialogCoordinator.cs new file mode 100644 index 0000000000..647c2653e0 --- /dev/null +++ b/MahApps.Metro/Controls/Dialogs/DialogCoordinator.cs @@ -0,0 +1,80 @@ +using System; +using System.Threading.Tasks; +using System.Windows; + +namespace MahApps.Metro.Controls.Dialogs +{ + public class DialogCoordinator : IDialogCoordinator + { + /// + /// Gets the default instance if the dialog coordinator, which can be injected into a view model. + /// + public static readonly DialogCoordinator Instance = new DialogCoordinator(); + + public Task ShowInputAsync(object context, string title, string message, MetroDialogSettings metroDialogSettings = null) + { + var metroWindow = GetMetroWindow(context); + + return metroWindow.ShowInputAsync(title, message, metroDialogSettings); + } + + public Task ShowLoginAsync(object context, string title, string message, MessageDialogStyle style = MessageDialogStyle.Affirmative, LoginDialogSettings settings = null) + { + var metroWindow = GetMetroWindow(context); + + return metroWindow.ShowLoginAsync(title, message, settings); + } + + public Task ShowMessageAsync(object context, string title, string message, MessageDialogStyle style = MessageDialogStyle.Affirmative, MetroDialogSettings settings = null) + { + var metroWindow = GetMetroWindow(context); + + return metroWindow.ShowMessageAsync(title, message, style, settings); + } + + public Task ShowProgressAsync(object context, string title, string message, + bool isCancelable = false, MetroDialogSettings settings = null) + { + var metroWindow = GetMetroWindow(context); + + return metroWindow.ShowProgressAsync(title, message, isCancelable, settings); + } + + public Task ShowMetroDialogAsync(object context, BaseMetroDialog dialog, + MetroDialogSettings settings = null) + { + var metroWindow = GetMetroWindow(context); + + return metroWindow.ShowMetroDialogAsync(dialog, settings); + } + + public Task HideMetroDialogAsync(object context, BaseMetroDialog dialog, MetroDialogSettings settings = null) + { + var metroWindow = GetMetroWindow(context); + + return metroWindow.HideMetroDialogAsync(dialog, settings); + } + + public Task GetCurrentDialogAsync(object context) where TDialog : BaseMetroDialog + { + var metroWindow = GetMetroWindow(context); + + return metroWindow.GetCurrentDialogAsync(); + } + + private static MetroWindow GetMetroWindow(object context) + { + if (context == null) throw new ArgumentNullException("context"); + if (!DialogParticipation.IsRegistered(context)) + throw new InvalidOperationException( + "Context is not registered. Consider using DialogParticipation.Register in XAML to bind in the DataContext."); + + var association = DialogParticipation.GetAssociation(context); + var metroWindow = Window.GetWindow(association) as MetroWindow; + + if (metroWindow == null) + throw new InvalidOperationException("Control is not inside a MetroWindow."); + return metroWindow; + } + } +} \ No newline at end of file diff --git a/MahApps.Metro/Controls/Dialogs/DialogParticipation.cs b/MahApps.Metro/Controls/Dialogs/DialogParticipation.cs new file mode 100644 index 0000000000..a369e31f4d --- /dev/null +++ b/MahApps.Metro/Controls/Dialogs/DialogParticipation.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Windows; + +namespace MahApps.Metro.Controls.Dialogs +{ + public static class DialogParticipation + { + private static readonly IDictionary ContextRegistrationIndex = new Dictionary(); + + public static readonly DependencyProperty RegisterProperty = DependencyProperty.RegisterAttached( + "Register", typeof(object), typeof(DialogParticipation), new PropertyMetadata(default(object), RegisterPropertyChangedCallback)); + + private static void RegisterPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (dependencyPropertyChangedEventArgs.OldValue != null) + ContextRegistrationIndex.Remove(dependencyPropertyChangedEventArgs.OldValue); + + if (dependencyPropertyChangedEventArgs.NewValue != null) + ContextRegistrationIndex[dependencyPropertyChangedEventArgs.NewValue] = dependencyObject; + } + + public static void SetRegister(DependencyObject element, object context) + { + element.SetValue(RegisterProperty, context); + } + + public static object GetRegister(DependencyObject element) + { + return element.GetValue(RegisterProperty); + } + + internal static bool IsRegistered(object context) + { + if (context == null) throw new ArgumentNullException("context"); + + return ContextRegistrationIndex.ContainsKey(context); + } + + internal static DependencyObject GetAssociation(object context) + { + if (context == null) throw new ArgumentNullException("context"); + + return ContextRegistrationIndex[context]; + } + } +} diff --git a/MahApps.Metro/Controls/Dialogs/IDialogCoordinator.cs b/MahApps.Metro/Controls/Dialogs/IDialogCoordinator.cs new file mode 100644 index 0000000000..a38722a63a --- /dev/null +++ b/MahApps.Metro/Controls/Dialogs/IDialogCoordinator.cs @@ -0,0 +1,86 @@ +using System; +using System.Threading.Tasks; + +namespace MahApps.Metro.Controls.Dialogs +{ + /// + /// Use the dialog coordinator to help you interfact with dialogs from a view model. + /// + public interface IDialogCoordinator + { + /// + /// Shows the input dialog. + /// + /// Typically this should be the view model, which you register in XAML using . + /// The title of the MessageDialog. + /// The message contained within the MessageDialog. + /// Optional settings that override the global metro dialog settings. + /// The text that was entered or null (Nothing in Visual Basic) if the user cancelled the operation. + Task ShowInputAsync(object context, string title, string message, MetroDialogSettings settings = null); + + /// + /// Creates a LoginDialog inside of the current window. + /// + /// Typically this should be the view model, which you register in XAML using . + /// The title of the LoginDialog. + /// The message contained within the LoginDialog. + /// + /// Optional settings that override the global metro dialog settings. + /// The text that was entered or null (Nothing in Visual Basic) if the user cancelled the operation. + Task ShowLoginAsync(object context, string title, string message, MessageDialogStyle style = MessageDialogStyle.Affirmative, LoginDialogSettings settings = null); + + /// + /// Creates a MessageDialog inside of the current window. + /// + /// Typically this should be the view model, which you register in XAML using . + /// The title of the MessageDialog. + /// The message contained within the MessageDialog. + /// The type of buttons to use. + /// Optional settings that override the global metro dialog settings. + /// A task promising the result of which button was pressed. + Task ShowMessageAsync(object context, string title, string message, MessageDialogStyle style = MessageDialogStyle.Affirmative, MetroDialogSettings settings = null); + + /// + /// Creates a ProgressDialog inside of the current window. + /// + /// Typically this should be the view model, which you register in XAML using . + /// The title of the ProgressDialog. + /// The message within the ProgressDialog. + /// Determines if the cancel button is visible. + /// Optional Settings that override the global metro dialog settings. + /// A task promising the instance of ProgressDialogController for this operation. + Task ShowProgressAsync(object context, string title, string message, + bool isCancelable = false, MetroDialogSettings settings = null); + + /// + /// Adds a Metro Dialog instance to the specified window and makes it visible asynchronously. + /// You have to close the resulting dialog yourself with . + /// + /// Typically this should be the view model, which you register in XAML using . + /// The dialog instance itself. + /// An optional pre-defined settings instance. + /// A task representing the operation. + /// The is already visible in the window. + Task ShowMetroDialogAsync(object context, BaseMetroDialog dialog, + MetroDialogSettings settings = null); + + /// + /// Hides a visible Metro Dialog instance. + /// + /// Typically this should be the view model, which you register in XAML using . + /// The dialog instance to hide. + /// An optional pre-defined settings instance. + /// A task representing the operation. + /// + /// The is not visible in the window. + /// This happens if hasn't been called before. + /// + Task HideMetroDialogAsync(object context, BaseMetroDialog dialog, MetroDialogSettings settings = null); + + /// + /// Gets the current shown dialog. + /// + /// Typically this should be the view model, which you register in XAML using . + Task GetCurrentDialogAsync(object context) where TDialog : BaseMetroDialog; + } +} \ No newline at end of file diff --git a/MahApps.Metro/MahApps.Metro.NET45.csproj b/MahApps.Metro/MahApps.Metro.NET45.csproj index b7d0adfdc8..e2a5f54947 100644 --- a/MahApps.Metro/MahApps.Metro.NET45.csproj +++ b/MahApps.Metro/MahApps.Metro.NET45.csproj @@ -130,6 +130,9 @@ + + + diff --git a/MahApps.Metro/MahApps.Metro.csproj b/MahApps.Metro/MahApps.Metro.csproj index 2923793a91..14f9444698 100644 --- a/MahApps.Metro/MahApps.Metro.csproj +++ b/MahApps.Metro/MahApps.Metro.csproj @@ -85,6 +85,8 @@ + + @@ -95,6 +97,7 @@ + diff --git a/samples/MetroDemo/ExampleWindows/FlyoutDemo.xaml.cs b/samples/MetroDemo/ExampleWindows/FlyoutDemo.xaml.cs index 2d286afe7b..6bfecd160c 100644 --- a/samples/MetroDemo/ExampleWindows/FlyoutDemo.xaml.cs +++ b/samples/MetroDemo/ExampleWindows/FlyoutDemo.xaml.cs @@ -2,6 +2,7 @@ using System.Windows; using System.Windows.Input; using MahApps.Metro.Controls; +using MahApps.Metro.Controls.Dialogs; using MetroDemo.Models; namespace MetroDemo.ExampleWindows @@ -13,7 +14,7 @@ public partial class FlyoutDemo : IDisposable public FlyoutDemo() { - this.DataContext = new MainWindowViewModel(); + this.DataContext = new MainWindowViewModel(DialogCoordinator.Instance); this.InitializeComponent(); this.Closing += (s, e) => { diff --git a/samples/MetroDemo/MainWindow.xaml b/samples/MetroDemo/MainWindow.xaml index f5781f214d..b3a1d13d65 100644 --- a/samples/MetroDemo/MainWindow.xaml +++ b/samples/MetroDemo/MainWindow.xaml @@ -20,7 +20,9 @@ d:DesignHeight="600" d:DesignWidth="800" d:DataContext="{d:DesignInstance MetroDemo:MainWindowViewModel}" - Closing="MetroWindow_Closing"> + Closing="MetroWindow_Closing" + Dialog:DialogParticipation.Register="{Binding}" + > @@ -178,6 +180,17 @@ Header="Show CustomDialog Externally" /> + + + + + + false }); } } + + private ICommand showInputDialogCommand; + + public ICommand ShowInputDialogCommand + { + get + { + return this.showInputDialogCommand ?? (this.showInputDialogCommand = new SimpleCommand + { + CanExecuteDelegate = x => true, + ExecuteDelegate = x => + { + _dialogCoordinator.ShowInputAsync(this, "From a VM", "This dialog was shown from a VM, without knowledge of Window").ContinueWith(t => Console.WriteLine(t.Result)); + } + }); + } + } + + private ICommand showLoginDialogCommand; + + public ICommand ShowLoginDialogCommand + { + get + { + return this.showLoginDialogCommand ?? (this.showLoginDialogCommand = new SimpleCommand + { + CanExecuteDelegate = x => true, + ExecuteDelegate = x => + { + _dialogCoordinator.ShowLoginAsync(this, "Login from a VM", "This login dialog was shown from a VM, so you can be all MVVM.").ContinueWith(t => Console.WriteLine(t.Result)); + } + }); + } + } + + private ICommand showMessageDialogCommand; + + public ICommand ShowMessageDialogCommand + { + get + { + return this.showMessageDialogCommand ?? (this.showMessageDialogCommand = new SimpleCommand + { + CanExecuteDelegate = x => true, + ExecuteDelegate = x => + { + _dialogCoordinator.ShowMessageAsync(this, "Message from VM", "MVVM based messages!").ContinueWith(t => Console.WriteLine(t.Result)); + } + }); + } + } + + private ICommand showProgressDialogCommand; + + public ICommand ShowProgressDialogCommand + { + get + { + return this.showProgressDialogCommand ?? (this.showProgressDialogCommand = new SimpleCommand + { + CanExecuteDelegate = x => true, + ExecuteDelegate = x => RunProgressFromVm() + }); + } + } + + private async void RunProgressFromVm() + { + var controller = await _dialogCoordinator.ShowProgressAsync(this, "Progress from VM", "Progressing all the things, wait 3 seconds"); + + await TaskEx.Delay(3000); + + await controller.CloseAsync(); + } + + + private ICommand showCustomDialogCommand; + + public ICommand ShowCustomDialogCommand + { + get + { + return this.showCustomDialogCommand ?? (this.showCustomDialogCommand = new SimpleCommand + { + CanExecuteDelegate = x => true, + ExecuteDelegate = x => RunCustomFromVm() + }); + } + } + + private async void RunCustomFromVm() + { + var customDialog = new CustomDialog() { Title = "Custom, wait 3 seconds" }; + + await _dialogCoordinator.ShowMetroDialogAsync(this, customDialog); + + await TaskEx.Delay(3000); + + await _dialogCoordinator.HideMetroDialogAsync(this, customDialog); + } + public IEnumerable BrushResources { get; private set; } public bool AnimateOnPositionChange