[Refactor] Extract the logic of the ShowAsync() method from the Dialog.cs file to the DialogExtension.cs file.

This commit is contained in:
Dingping Zhang committed 2019-04-02 23:36:52 +08:00
1 parent 19d6d1c289
commit 70d4a39e3b
2 files changed
+102 -57

No files matched your search

+7 -57
View File
@@ -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<TResult> ShowAsync<TView, TResult>() where TView : class, new() =>
ShowAsync<TResult>(new TView());
public static Task<TResult> ShowAsync<TResult>(object content)
{
var tcs = new TaskCompletionSource<TResult>();
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<T>() where T : class, new() => Show(new T());
public static Dialog Show<T>() 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;
}
}
}
@@ -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<TResult> GetResultAsync<TResult>(this Dialog @this)
{
var tcs = new TaskCompletionSource<TResult>();
try
{
if (@this.IsClosed)
{
SetResult();
}
else
{
@this.Unloaded += OnUnloaded;
@this.GetViewModel<IDialogResultable>().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<TResult>(@this));
}
catch (Exception e)
{
tcs.TrySetException(e);
}
}
}
public static Dialog Initialize<TViewModel>(this Dialog @this, Action<TViewModel> configure)
{
configure?.Invoke(@this.GetViewModel<TViewModel>());
return @this;
}
public static TViewModel GetViewModel<TViewModel>(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<TResult>(Dialog dialog)
{
var viewModelResult = dialog.GetViewModel<IDialogResultable>().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. ");
}
}
}
}