From 70d4a39e3b12f3aa85131ac9a23ac026909ed7b8 Mon Sep 17 00:00:00 2001 From: Dingping Zhang Date: Tue, 2 Apr 2019 23:36:52 +0800 Subject: [PATCH] [Refactor] Extract the logic of the ShowAsync() method from the Dialog.cs file to the DialogExtension.cs file. --- HandyControl/Controls/Other/Dialog.cs | 64 ++----------- .../Tools/Extension/DialogExtension.cs | 95 +++++++++++++++++++ 2 files changed, 102 insertions(+), 57 deletions(-) create mode 100644 HandyControl/Tools/Extension/DialogExtension.cs diff --git a/HandyControl/Controls/Other/Dialog.cs b/HandyControl/Controls/Other/Dialog.cs index 16b23909..ec17afec 100644 --- a/HandyControl/Controls/Other/Dialog.cs +++ b/HandyControl/Controls/Other/Dialog.cs @@ -1,7 +1,4 @@ -using System; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; +using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using HandyControl.Interactivity; @@ -9,68 +6,19 @@ using HandyControl.Tools; namespace HandyControl.Controls { - public interface IDialogResult - { - object Result { get; set; } - } - public class Dialog : ContentControl { private Adorner _container; + // It can not be replaced with the FrameworkElement.IsLoaded property, which will be false when the dialog just created. + public bool IsClosed { get; private set; } + public Dialog() { CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => Close())); } - public static Task ShowAsync() where TView : class, new() => - ShowAsync(new TView()); - - public static Task ShowAsync(object content) - { - var tcs = new TaskCompletionSource(); - - var dialog = Show(content); - dialog.Unloaded += OnUnloaded; - - return tcs.Task; - - void OnUnloaded(object sender, RoutedEventArgs args) - { - try - { - dialog.Unloaded -= OnUnloaded; - tcs.SetResult(GetResult(dialog)); - } - catch (Exception e) - { - tcs.SetException(e); - } - } - - TResult GetResult(Dialog embeddedDialog) - { - if (!(embeddedDialog.Content is FrameworkElement frameworkElement)) - throw new InvalidOperationException("The dialog is not a derived class of the FrameworkElement. "); - - if (!(frameworkElement.DataContext is IDialogResult dialogResult)) - throw new InvalidOperationException("The view model of the dialog is not implement the IDialogResult interface. "); - - switch (dialogResult.Result) - { - case TResult result: - return result; - case null when !typeof(TResult).IsValueType: - return default(TResult); // The default value of the value type may cause confusion, so should not help the user decide. - default: - throw new InvalidCastException("Could not cast " + - $"the {(dialogResult.Result != null ? $"{dialogResult.Result.GetType()} type" : "null value")} " + - $"to the {typeof(TResult)} type. "); - } - } - } - - public static Dialog Show() where T : class, new() => Show(new T()); + public static Dialog Show() where T : new() => Show(new T()); public static Dialog Show(object content) { @@ -97,6 +45,7 @@ namespace HandyControl.Controls Child = dialog }; dialog._container = container; + dialog.IsClosed = false; layer.Add(container); } } @@ -119,6 +68,7 @@ namespace HandyControl.Controls } var layer = decorator.AdornerLayer; layer?.Remove(_container); + IsClosed = true; } } } diff --git a/HandyControl/Tools/Extension/DialogExtension.cs b/HandyControl/Tools/Extension/DialogExtension.cs new file mode 100644 index 00000000..ab536e3e --- /dev/null +++ b/HandyControl/Tools/Extension/DialogExtension.cs @@ -0,0 +1,95 @@ +using System; +using System.Threading.Tasks; +using System.Windows; +using HandyControl.Controls; + +namespace HandyControl.Tools.Extension +{ + public interface IDialogResultable + { + object Result { get; set; } + + Action ClosureToken { get; set; } + } + + public static class DialogExtension + { + public static Task GetResultAsync(this Dialog @this) + { + var tcs = new TaskCompletionSource(); + + try + { + if (@this.IsClosed) + { + SetResult(); + } + else + { + @this.Unloaded += OnUnloaded; + @this.GetViewModel().ClosureToken = @this.Close; + } + } + catch (Exception e) + { + tcs.TrySetException(e); + } + + return tcs.Task; + + // ReSharper disable once ImplicitlyCapturedClosure + void OnUnloaded(object sender, RoutedEventArgs args) + { + @this.Unloaded -= OnUnloaded; + SetResult(); + } + + void SetResult() + { + try + { + tcs.TrySetResult(ExtractResult(@this)); + } + catch (Exception e) + { + tcs.TrySetException(e); + } + } + } + + public static Dialog Initialize(this Dialog @this, Action configure) + { + configure?.Invoke(@this.GetViewModel()); + + return @this; + } + + public static TViewModel GetViewModel(this Dialog @this) + { + if (!(@this.Content is FrameworkElement frameworkElement)) + throw new InvalidOperationException("The dialog is not a derived class of the FrameworkElement. "); + + if (!(frameworkElement.DataContext is TViewModel viewModel)) + throw new InvalidOperationException($"The view model of the dialog is not the {typeof(TViewModel)} type or its derived class. "); + + return viewModel; + } + + private static TResult ExtractResult(Dialog dialog) + { + var viewModelResult = dialog.GetViewModel().Result; + + switch (viewModelResult) + { + case TResult result: + return result; + case null when !typeof(TResult).IsValueType: + return default(TResult); // The default value of the value type may cause confusion, so should not help the user decide. + default: + throw new InvalidCastException("Could not cast " + + $"the {(viewModelResult != null ? $"{viewModelResult.GetType()} type" : "null value")} " + + $"to the {typeof(TResult)} type. "); + } + } + } +}