c# - 等待 MahApps Metro Dialog 返回结果

标签 c# wpf mvvm mvvm-light mahapps.metro

我有一个 ViewModel 向 View 发送消息(使用 MVVM Light Messenger)以显示 Metro 对话框,如下所示:

在 ViewModel 中,我从 DialogBox 类调用这段代码:

DialogBox.ShowDialogBox(
                        (result) => { DialogResult(result); },
                        "Dialog Title",
                        "Dialog Message",
                        MessageDialogStyle.AffirmativeAndNegative
                        );

这是处理将消息发送到 View 的 DialogBox 类:

public class DialogBox
{
    public Action<MessageDialogResult> dialogResultCallBack { get; set; }
    public string dialogTitle;
    public string dialogText;
    public MessageDialogStyle dialogStyle;
    public string okButtonText;
    public string cancelButtonText;

    public DialogBox(Action<MessageDialogResult> _dialogResultCallBack, string _dialogTitle, string _dialogText, MessageDialogStyle _dialogStyle, string _okButtonText, string _cancelButtonText)
    {
        dialogResultCallBack = _dialogResultCallBack;
        dialogTitle = _dialogTitle;
        dialogText = _dialogText;
        dialogStyle = _dialogStyle;
        okButtonText = _okButtonText;
        cancelButtonText = _cancelButtonText;
    }


    public static void ShowDialogBox(Action<MessageDialogResult> _dialogResultCallBack, string _dialogTitle, string _dialogText,
        MessageDialogStyle _dialogStyle, string _affirmativeButtonText = "OK", string _negativeButtonText = "Cancel")
    {
        Messenger.Default.Send(new DialogBox(
                       _dialogResultCallBack,
                       _dialogTitle,
                       _dialogText,
                       _dialogStyle,
                       _affirmativeButtonText,
                       _negativeButtonText), GlobalResources.MessengerTokens.dialogTokenMainWindow);
    }
}

View 代码隐藏有以下代码来接收消息:

Messenger.Default.Register<DialogBox>(this, GlobalResources.MessengerTokens.dialogTokenMainWindow, dialogData =>
            {
                ShowMessageDialog(dialogData);
            });

而 ShowMessageDialog 处理显示实际的对话框。这些都很好用。

目前,当用户选择肯定/否定时,将返回结果并触发对 ViewModel 中 DialogResult(result) 的操作调用,如最上面的代码片段所示。

private void DialogResult(MessageDialogResult result)
    {
        if (result == MessageDialogResult.Affirmative)
        {
             //deal with the situation
        }
        else
        {
            //deal with the situation
        }
    }

我实际上想在调用 ViewModel 中的 DialogBox.ShowDialogBox() 方法后立即等待结果。当前的方法导致代码跳转到单独的方法调用,而不是能够立即处理结果。为了简要说明这一点,

if(condition)
{
  DialogBox.ShowDialogBox(...);

  //Needs some sort of await method to wait for results here

  if(result == MessageDialogResult.Affirmative)
  {
         //do stuff
  }
  else
  {
        //do stuff
  }
 }

我至少在 WinForms 上看到了一些示例代码,通过执行以下操作,等待结果更容易(使用代码隐藏且没有 MVVM):

if (MessageBox.Show("Title", "Message", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == System.Windows.Forms.DialogResult.OK)

是否可以针对我目前的情况采取类似的方法?感谢您的任何建议,如果我的问题太长,我深表歉意。

最佳答案

我认为有更好的方法来做您正在做的事情。为了让这个更 MVVM,这就是我所做的......首先,我使用 Caliburn Micro 来处理我的 MVVM 东西和 MEF。所以首先我们有两个接口(interface):

internal interface IDialogViewModel
{
    event EventHandler Closed;
}

下面的界面将帮助您获得对话的结果

public interface IDialogManager
{
    /// <summary>
    /// Show a dialog that performs as Task with generic return type. 
    /// </summary>
    /// <typeparam name="TResult">The result to be returned from the dialog task.</typeparam>
    /// <param name="viewModel">The DialogViewModel type to be displayed.</param>
    /// <returns>The Task to be awaited.</returns>
    Task<TResult> ShowDialog<TResult>(DialogViewModel<TResult> viewModel);

    /// <summary>
    /// Show a dialog that performs as Task.
    /// </summary>
    /// <param name="viewModel">The result to be returned from the dialog task.</param>
    /// <returns>The Task to be awaited.</returns>
    Task ShowDialog(DialogViewModel viewModel);
}

这些接口(interface)的实现是

/// <summary>
/// DialogViewModel class which should be inherited for all view 
/// model that want to be displayed as metro dialogs.
/// </summary>
public abstract class DialogViewModel : Screen, IDialogViewModel
{
    private readonly TaskCompletionSource<int> tcs;
    internal Task Task { get { return tcs.Task; } }

    /// <summary>
    /// Deafult constructor.
    /// </summary>
    protected DialogViewModel()
    {
        tcs = new TaskCompletionSource<int>();
    }

    /// <summary>
    /// Close the dialog.
    /// </summary>
    protected void Close()
    {
        tcs.SetResult(0);
        var handler = Closed;
        if (handler != null)
            handler(this, EventArgs.Empty);
    }

    /// <summary>
    /// Closed event.
    /// </summary>
    public event EventHandler Closed;
}

/// <summary>
/// DialogViewModel class which should be inherited for all view 
/// model that want to be displayed as metro dialogs that can return a 
/// specific result.
/// </summary>
public abstract class DialogViewModel<TResult> : Screen, IDialogViewModel
{
    private readonly TaskCompletionSource<TResult> tcs;
    internal Task<TResult> Task { get { return tcs.Task; } }

    /// <summary>
    /// Deafult constructor.
    /// </summary>
    protected DialogViewModel()
    {
        tcs = new TaskCompletionSource<TResult>();
    }

    /// <summary>
    /// Close the dialog.
    /// </summary>
    protected void Close(TResult result)
    {
        tcs.SetResult(result);
        var handler = Closed;
        if (handler != null)
            handler(this, EventArgs.Empty);
    }

    /// <summary>
    /// Closed event.
    /// </summary>
    public event EventHandler Closed;
}

经理类是

/// <summary>
/// The DialogManager that can be used to show Views as Metro modal dialogs.
/// Import IDialogManager to any view model that needs to show a metro message 
/// box.
/// </summary>
[Export(typeof(IDialogManager))]
public class DialogManager : IDialogManager
{
    /// <summary>
    /// Show the required dialog.
    /// </summary>
    /// <param name="viewModel">The view model ascociated with the view.</param>
    public async Task ShowDialog(DialogViewModel viewModel)
    {
        // Locate the ascociated view.
        var viewType = ViewLocator.LocateTypeForModelType(viewModel.GetType(), null, null);
        var dialog = (BaseMetroDialog)Activator.CreateInstance(viewType);
        if (dialog == null)
        {
            throw new InvalidOperationException(
                String.Format("The view {0} belonging to view model {1} " +
                                  "does not inherit from {2}",
                                  viewType,
                                  viewModel.GetType(),
                                  typeof(BaseMetroDialog)));
        }
        dialog.DataContext = viewModel;

        // Show the metro window.
        MetroWindow firstMetroWindow = 
            Application.Current.Windows.OfType<MetroWindow>().First();
        await firstMetroWindow.ShowMetroDialogAsync(dialog);
        await viewModel.Task;
        await firstMetroWindow.HideMetroDialogAsync(dialog);
    }

    /// <summary>
    /// Show the required dialog.
    /// </summary>
    /// <typeparam name="TResult">The type of result to return.</typeparam>
    /// <param name="viewModel">The view model ascociated with the view.</param>
    public async Task<TResult> ShowDialog<TResult>(DialogViewModel<TResult> viewModel)
    {
        // Locate the ascociated view.
        var viewType = ViewLocator.LocateTypeForModelType(viewModel.GetType(), null, null);
        var dialog = (BaseMetroDialog)Activator.CreateInstance(viewType);
        if (dialog == null)
        {
            throw new InvalidOperationException(
                String.Format("The view {0} belonging to view model {1} " +
                                  "does not inherit from {2}",
                                  viewType,
                                  viewModel.GetType(),
                                  typeof(BaseMetroDialog)));
        }
        dialog.DataContext = viewModel;

        // Show the metro window.
        MetroWindow firstMetroWindow = Application.Current.Windows.OfType<MetroWindow>().First();
        await firstMetroWindow.ShowMetroDialogAsync(dialog);
        TResult result = await viewModel.Task;
        await firstMetroWindow.HideMetroDialogAsync(dialog);
        return result;
    }
}

我们还有消息框设置

/// <summary>
/// Class that holds the settings for message box dialogs.
/// </summary>
public class MessageBoxSettings
{
    /// <summary>
    /// Default constructor.
    /// </summary>
    public MessageBoxSettings()
    {
        this.AffirmativeButtonText = "OK";
        this.NegativeButtonText = "Cancel";
        this.MessageDialogStyle = MessageDialogStyle.AffirmativeAndNegative;
        this.MetroColorDialogScheme = MetroDialogColorScheme.Theme;
        this.Animation = false;
    }

    /// <summary>
    /// Sets the button styles to use.
    /// Default is 'OK' and 'Cancel'.
    /// </summary>
    public MessageDialogStyle MessageDialogStyle { get; set; }

    /// <summary>
    /// The color sheme to use for the dialog.
    /// </summary>
    public MetroDialogColorScheme MetroColorDialogScheme { get; set; }

    /// <summary>
    /// Affirmative button text. Default OK.
    /// </summary>
    public string AffirmativeButtonText { get; set; }

    /// <summary>
    /// The negative button text to use.
    /// </summary>
    public string NegativeButtonText { get; set; }

    /// <summary>
    /// First auxillary button text.
    /// </summary>
    public string FirstAuxillaryButtonText { get; set; }

    /// <summary>
    /// Second auxillary button text.
    /// </summary>
    public string SecondAuxiliaryButtonText { get; set; }

    /// <summary>
    /// Show animation on the dialog.
    /// </summary>
    public bool Animation { get; set; }
}

现在实际使用上面代码的 View 和 View 模型是

/// <summary>
/// View model for the message box view.
/// </summary>
public class MessageBoxViewModel : DialogViewModel<MessageDialogResult>
{
    private MessageBoxSettings settings;

    #region Initialisation.
    /// <summary>
    /// Null.
    /// </summary>
    public MessageBoxViewModel() { }

    /// <summary>
    /// Default constructor.
    /// </summary>
    /// <param name="title">The title of the message box dialog.</param>
    /// <param name="message">The message to display in the message box.</param>
    public MessageBoxViewModel(string title, string message)
    {
        this.Title = title;
        this.DialogBody = message;
        if (this.settings == null)
            this.settings = new MessageBoxSettings();
        SetDialogVisuals();
    }

    /// <summary>
    /// Overloaded.
    /// </summary>
    /// <param name="title">The title of the message box dialog.</param>
    /// <param name="message">The message to display in the message box.</param>
    /// <param name="settings">MessageBoxSettings for the dialog.</param>
    public MessageBoxViewModel(string title, string message, MessageBoxSettings settings)
    {
        this.Title = title;
        this.DialogBody = message;
        this.settings = settings;
        SetDialogVisuals();
    }
    #endregion // Initialisation.

    #region Class Methods.
    /// <summary>
    /// Set the dialog visuals based on the MessageBoxSettings.
    /// </summary>
    private void SetDialogVisuals()
    {
        // Set dialog button visibility.
        switch (settings.MessageDialogStyle)
        {
            case MessageDialogStyle.Affirmative:
                this.AffirmativeButtonVisibility = Visibility.Visible;
                this.NegativeButtonVisibility = Visibility.Collapsed;
                this.FirstAuxillaryButtonVisibility = Visibility.Collapsed;
                this.SecondAuxillaryButtonVisibility = Visibility.Collapsed;
                break;
            case MessageDialogStyle.AffirmativeAndNegative:
                this.AffirmativeButtonVisibility = Visibility.Visible;
                this.NegativeButtonVisibility = Visibility.Visible;
                this.FirstAuxillaryButtonVisibility = Visibility.Collapsed;
                this.SecondAuxillaryButtonVisibility = Visibility.Collapsed;
                break;
            case MessageDialogStyle.AffirmativeAndNegativeAndDoubleAuxiliary:
                this.AffirmativeButtonVisibility = Visibility.Visible;
                this.NegativeButtonVisibility = Visibility.Visible;
                this.FirstAuxillaryButtonVisibility = Visibility.Visible;
                this.SecondAuxillaryButtonVisibility = Visibility.Visible;
                break;
            case MessageDialogStyle.AffirmativeAndNegativeAndSingleAuxiliary:
                this.AffirmativeButtonVisibility = Visibility.Visible;
                this.NegativeButtonVisibility = Visibility.Visible;
                this.FirstAuxillaryButtonVisibility = Visibility.Visible;
                this.SecondAuxillaryButtonVisibility = Visibility.Collapsed;
                break;
            default:
                break;
        }

        // Set the button text.
        this.AffirmativeButtonText = settings.AffirmativeButtonText;
        this.NegativeButtonText = settings.NegativeButtonText;
        this.FirstAuxillaryButtonText = settings.FirstAuxillaryButtonText;
        this.SecondAuxiliaryButtonText = settings.SecondAuxiliaryButtonText;

        // Color scheme.
        string name = MahApps.Metro.ThemeManager.DetectAppStyle(Application.Current).Item2.Name;
        this.Background = settings.MetroColorDialogScheme == MetroDialogColorScheme.Theme ?
            MahApps.Metro.ThemeManager.Accents
                .Where(a => a.Name.CompareNoCase(name))
                .First().Resources["HighlightBrush"] as SolidColorBrush :
            new SolidColorBrush(System.Windows.Media.Colors.White);
    }

    /// <summary>
    /// Handles the button click events for the affermative button.
    /// </summary>
    public void AffirmativeButtonClick()
    {
        Close(MessageDialogResult.Affirmative);
    }

    /// <summary>
    /// Handles the button click events for the negative button.
    /// </summary>
    public void NegativeButtonClick()
    {
        Close(MessageDialogResult.Negative);
    }

    /// <summary>
    /// Handles the button click events for the first auxillary button.
    /// </summary>
    public void FirstAuxillaryButtonClick()
    {
        Close(MessageDialogResult.FirstAuxiliary);
    }

    /// <summary>
    /// Handles the button click events for the second auxillary button.
    /// </summary>
    public void SecondAuxillaryButtonClick()
    {
        Close(MessageDialogResult.SecondAuxiliary);
    }
    #endregion // Class Methods.

    #region Properties.
    /// <summary>
    /// Hold the dialog title.
    /// </summary>
    private string title;
    public string Title
    {
        get { return title; }
        set
        {
            if (title == value)
                return;
            title = value;
            NotifyOfPropertyChange(() => Title);
        }
    }

    /// <summary>
    /// Hold the text for the dialog body.
    /// </summary>
    private string dialogBody;
    public string DialogBody
    {
        get { return dialogBody; }
        set
        {
            if (dialogBody == value)
                return;
            dialogBody = value;
            NotifyOfPropertyChange(() => DialogBody);
        }
    }

    /// <summary>
    /// Sets the button styles to use.
    /// Default is 'OK' and 'Cancel'.
    /// </summary>
    private MessageDialogStyle messageDialogStyle =
        MessageDialogStyle.AffirmativeAndNegative;
    public MessageDialogStyle MessageDialogStyle
    {
        get { return messageDialogStyle; }
        set
        {
            if (messageDialogStyle == value)
                return;
            messageDialogStyle = value;
            NotifyOfPropertyChange(() => MessageDialogStyle);
        }
    }

    /// <summary>
    /// The color sheme to use for the dialog.
    /// </summary>
    private SolidColorBrush background;
    public SolidColorBrush Background
    {
        get { return background; }
        set
        {
            if (background == value)
                return;
            background = value;
            NotifyOfPropertyChange(() => Background);
        }
    }

    /// <summary>
    /// Affirmative button text. Default OK.
    /// </summary>
    private string affirmativeButtonText = "OK";
    public string AffirmativeButtonText
    {
        get { return affirmativeButtonText; }
        set
        {
            if (affirmativeButtonText == value)
                return;
            affirmativeButtonText = value;
            NotifyOfPropertyChange(() => AffirmativeButtonText);
        }
    }

    /// <summary>
    /// Visibility for the default affirmative button.
    /// </summary>
    private Visibility affirmativeButtonVisibility = Visibility.Visible;
    public Visibility AffirmativeButtonVisibility
    {
        get { return affirmativeButtonVisibility = Visibility.Visible; }
        set
        {
            if (affirmativeButtonVisibility == value)
                return;
            affirmativeButtonVisibility = value;
            NotifyOfPropertyChange(() => AffirmativeButtonVisibility);
        }
    }

    /// <summary>
    /// The negative button text to use.
    /// </summary>
    private string negativeButtonText = "Cancel";
    public string NegativeButtonText
    {
        get { return negativeButtonText; }
        set
        {
            if (negativeButtonText == value)
                return;
            negativeButtonText = value;
            NotifyOfPropertyChange(() => NegativeButtonText);
        }
    }

    /// <summary>
    /// Visibility for the default negative button.
    /// </summary>
    private Visibility negativeButtonVisibility = Visibility.Visible;
    public Visibility NegativeButtonVisibility
    {
        get { return negativeButtonVisibility; }
        set
        {
            if (negativeButtonVisibility == value)
                return;
            negativeButtonVisibility = value;
            NotifyOfPropertyChange(() => NegativeButtonVisibility);
        }
    }

    /// <summary>
    /// First auxillary button text.
    /// </summary>
    private string firstAuxillaryButtonText;
    public string FirstAuxillaryButtonText
    {
        get { return firstAuxillaryButtonText; }
        set
        {
            if (firstAuxillaryButtonText == value)
                return;
            firstAuxillaryButtonText = value;
            NotifyOfPropertyChange(() => FirstAuxillaryButtonText);
        }
    }

    /// <summary>
    /// First auxillary button visibility.
    /// </summary>
    private Visibility firstAuxillaryButtonVisibility = Visibility.Collapsed;
    public Visibility FirstAuxillaryButtonVisibility
    {
        get { return firstAuxillaryButtonVisibility; }
        set
        {
            if (firstAuxillaryButtonVisibility == value)
                return;
            firstAuxillaryButtonVisibility = value;
            NotifyOfPropertyChange(() => FirstAuxillaryButtonVisibility);
        }
    }

    /// <summary>
    /// Second auxillary button text.
    /// </summary>
    private string secondAuxiliaryButtonText;
    public string SecondAuxiliaryButtonText
    {
        get { return secondAuxiliaryButtonText; }
        set
        {
            if (secondAuxiliaryButtonText == value)
                return;
            secondAuxiliaryButtonText = value;
            NotifyOfPropertyChange(() => SecondAuxiliaryButtonText);
        }
    }

    /// <summary>
    /// Second auxillary button visibility.
    /// </summary>
    private Visibility secondAuxillaryButtonVisibility = Visibility.Collapsed;
    public Visibility SecondAuxillaryButtonVisibility
    {
        get { return secondAuxillaryButtonVisibility; }
        set
        {
            if (secondAuxillaryButtonVisibility == value)
                return;
            secondAuxillaryButtonVisibility = value;
            NotifyOfPropertyChange(() => SecondAuxillaryButtonVisibility);
        }
    }
    #endregion // Properties.
}

观点是

<MahAppsDialogs:CustomDialog 
        x:Class="GambitFramework.Core.MessageBox.MessageBoxView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Caliburn="http://www.caliburnproject.org"
        xmlns:MahApps="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:Local="clr-namespace:GambitFramework.Core.MessageBox"
        xmlns:Converters="clr-namespace:GambitFramework.Core.Converters;assembly=GambitFramework"
        xmlns:MahAppsDialogs="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro"
        Title="{Binding Title}">
    <MahAppsDialogs:CustomDialog.Content>
        <TextBlock Text="{Binding DialogBody}"
                      Margin="0,5,0,0"
                      TextWrapping="Wrap"/>
    </MahAppsDialogs:CustomDialog.Content>
    <MahAppsDialogs:CustomDialog.DialogBottom>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="25*" />
                <ColumnDefinition Width="50*" />
                <ColumnDefinition Width="25*" />
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Column="1"
                            Orientation="Horizontal" 
                            HorizontalAlignment="Right"
                            Margin="0,0,0,0">
                <Button x:Name="AffirmativeButton" 
                          Content="{Binding AffirmativeButtonText}"
                          Visibility="{Binding AffirmativeButtonVisibility}"
                          Style="{StaticResource AccentedSquareButtonStyle}"
                          Caliburn:Message.Attach="[Event Click] = [Action AffirmativeButtonClick()]"
                          MinWidth="75"
                          Padding="15,0"
                          Margin="5,10,0,5"/>
                <Button x:Name="NegativeButton" 
                          Content="{Binding NegativeButtonText}"
                          Visibility="{Binding NegativeButtonVisibility}" 
                          Caliburn:Message.Attach="[Event Click] = [Action NegativeButtonClick()]"
                          MinWidth="75"
                          Padding="15,0"
                          Margin="10,10,0,5"/>
                <Button x:Name="FirstAuxiliaryButton" 
                          Content="{Binding FirstAuxillaryButtonText}"
                          Visibility="{Binding FirstAuxillaryButtonVisibility}"
                          Caliburn:Message.Attach="[Event Click] = [Action FirstAuxillaryButtonClick()]"
                          MinWidth="75"
                          Padding="15,0"
                          Margin="5,10,0,5"/>
                <Button x:Name="SecondAuxiliaryButton" 
                          Content="{Binding SecondAuxiliaryButtonText}"
                          Visibility="{Binding SecondAuxillaryButtonVisibility}"
                          Caliburn:Message.Attach="[Event Click] = [Action SecondAuxillaryButtonClick()]"
                          MinWidth="75"
                          Padding="15,0"
                          Margin="5,10,0,5"/>
            </StackPanel>
        </Grid>
    </MahAppsDialogs:CustomDialog.DialogBottom>
</MahAppsDialogs:CustomDialog>

这个 View 后面有一个空代码。然后可以按如下方式使用此代码

MessageBoxSettings settings = new MessageBoxSettings()
{
    MessageDialogStyle = MessageDialogStyle.AffirmativeAndNegative,
    MetroColorDialogScheme = MetroDialogColorScheme.Accented,
    AffirmativeButtonText = "Delete",
    NegativeButtonText = "Cancel"
};
string message = String.Format(
    "Are you sure you want to delete back test \"{0}\" {1}",
    SelectedBackTest.Name,
    SelectedBackTest.LastRun == null ? 
        String.Empty : 
        String.Format("which was late run on {0:G}?", SelectedBackTest.LastRun));
MessageDialogResult r = await dialogManager
    .ShowDialog<MessageDialogResult>(
        new MessageBoxViewModel("Confirm Delete", message, settings));
if (r == MessageDialogResult.Affirmative)
{
    ...
}

希望对您有所帮助。

关于c# - 等待 MahApps Metro Dialog 返回结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29228925/

相关文章:

c# - 上传图片时 PC 卡住一秒

c# - 我应该如何从 Task 更新 UI 线程?

WPF 对象将自身发送为 MultiBinding 路径

wpf - 无法在DataGrid中显示记录

c# - wpf c# 数据绑定(bind)以使用 viewModel 对象的属性设置字符串

javascript - 如何使用c# asp.net上传默认文件

c# - Visual Studio Visual Studio MVC asp.NET 谷歌地图

wpf - WPF的进度条不会在最后的ProgressChanged更新

c# - 向我的游戏添加 "open with..."功能

c# - Wpf 递归绑定(bind)