我认为有更好的方法来做你正在做的事情。为了让这个更 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)
{
...
}
我希望这会有所帮助。