向 EventToCommand 添加确认对话框
Laurent 表示,
当用户选择按钮来执行操作时,常见的期望行为是在执行该操作之前显示确认对话框。当这种行为与视图模型结合处理时,需要大量的管道来实现这一点。由于这种行为严格地可以被视为 GUI 问题,因此我想设计一种允许视图处理确认对话框的方法。
因此,我继承了您的 EventToCommand 类并创建了 EventToCommandWithConfirm。此类调用一个自定义消息框视图,我不会包含该视图,但我想获得您对这个概念的反馈以及它是否可以考虑包含在您的工具包中。
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
{
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); }
}
}
}
使用示例:
<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;
}
}
}
}
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
{
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;
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我在你的想法中看到了一些积极的一面和一些消极的一面。积极的一面是需要更少的“管道”。缺点是 MessageBoxPopUp 不可配置。
在MIX11中,我将提出一种我认为更灵活的显示对话框的方式。我已经在多个 WP7 应用程序和 Silverlight 应用程序中使用它,并且效果非常好。它确实有点“管道”,但它很灵活,易于测试(我将在 MIX 中展示如何进行)并且可以打包在组件中以供重用。
干杯,
洛朗
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