Adding confirmation dialog to EventToCommand
Laurent,
a common desired behavior when a user selects a button to perform an action is to display a confirmation dialog before performing that action. When this behavior is handled in conjunction with the viewmodel, a good amount of plumbing is required to make this happen. Since this behavior could be considered strictly a GUI issue, I wanted to devise a method that allowed the view to handle confirmation dialogs.
As a result, I have inherited from your EventToCommand class and created EventToCommandWithConfirm. This class calls to a custom messagebox view that I'm not going to include, but I wanted to get your feedback on the concept and whether it's something that could be considered to be included in your toolkit.
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace GalaSoft.MvvmLight.Command
{
public partial class EventToCommandWithConfirm : EventToCommand
{
protected override void Invoke(object parameter)
{
// if confirmation is not required, the command can be run immediately
if (!IsConfirm)
{
base.Invoke(parameter);
}
else
{
switch (ConfirmButtons)
{
case MessageBoxPopUp.Buttons.YesNo:
case MessageBoxPopUp.Buttons.YesNoCancel:
MessageBoxPopUp.Show(ConfirmMessage, ConfirmCaption , ConfirmButtons, YesAction: () => base.Invoke(parameter));
break;
case MessageBoxPopUp.Buttons.Ok:
case MessageBoxPopUp.Buttons.OkCancel:
case MessageBoxPopUp.Buttons.None:
default:
MessageBoxPopUp.Show(ConfirmMessage, ConfirmCaption, ConfirmButtons, OKAction: () => base.Invoke(parameter));
break;
}
}
}
public static readonly DependencyProperty IsConfirmProperty = DependencyProperty.Register("Confirm", typeof(bool), typeof(EventToCommandWithConfirm), null);
public bool IsConfirm
{
get { return (bool)GetValue(IsConfirmProperty); }
set { SetValue(IsConfirmProperty, value); }
}
public static readonly DependencyProperty ConfirmCaptionProperty = DependencyProperty.Register("ConfirmCaption", typeof(string), typeof(EventToCommandWithConfirm), null);
public string ConfirmCaption
{
开发者_Python百科 get { return (string)GetValue(ConfirmCaptionProperty); }
set { SetValue(ConfirmCaptionProperty, value); }
}
public static readonly DependencyProperty ConfirmMessageProperty = DependencyProperty.Register("ConfirmMessage", typeof(string), typeof(EventToCommandWithConfirm), null);
public string ConfirmMessage
{
get { return (string)GetValue(ConfirmMessageProperty); }
set { SetValue(ConfirmMessageProperty, value); }
}
public static readonly DependencyProperty ConfirmButtonsProperty = DependencyProperty.Register("ConfirmButtons", typeof(MessageBoxPopUp.Buttons), typeof(EventToCommandWithConfirm), null);
public MessageBoxPopUp.Buttons ConfirmButtons
{
get { return (MessageBoxPopUp.Buttons)GetValue(ConfirmButtonsProperty); }
set { SetValue(ConfirmButtonsProperty, value); }
}
}
}
Example use:
<Button Content="Delete Room..." Height="36" HorizontalAlignment="Left" Margin="279.838,237,0,0" Name="DeleteRoomButton" VerticalAlignment="Top" Width="92" Grid.Column="2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<confirm:EventToCommandWithConfirm Command="{Binding DeleteRoomClick}" MustToggleIsEnabled="True" ConfirmMessage="{Binding cvsRooms.View.CurrentItem.Description}" ConfirmCaption="Delete Room?" IsConfirm="True" ConfirmButtons="YesNoCancel"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
MessageBoxPopUp.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
namespace GalaSoft.MvvmLight.Command
{
public partial class MessageBoxPopUp : ChildWindow
{
public enum Buttons
{
Ok,
YesNo,
OkCancel,
YesNoCancel,
None,
}
public enum ButtonTypes
{
Ok,
Yes,
No,
Cancel,
Custom,
}
private int _buttonCount = 0;
private Dictionary<int, Button> _buttons = new Dictionary<int, Button>();
private int _defaultButtonIdx = Int32.MinValue;
private Action _defaultAction = null;
private Button _defaultButton = null;
public string Message
{
get { return MessageTextBlock.Text; }
set { MessageTextBlock.Text = value; }
}
public ButtonTypes DefaultButton {get;set;}
public ButtonTypes ResultButton {get; private set;}
public int ButtonSelectedIdx { get; private set; }
public MessageBoxPopUp()
{
InitializeComponent();
}
/// <summary>
/// Displays a message box with the specified text, caption, buttons
/// </summary>
/// <param name="Message">The text to display in the message box.</param>
/// <param name="Caption">he text to display in the title bar of the message box.</param>
/// <param name="Buttons">The set of buttons to display</param>
/// <param name="DefaultButton">Which button is the default</param>
/// <param name="Width">Width of the message box</param>
/// <param name="Height">Height of the message box</param>
/// <param name="Opacity">Opacity of the message box</param>
/// <param name="OKAction">Action to perform when the OK button is selected</param>
/// <param name="CancelAction">Action to perform when the Cancel button is selected</param>
/// <param name="YesAction">Action to perform when the Yes button is selected</param>
/// <param name="NoAction">Action to perform when the No button is selected</param>
/// <param name="MessageBoxToUse">Optional pre-configured instance of window to use. Use only if custom results are required.</param>
/// <param name="CustomButton">Any number of custom pairs of button names and associated actions if selected</param>
public static void Show(String Message, String Caption = "", Buttons Buttons = Buttons.None, ButtonTypes DefaultButton = ButtonTypes.Ok, double Width = Int32.MinValue, double Height = Int32.MinValue, double Opacity = Int32.MinValue, Action OKAction = null, Action CancelAction = null, Action YesAction = null, Action NoAction = null, MessageBoxPopUp MessageBoxToUse = null, params Tuple<string, Action>[] CustomButton)
{
MessageBoxPopUp msgBox = MessageBoxToUse != null ? MessageBoxToUse : new MessageBoxPopUp();
msgBox.ResultButton = ButtonTypes.Cancel;
if (Width != Int32.MinValue)
msgBox.Width = Width;
if (Width != Int32.MinValue)
msgBox.Height = Height;
if (Width != Int32.MinValue)
msgBox.Opacity = Opacity;
msgBox.Title = Caption;
msgBox.Message = Message;
msgBox.ButtonSelectedIdx = -1;
// create the stock buttons
if (Buttons == Buttons.Ok || Buttons == Buttons.OkCancel)
msgBox.CreateButton("Ok", () => { msgBox.ResultButton = ButtonTypes.Ok; if (OKAction != null) msgBox.Closed += (s, e) => OKAction.Invoke(); msgBox.DialogResult = true; }, DefaultButton == ButtonTypes.Ok);
if (Buttons == Buttons.YesNo || Buttons == Buttons.YesNoCancel)
msgBox.CreateButton("Yes", () => { msgBox.ResultButton = ButtonTypes.Yes; if (YesAction != null) msgBox.Closed += (s, e) => YesAction.Invoke(); msgBox.DialogResult = true; }, DefaultButton == ButtonTypes.Yes);
if (Buttons == Buttons.YesNo || Buttons == Buttons.YesNoCancel)
msgBox.CreateButton("No", () => { msgBox.ResultButton = ButtonTypes.No; if (NoAction != null) msgBox.Closed += (s, e) => NoAction.Invoke(); msgBox.DialogResult = false; }, DefaultButton == ButtonTypes.No);
if (Buttons == Buttons.YesNoCancel || Buttons == Buttons.OkCancel)
msgBox.CreateButton("Cancel", () => { msgBox.ResultButton = ButtonTypes.Cancel; if (CancelAction != null) msgBox.Closed += (s, e) => CancelAction.Invoke(); msgBox.DialogResult = false; }, DefaultButton == ButtonTypes.Cancel);
if (CustomButton != null)
{
foreach (Tuple<string, Action> custom in CustomButton)
{
msgBox.CreateButton(custom.Item1, () => { msgBox.ResultButton = ButtonTypes.Custom; if (custom.Item2 != null) msgBox.Closed += (s, e) => custom.Item2.Invoke(); msgBox.DialogResult = true; }, msgBox._buttons.Count == 0);
}
}
// add the buttons to the grid
resetAndClearGrid(msgBox.ButtonsGrid);
addColumnDefinitionsToGrid(msgBox.ButtonsGrid, msgBox._buttons.Count);
for (int i = 0; i < msgBox._buttons.Count; i++)
{
addButtonToGrid(msgBox.ButtonsGrid, msgBox._buttons[i], i);
}
if (msgBox._defaultButton != null)
msgBox._defaultButton.Focus();
msgBox.Show();
}
private static void resetAndClearGrid(Grid grid)
{
grid.Children.Clear();
grid.ColumnDefinitions.Clear();
}
private static void addColumnDefinitionsToGrid(Grid grid, int columns)
{
for (var i = 0; i < columns; i++)
{ grid.ColumnDefinitions.Add(new ColumnDefinition()); }
}
private static void addButtonToGrid(Panel grid, UIElement button, int columnIndex)
{
grid.Children.Add(button);
button.SetValue(Grid.ColumnProperty, columnIndex);
}
private void CreateButton(String ButtonText, Action ButtonAction, bool IsDefaultButton)
{
Button _newButton = new Button
{
Content = ButtonText,
Margin = new Thickness(2)
};
int thisButtonIdx = _buttons.Count;
Action doAction = () => { ButtonSelectedIdx = thisButtonIdx; ButtonAction.Invoke(); };
_newButton.Click += (sender, args) =>
doAction.Invoke();
_buttons.Add(_buttonCount++, _newButton);
if (IsDefaultButton)
{
_defaultButton = _newButton;
_defaultAction = doAction;
}
}
}
}
I see some positive and some negative in your idea. The positive is that less "plumbing" is needed. The negative is that the MessageBoxPopUp is not configurable.
At MIX11, I am going to present a way to show dialogs which I think is more flexible. I have been using that in multiple WP7 apps and Silverlight apps, and it works quite well. It is a little bit more "plumbing" indeed but it is flexible, easy to test (I will show how at MIX) and can be packed in an assembly for reuse.
Cheers, Laurent
精彩评论