MVVM设计模式中的确认窗口
本文关键字:确认 窗口 设计模式 MVVM | 更新日期: 2023-09-27 18:06:34
你好,我想显示确认窗口的按钮点击。我正在尝试使用MVVM设计模式开发,我已经实现了它,但我不认为在viewModel中调用视图
这才是正确的做法。我已经附上了这个代码,请通过它是正确的方式或不
<Window x:Class="MessegeBox_Demo_2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Ckick ME" HorizontalAlignment="Left"
Command="{Binding GetMessegeboxCommand}"
Margin="200,131,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
public class MainWindowViewModel : ViewModelBaseClass
{
private ICommand _getMessegeboxCommand;
public ICommand GetMessegeboxCommand
{
get
{
return _getMessegeboxCommand ?? (_getMessegeboxCommand = new MessegeBox_Demo_2.Command.realyCommand(() => ShowUsercontrol(), true));
}
}
private void ShowUsercontrol()
{
MessegeBox_Demo_2.View.Window1 mbox = new View.Window1();
mbox.ShowDialog();
}
}
最简单的方法是实现一个dialogservice,并使用依赖注入将服务注入到视图模型中。可以依赖于接口,但不能依赖于具体的实现。
下面是我使用的接口:
namespace DialogServiceInterfaceLibrary
{
public enum MessageBoxButton
{
// Summary:
// The message box displays an OK button.
OK = 0,
//
// Summary:
// The message box displays OK and Cancel buttons.
OKCancel = 1,
//
// Summary:
// The message box displays Yes, No, and Cancel buttons.
YesNoCancel = 3,
//
// Summary:
// The message box displays Yes and No buttons.
YesNo = 4,
}
public enum MessageBoxResult
{
// Summary:
// The message box returns no result.
None = 0,
//
// Summary:
// The result value of the message box is OK.
OK = 1,
//
// Summary:
// The result value of the message box is Cancel.
Cancel = 2,
//
// Summary:
// The result value of the message box is Yes.
Yes = 6,
//
// Summary:
// The result value of the message box is No.
No = 7,
}
// Summary:
// Specifies the icon that is displayed by a message box.
public enum MessageBoxIcon
{
// Summary:
// No icon is displayed.
None = 0,
//
// Summary:
// The message box contains a symbol consisting of white X in a circle with
// a red background.
Error = 16,
//
// Summary:
// The message box contains a symbol consisting of a white X in a circle with
// a red background.
Hand = 16,
//
// Summary:
// The message box contains a symbol consisting of white X in a circle with
// a red background.
Stop = 16,
//
// Summary:
// The message box contains a symbol consisting of a question mark in a circle.
Question = 32,
//
// Summary:
// The message box contains a symbol consisting of an exclamation point in a
// triangle with a yellow background.
Exclamation = 48,
//
// Summary:
// The message box contains a symbol consisting of an exclamation point in a
// triangle with a yellow background.
Warning = 48,
//
// Summary:
// The message box contains a symbol consisting of a lowercase letter i in a
// circle.
Information = 64,
//
// Summary:
// The message box contains a symbol consisting of a lowercase letter i in a
// circle.
Asterisk = 64,
}
public interface IDialogService
{
bool OpenFileDialog(bool checkFileExists,string Filter, out string FileName);
void OpenGenericDialog(object Context,IRegionManager RegionManager);
MessageBoxResult ShowMessageBox(string message, string caption, MessageBoxButton buttons, MessageBoxIcon icon);
}
和实现:
public class DialogService : IDialogService
{
public bool OpenFileDialog(bool checkFileExists, string Filter, out string FileName)
{
FileName = "";
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = false;
//openFileDialog.Filter = "All Image Files | *.jpg;*.png | All files | *.*";
openFileDialog.Filter = Filter;
openFileDialog.CheckFileExists = checkFileExists;
bool result = ((bool)openFileDialog.ShowDialog());
if (result)
{
FileName = openFileDialog.FileName;
}
return result;
}
public void OpenGenericDialog(object Context,IRegionManager RegionManager)
{
GenericDialogWindow dlg = new GenericDialogWindow(Context,RegionManager);
dlg.Owner = System.Windows.Application.Current.MainWindow;
dlg.Show();
}
public MessageBoxResult ShowMessageBox(string message, string caption, MessageBoxButton buttons, MessageBoxIcon icon)
{
return (DialogServiceInterfaceLibrary.MessageBoxResult)System.Windows.MessageBox.Show(message, caption,
(System.Windows.MessageBoxButton)buttons,
(System.Windows.MessageBoxImage)icon);
}
}
然后将IDialogservice注入视图模型。接口和具体实现通常在不同的程序集中
MainWindowViewModel(IDialogService dialogservice){
_dialogservice = dialogservice;
}
private void ShowUsercontrol()
{
_dialogservice.ShowMessageBox(... //you get what i mean ;-)
}
dialogservice能够打开标准的窗口对话框,比如文件打开对话框。一般的版本也可以使用,但是你需要理解prism,它有点复杂。Prism还允许使用交互请求从视图模型到视图进行通信。我更喜欢用这种方法来处理棱镜,但如果你不知道棱镜,就忘了那句话。对于一个简单的确认窗口来说,它可能太复杂了。像这样简单的dialogservice是一个简单的确认窗口的理想选择。
通过视图模型的构造函数注入你的依赖,你已经在使用反转控制容器的正确方向上移动了。并且允许更容易的单元测试,因为您可以模拟注入的接口来检查它们是否被正确调用等等。
-
DialogService方法比消息传递机制更适合于此场景。它更直接,更容易调试,更容易编写,更容易理解,你不需要任何第三方框架等。
-
提示:依赖注入是不错的,但是你通常需要相当多的服务,如
NavigationService
,DialogService
,NotificationService
等,如果你需要将它们注入到很多视图模型中,这些元素就会变得庞大,无聊,重复相同的注入等等。除了依赖注入,你可以使用任何其他的"testable"方法。
由于DialogService在所有视图模型中都是相同的,所以你不必把它注入到每个视图模型中,但你可以使用某种AppContext类或服务定位器。
ViewModelBase类示例:
public class ViewModelBase : BindableBase
{
public virtual IDialogService DialogService
{
get { return AppContext.Current.DialogService; }
}
}
具体视图模型示例:
public class HomePageViewModel : ViewModelBase
{
...
public void Cancel_CommandExecute()
{
var dlgResult = DialogService.ShowMessageBox("Do you really want to discard unsaved changes?", "Confirm Exit", DialogButtons.YesNo);
if (dlgResult != MessageBoxResult.Yes) return;
}
}
对话服务:
public interface IDialogService
{
MessageBoxResult ShowMessageBox(string messageBoxText, string caption = null, MessageBoxButton buttons = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.None);
}
public class DialogService : IDialogService
{
public virtual MessageBoxResult ShowMessageBox(string messageBoxText, string caption = null, MessageBoxButton buttons = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.None)
{
return MessageBox.Show(messageBoxText, caption, buttons, icon, defaultResult);
}
}
在测试中,您可以模拟AppContext.Current
,也可以覆盖ViewModelBase.DialogService
属性。
也许这不是嘲笑DialogService
的最干净的方式,但这是一种实用的方法。它使你的视图模型代码更干净,可读性和可维护性,因为你没有注入和存储DialogService
实例在每个视图模型。您的视图模型仍然与视图解耦,可测试,可混合等
您可以在后面的代码中定义和填充Binding。由于后台代码是View的一部分,因此在其中调用Messagebox并不会破坏MVVM模式。这样,您可以在设置绑定值之前显示一个确认对话框。
后面需要的代码是:
public partial class MainWindow : Window
{
private DependencyProperty myDP;
public MainWindow()
{
...
Binding myBinding = new Binding();
myBinding.Path = new PropertyPath("myValue"); //myValue is a variable in ViewModel
myBinding.Source = DataContext;
myDP = DependencyProperty.Register("myValue", typeof(/*class or primitive type*/), typeof(MainWindow));
BindingOperations.SetBinding(this, myDP, myBinding);
...
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result = MessageBox.Show("Do you really want to do that?", "", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes
{
SetValue(myDP, /*value*/); //this sets the Binding value. myValue in ViewModel is set
}
}
}
要在单击按钮时调用Button_Click
-方法,添加
Click="Button_Click"
如果你想坚持MVVM模式,你不应该(永远)在视图模型中调用UI方法。
从ViewModel打开/关闭窗口的正确方法是使用MVVMLight的Messanger或Prism的EventAggregator向视图发送消息。
EventAggregator允许ViewModel向订阅者列表发送消息。当你订阅一个特定的消息时,你附加了一个要执行的函数。
我知道你正在学习这种模式的机制,所以你可以编写自己的EventAggregator并使用它。
我使用了MVVM轻型消息传递。PRISM库也提供了一种很好的方法。
为了处理从视图模型触发的交互和由位于视图中的控件触发的交互,Prism库提供了InteractionRequests和InteractionRequestTriggers,以及自定义的InvokeCommandAction操作。InvokeCommandAction用于将包含事件的触发器连接到WPF命令。
在ViewModel中创建InteractionRequest属性:
public InteractionRequest<IConfirmation> ConfirmationRequest { get; private set; }
像这样调用交互:
private void RaiseConfirmation()
{
this.ConfirmationRequest.Raise(
new Confirmation { Content = "Confirmation Message", Title = "Confirmation" },
c => { InteractionResultMessage = c.Confirmed ? "The user accepted." : "The user cancelled."; });
}
要使用交互请求,需要在视图的XAML代码中定义相应的InteractionRequestTrigger:
<prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest, Mode=OneWay}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True"/>
</prism:InteractionRequestTrigger>
参见交互式快速入门使用Prism库5.0 for WPF