在WPF的MVVM模式中,处理对话框是比较复杂的操作之一。由于视图模型不知道视图的任何信息,因此对话框通信可能会很有趣。我可以公开一个ICommand,当视图调用它时,就会出现一个对话框。
有人知道处理对话框结果的好方法吗?我说的是windows对话框,比如MessageBox。
其中一种方法是在视图模型上设置一个事件,当需要对话框时,视图会订阅该事件。
public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;
这是可以的,但这意味着视图需要代码,这是我想要避免的。
使用可冻结命令
<Grid>
<Grid.DataContext>
<WpfApplication1:ViewModel />
</Grid.DataContext>
<Button Content="Text">
<Button.Command>
<WpfApplication1:MessageBoxCommand YesCommand="{Binding MyViewModelCommand}" />
</Button.Command>
</Button>
</Grid>
public class MessageBoxCommand : Freezable, ICommand
{
public static readonly DependencyProperty YesCommandProperty = DependencyProperty.Register(
"YesCommand",
typeof (ICommand),
typeof (MessageBoxCommand),
new FrameworkPropertyMetadata(null)
);
public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register(
"OKCommand",
typeof (ICommand),
typeof (MessageBoxCommand),
new FrameworkPropertyMetadata(null)
);
public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register(
"CancelCommand",
typeof (ICommand),
typeof (MessageBoxCommand),
new FrameworkPropertyMetadata(null)
);
public static readonly DependencyProperty NoCommandProperty = DependencyProperty.Register(
"NoCommand",
typeof (ICommand),
typeof (MessageBoxCommand),
new FrameworkPropertyMetadata(null)
);
public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
"Message",
typeof (string),
typeof (MessageBoxCommand),
new FrameworkPropertyMetadata("")
);
public static readonly DependencyProperty MessageBoxButtonsProperty = DependencyProperty.Register(
"MessageBoxButtons",
typeof(MessageBoxButton),
typeof(MessageBoxCommand),
new FrameworkPropertyMetadata(MessageBoxButton.OKCancel)
);
public ICommand YesCommand
{
get { return (ICommand) GetValue(YesCommandProperty); }
set { SetValue(YesCommandProperty, value); }
}
public ICommand OKCommand
{
get { return (ICommand) GetValue(OKCommandProperty); }
set { SetValue(OKCommandProperty, value); }
}
public ICommand CancelCommand
{
get { return (ICommand) GetValue(CancelCommandProperty); }
set { SetValue(CancelCommandProperty, value); }
}
public ICommand NoCommand
{
get { return (ICommand) GetValue(NoCommandProperty); }
set { SetValue(NoCommandProperty, value); }
}
public MessageBoxButton MessageBoxButtons
{
get { return (MessageBoxButton)GetValue(MessageBoxButtonsProperty); }
set { SetValue(MessageBoxButtonsProperty, value); }
}
public string Message
{
get { return (string) GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public void Execute(object parameter)
{
var messageBoxResult = MessageBox.Show(Message);
switch (messageBoxResult)
{
case MessageBoxResult.OK:
OKCommand.Execute(null);
break;
case MessageBoxResult.Yes:
YesCommand.Execute(null);
break;
case MessageBoxResult.No:
NoCommand.Execute(null);
break;
case MessageBoxResult.Cancel:
if (CancelCommand != null) CancelCommand.Execute(null); //Cancel usually means do nothing ,so can be null
break;
}
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
}
我知道这是一个老问题,但当我做这个搜索时,我发现了很多相关的问题,但我没有找到一个真正明确的回答。所以我做了我自己的对话框/消息框/popin的实现,我分享它!
我认为这是“MVVM证明”,我试着让它简单和适当,但我是WPF的新手,所以请随意评论,甚至提出拉请求。
https://github.com/Plasma-Paris/Plasma.WpfUtils
你可以这样使用它:
public RelayCommand YesNoMessageBoxCommand { get; private set; }
async void YesNoMessageBox()
{
var result = await _Service.ShowMessage("This is the content of the message box", "This is the title", System.Windows.MessageBoxButton.YesNo);
if (result == System.Windows.MessageBoxResult.Yes)
// [...]
}
或者像这样,如果你想要更复杂的popin:
var result = await _Service.ShowCustomMessageBox(new MyMessageBoxViewModel { /* What you want */ });
它展示了这样的东西:
我建议放弃20世纪90年代的模态对话框,而是实现一个覆盖控件(画布+绝对定位),可见性绑定到虚拟机中的布尔值。更接近ajax类型控件。
这非常有用:
<BooleanToVisibilityConverter x:Key="booltoVis" />
如:
<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>
下面是我如何实现一个用户控件。单击“x”关闭后面用户控件代码中的一行代码中的控件。(因为我有我的视图在一个。exe和ViewModels在一个dll,我不觉得代码操纵UI。)
在学习(仍在学习)MVVM时,我真的为这个概念挣扎了一段时间。我的决定,以及我认为其他人已经决定但我不清楚的是:
我最初的想法是ViewModel不应该被允许直接调用对话框,因为它没有业务决定对话框应该如何显示。正因为如此,我开始思考如何像在MVP(即View.ShowSaveFileDialog())一样传递消息。然而,我认为这是错误的方法。
ViewModel直接调用对话框是可以的。然而,当您测试ViewModel时,这意味着对话框要么在测试期间弹出,要么全部失败(从未真正尝试过这个)。
因此,在测试时需要使用对话框的“测试”版本。这意味着,对于您拥有的任何对话,您都需要创建一个Interface,并模拟对话响应,或者创建一个具有默认行为的测试模拟。
您应该已经在使用某种服务定位器或IoC,您可以对其进行配置,以便根据上下文提供正确的版本。
使用这种方法,你的ViewModel仍然是可测试的,根据你如何模拟对话框,你可以控制行为。
希望这能有所帮助。