.net 使用MVVM处理WPF中的对话框
在WPF的MVVM模式中,处理对话框是更复杂的操作之一。由于视图模型对视图一无所知,因此对话框通信可能会很有趣。我可以公开一个.net 使用MVVM处理WPF中的对话框,.net,wpf,design-patterns,mvvm,dialog,.net,Wpf,Design Patterns,Mvvm,Dialog,在WPF的MVVM模式中,处理对话框是更复杂的操作之一。由于视图模型对视图一无所知,因此对话框通信可能会很有趣。我可以公开一个ICommand,当视图调用它时,会出现一个对话框 有人知道处理对话框结果的好方法吗?我说的是windows对话框,比如MessageBox 我们这样做的方法之一是在viewmodel上有一个事件,当需要一个对话框时,视图将订阅该事件 公共事件事件处理程序RequiresDeleteDialog; 这是可以的,但这意味着视图需要代码,这是我不希望看到的。我认为对话框的处
ICommand
,当视图调用它时,会出现一个对话框
有人知道处理对话框结果的好方法吗?我说的是windows对话框,比如MessageBox
我们这样做的方法之一是在viewmodel上有一个事件,当需要一个对话框时,视图将订阅该事件
公共事件事件处理程序RequiresDeleteDialog;
这是可以的,但这意味着视图需要代码,这是我不希望看到的。我认为对话框的处理应该是视图的责任,视图需要代码来支持这一点 如果将ViewModel-View交互更改为处理对话框,则ViewModel依赖于该实现。处理此问题的最简单方法是让视图负责执行任务。如果这意味着显示一个对话框,那么可以,但也可以是状态栏中的状态消息等
我的观点是MVVM模式的全部要点是将业务逻辑与GUI分离,因此您不应该在业务层(ViewModel)中混合GUI逻辑(以显示对话框)。我在询问时也在考虑类似的问题 我当前的解决方案如下所示:
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)
// [...]
}
var NoTrump = ViewModelLocator.DialogService.AskBooleanQuestion("Really stop the trade war???", "");
public static SettingsVM Settings => SimpleIoc.Default.GetInstance<SettingsVM>();
<Window x:Class="YourViewNamespace.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:YourViewProject"
xmlns:vm="clr-namespace:YourVMProject;assembly=YourVMProject"
DataContext="{x:Static vm:ViewModelLocator.Settings}"
d:DataContext="{d:DesignInstance Type=vm:SettingsVM}" />
公共类选择任务模型:视图模型
其中TChoosable:ViewModel
{
公共选择任务模型(ICollection选项);
公共只读集合选项{get;}
公共无效选择(TChoosable choosen);
公共无效中止();
}
当视图模型决定需要用户输入时,它会调出一个
SelectionTaskModel
实例,其中包含用户可能的选择。基础设施负责调出相应的视图,在适当的时候,该视图将根据用户的选择调用Choose()
函数。我也遇到了同样的问题。我想出了一种在视图和ViewModel之间进行交互的方法。您可以开始从ViewModel向视图发送一条消息,告诉它显示一个messagebox,它将报告结果。然后ViewModel可以响应视图返回的结果
我在:编辑:十多年后,我可以看出,在这么多层面上,使用中介或任何其他消息传递模式都是一个非常糟糕的想法。不要这样做,只需实现Jeffrey的答案或在视图模型中注入IDialogService
您应该为此使用中介。 Mediator是一种常见的设计模式,在某些实现中也称为Messenger。 它是Register/Notify类型的范例,使您的视图模型和视图能够通过低耦合的消息传递机制进行通信 您应该查看GoogleWPF弟子组,只需搜索Mediator。 你会对答案非常满意 但是,您可以从以下内容开始: 享受吧 编辑:您可以在此处使用MVVM Light Toolkit查看此问题的答案:
一个好的MVVM对话框应该:
ShowDialog()
进行代码隐藏调用。支持对话框的Window类不能在XAML中声明,因此不能轻松地将其数据绑定到DataContext
为了解决这个问题,我编写了一个XAML存根控件,它位于逻辑树中,将数据绑定中继到窗口,并处理对话框的显示和隐藏。你可以在这里找到它:
它的使用非常简单,不需要对ViewModel进行任何奇怪的更改,也不需要事件或消息。基本调用如下所示:
<dialog:Dialog Content="{Binding Path=DialogViewModel}" Showing="True" />
您可能希望添加一个设置显示的样式。我在文章中对此进行了解释。我希望这对您有所帮助。我认为视图可能有代码来处理视图模型中的事件
根据事件/场景的不同,它还可能有一个订阅查看模型事件的事件触发器,以及一个或多个响应调用的操作。我建议放弃20世纪90年代的模式对话框,而是将控件实现为覆盖(画布+绝对定位),可见性绑定到VM中的布尔值。更接近ajax类型控件
这非常有用:
<BooleanToVisibilityConverter x:Key="booltoVis" />
例如:
<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>
下面是我如何将一个实现为用户控件。单击“x”关闭usercontrol代码隐藏中一行代码中的控件。(因为我在.exe中有我的视图,在dll中有ViewModels,所以我对操纵UI的代码并不感到难过。)
我当前的解决方案解决了您提到的大多数问题,但它完全是从特定于平台的事物中抽象出来的,可以重用。
此外,我没有使用任何代码隐藏,只与实现ICommand的DelegateCommand绑定。
对话框基本上是一个视图-一个单独的控件,它有自己的ViewModel,从主屏幕的ViewModel显示,但通过DelagateCommand绑定从UI触发
在这里查看完整的Silverlight 4解决方案在学习(仍在学习)MVVM时,我确实为这个概念挣扎了一段时间。我的决定,以及我认为其他人已经决定但我不清楚的是:
我最初的想法是,不应允许ViewModel直接调用对话框,因为它无权决定对话框的显示方式。由于这个原因,我开始思考如何像在MVP中一样传递消息(即View.ShowSaveFileDialog())。然而,我认为这是错误的做法
ViewModel可以直接调用对话框。然而,wh
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)
// [...]
}
var result = await _Service.ShowCustomMessageBox(new MyMessageBoxViewModel { /* What you want */ });
public interface IDialogService
{
void ShowMessage(string msg, bool isError);
bool AskBooleanQuestion(string msg);
string AskStringQuestion(string msg, string default_value);
string ShowOpen(string filter, string initDir = "", string title = "");
string ShowSave(string filter, string initDir = "", string title = "", string fileName = "");
string ShowFolder(string initDir = "");
bool ShowSettings();
}
public static IDialogService DialogService => SimpleIoc.Default.GetInstance<IDialogService>();
public class DialogPresenter : IDialogService
{
private static OpenFileDialog dlgOpen = new OpenFileDialog();
private static SaveFileDialog dlgSave = new SaveFileDialog();
private static FolderBrowserDialog dlgFolder = new FolderBrowserDialog();
/// <summary>
/// Displays a simple Information or Error message to the user.
/// </summary>
/// <param name="msg">String text that is to be displayed in the MessageBox</param>
/// <param name="isError">If true, Error icon is displayed. If false, Information icon is displayed.</param>
public void ShowMessage(string msg, bool isError)
{
if(isError)
System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.OK, MessageBoxImage.Error);
else
System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.OK, MessageBoxImage.Information);
}
/// <summary>
/// Displays a Yes/No MessageBox.Returns true if user clicks Yes, otherwise false.
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public bool AskBooleanQuestion(string msg)
{
var Result = System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
return Result;
}
/// <summary>
/// Displays Save dialog. User can specify file filter, initial directory and dialog title. Returns full path of the selected file if
/// user clicks Save button. Returns null if user clicks Cancel button.
/// </summary>
/// <param name="filter"></param>
/// <param name="initDir"></param>
/// <param name="title"></param>
/// <param name="fileName"></param>
/// <returns></returns>
public string ShowSave(string filter, string initDir = "", string title = "", string fileName = "")
{
if (!string.IsNullOrEmpty(title))
dlgSave.Title = title;
else
dlgSave.Title = "Save";
if (!string.IsNullOrEmpty(fileName))
dlgSave.FileName = fileName;
else
dlgSave.FileName = "";
dlgSave.Filter = filter;
if (!string.IsNullOrEmpty(initDir))
dlgSave.InitialDirectory = initDir;
if (dlgSave.ShowDialog() == DialogResult.OK)
return dlgSave.FileName;
else
return null;
}
public string ShowFolder(string initDir = "")
{
if (!string.IsNullOrEmpty(initDir))
dlgFolder.SelectedPath = initDir;
if (dlgFolder.ShowDialog() == DialogResult.OK)
return dlgFolder.SelectedPath;
else
return null;
}
/// <summary>
/// Displays Open dialog. User can specify file filter, initial directory and dialog title. Returns full path of the selected file if
/// user clicks Open button. Returns null if user clicks Cancel button.
/// </summary>
/// <param name="filter"></param>
/// <param name="initDir"></param>
/// <param name="title"></param>
/// <returns></returns>
public string ShowOpen(string filter, string initDir = "", string title = "")
{
if (!string.IsNullOrEmpty(title))
dlgOpen.Title = title;
else
dlgOpen.Title = "Open";
dlgOpen.Multiselect = false;
dlgOpen.Filter = filter;
if (!string.IsNullOrEmpty(initDir))
dlgOpen.InitialDirectory = initDir;
if (dlgOpen.ShowDialog() == DialogResult.OK)
return dlgOpen.FileName;
else
return null;
}
/// <summary>
/// Shows Settings dialog.
/// </summary>
/// <returns>true if User clicks OK button, otherwise false.</returns>
public bool ShowSettings()
{
var w = new SettingsWindow();
MakeChild(w); //Show this dialog as child of Microsoft Word window.
var Result = w.ShowDialog().Value;
return Result;
}
/// <summary>
/// Prompts user for a single value input. First parameter specifies the message to be displayed in the dialog
/// and the second string specifies the default value to be displayed in the input box.
/// </summary>
/// <param name="m"></param>
public string AskStringQuestion(string msg, string default_value)
{
string Result = null;
InputBox w = new InputBox();
MakeChild(w);
if (w.ShowDialog(msg, default_value).Value)
Result = w.Value;
return Result;
}
/// <summary>
/// Sets Word window as parent of the specified window.
/// </summary>
/// <param name="w"></param>
private static void MakeChild(System.Windows.Window w)
{
IntPtr HWND = Process.GetCurrentProcess().MainWindowHandle;
var helper = new WindowInteropHelper(w) { Owner = HWND };
}
}
SimpleIoc.Default.Register<IDialogService, DialogPresenter>();
var NoTrump = ViewModelLocator.DialogService.AskBooleanQuestion("Really stop the trade war???", "");
public static SettingsVM Settings => SimpleIoc.Default.GetInstance<SettingsVM>();
<Window x:Class="YourViewNamespace.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:YourViewProject"
xmlns:vm="clr-namespace:YourVMProject;assembly=YourVMProject"
DataContext="{x:Static vm:ViewModelLocator.Settings}"
d:DataContext="{d:DesignInstance Type=vm:SettingsVM}" />