我有一个ViewModel向View发送消息(使用MVVM Light Messenger)以显示Metro对话框,如下所示:
在ViewModel中,我从DialogBox类中调用此代码:
DialogBox.ShowDialogBox(
(result) => { DialogResult(result); },
"Dialog Title",
"Dialog Message",
MessageDialogStyle.AffirmativeAndNegative
);
这是DialogBox类,它处理将消息发送到View:
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 codebehind具有以下代码来接收消息:
Messenger.Default.Register<DialogBox>(this, GlobalResources.MessengerTokens.dialogTokenMainWindow, dialogData =>
{
ShowMessageDialog(dialogData);
});
ShowMessageDialog处理显示实际的对话框。一切都很好。
目前,当用户选择了肯定/否定时,结果将返回并触发对ViewModel中DialogResult(结果)的动作调用,如最顶层的代码片段所示。
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)
对于我目前的情况,我能采取类似的方法吗?感谢您的任何建议,如果我的问题太长,请抱歉。
答案 0 :(得分:4)
我认为有更好的方式来做你正在做的事情。为了使这更像MVVM,这就是我所做的......首先,我使用Caliburn Micro来处理我的MVVM和MEF。首先,我们有两个接口:
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);
}
这些接口的实现是
/// <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; }
}
现在实际使用上述代码的视图和视图模型是
/// <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>
此视图后面有一个空代码。然后可以按如下方式使用此代码
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)
{
...
}
我希望这会有所帮助。
答案 1 :(得分:0)
可接受的答案需要一些额外的库和大量的代码。 这是我等待对话框完成的操作,仅使用MahApps。
using SysThread = System.Threading;
using WpfThread = System.Windows.Threading;
using SysTasks = System.Threading.Tasks;
using MahCtl = MahApps.Metro.Controls;
using MahDlg = MahApps.Metro.Controls.Dialogs;
using Win32 = Microsoft.Win32;
using Wpf = System.Windows;
using SysCompMod = System.ComponentModel;
[...]
MahCtl.MetroWindow parentMetroWindow = Wpf.Application.Current.Windows.OfType<MahCtl.MetroWindow>().First();
var metroDialogSettings = new MahDlg.MetroDialogSettings();
metroDialogSettings.OwnerCanCloseWithDialog = true; //does not appear to have any effect
metroDialogSettings.AnimateHide = false;
metroDialogSettings.AnimateShow = false;
[...]
using( SysThread.CancellationTokenSource tokenSource = new SysThread.CancellationTokenSource() )
{
metroDialogSettings.CancellationToken = tokenSource.Token;
SysTasks.Task<MahDlg.MessageDialogResult> task = MahDlg.DialogManager.ShowMessageAsync( parentMetroWindow, title, message, mahStyle, metroDialogSettings );
// ReSharper disable once AccessToDisposedClosure
SysCompMod.CancelEventHandler cancelEventHandler = (s, e) => tokenSource.Cancel();
parentMetroWindow.Closing += cancelEventHandler;
while( !(task.IsCompleted || task.IsCanceled || task.IsFaulted) )
Wpf.Application.Current.Dispatcher.Invoke( WpfThread.DispatcherPriority.Background, new Action( delegate { } ) );
parentMetroWindow.Closing -= cancelEventHandler;
return responseFromMahAppsMessageDialogResult( type, task.Result );
}
cancelEventHandler
是必需的,以防用户在模式对话框打开时尝试通过任务栏关闭主窗口。