如何将 SaveAsDialog 的结果传递给 RelayCommand
本文关键字:RelayCommand 结果 SaveAsDialog | 更新日期: 2023-09-27 18:30:26
这是我的问题流程:
-
用户点击"保存"按钮
-
出现"另存为"对话框,用于选择目标路径
-
执行"保存"按钮的点击事件的中继命令
目前我对以下方面一无所知:
-
如何打开对话框并使用 EventToCommand 绑定执行 RelayCommand
-
如何将"另存为"对话框的选定路径传递到中继命令中
我正在使用 MVVM Light 库。
虽然我认为 Dmitriy Reznik 的答案相当不错,但另一种解决方案是使用按钮的命令来完成 ViewModel 中的大部分工作。这并不严格遵循 MVVM 模式,但可能更容易实现。
将按钮上的命令设置为视图模型上的 ICommand。ICommand 启动 SaveFileDialog,并在对话框关闭后将文件写入磁盘。由于您使用的是 MVVM Light,我将使用 RelayCommand 来实现 ICommand。
Xaml:
<Button Command="{Binding SaveAsClickCmd}/>
法典:
public class MyViewModel
{
public RelayCommand SaveAsClickCmd
{
get {
return _saveAsClickCmd ?? (_saveAsClickCmd = new RelayCommand(() => {
var dialog = new Microsoft.Win32.SaveFileDialog();
if (dialog.ShowDialog() != true)
return;
using (var stream = dialog.OpenFile()) {
//write out file to disk
}
}));
}
}
private RelayCommand _saveAsClickCmd;
}
库中的 DialogMessage 类创建了一个类(您需要引用 MVVM Light 库)
public class SaveFileDialogMessage : GenericMessage<string>
{
/// <summary>
/// Initializes a new instance of the <see cref="SaveFileDialogMessage" /> class.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="filter">The filter.</param>
/// <param name="callback">The callback.</param>
public SaveFileDialogMessage(string content, string filter, Action<bool?, string> callback)
: base(content)
{
Filter = filter;
Callback = callback;
}
/// <summary>
/// Initializes a new instance of the <see cref="SaveFileDialogMessage" /> class.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="content">The content.</param>
/// <param name="filter">The filter.</param>
/// <param name="callback">The callback.</param>
public SaveFileDialogMessage(object sender, string content, string filter, Action<bool?, string> callback)
: base(sender, content)
{
Filter = filter;
Callback = callback;
}
/// <summary>
/// Initializes a new instance of the <see cref="SaveFileDialogMessage" /> class.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="target">The target.</param>
/// <param name="content">The content.</param>
/// <param name="filter">The filter.</param>
/// <param name="callback">The callback.</param>
public SaveFileDialogMessage(object sender, object target, string content, string filter, Action<bool?, string> callback)
: base(sender, target, content)
{
Filter = filter;
Callback = callback;
}
/// <summary>
/// Gets a callback method that should be executed to deliver the result
/// of the message box to the object that sent the message.
/// </summary>
public Action<bool?, string> Callback { get; private set; }
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>
/// The title.
/// </value>
public string Title { get; set; }
/// <summary>
/// Sets or gets the filter property.
/// </summary>
public string Filter { get; set; }
/// <summary>
/// Utility method, checks if the <see cref="Callback" /> property is
/// null, and if it is not null, executes it.
/// </summary>
/// <param name="result">The result that must be passed
/// to the dialog message caller.</param>
/// <param name="fileName">Name of the file.</param>
public void ProcessCallback(bool? result, string fileName)
{
if (Callback != null)
{
Callback(result, fileName);
}
}
然后在您的视图模型中,您有如下所示的内容:
var dialog = new SaveFileDialogMessage("Title", "XML Files" + "|" + ".xml", ProcessSaveFileDialog);
Messenger.Default.Send(dialog);
private void ProcessSaveFileDialog(bool? dialogResult, string fileName)
{
..........
}
并在视图构造函数中:
/// <summary>
/// Initialize a new instance of the <see cref="MainView"/> class.
/// </summary>
public MainView()
{
InitializeComponent();
Messenger.Default.Register<SaveFileDialogMessage>(this, msg =>
{
var sfd = new SaveFileDialog { Filter = msg.Filter, Title = msg.Title };
var result = sfd.ShowDialog();
msg.ProcessCallback(result, sfd.FileName);
});
}
就个人而言,我不会通过在视图模型中完成所有操作来追求 MVVM 的纯度,并且会作弊为代码隐藏保存一些代码。组件不是开箱即用的MVVM就绪并不罕见,而且"追逐"可能需要一些时间,而客户会失去价值。
无论如何,对于当前的问题,有这种方法:
- 在视图模型中创建一个名为 OnSelectSavePath 的事件,该事件返回字符串 (filePath)
- 单击视图模型引发事件
- Page/UserControl/Window 使用运行 OpenFileDialog 或其他内容的方法订阅事件
- 页面/用户控件/窗口从该方法返回选定的字符串
- ViewModel 从事件中接收字符串并充分利用它。
通过这种方式,您可以正确地将 viewModel 与表示层分离(不给出选择表单的详细信息),同时仍提供所需的功能。
另一种方法是定义支持 MVVM 的自定义 openFileDialog。您将当前视图模型传递给对话框,对话框将更新视图模型上的 selectedPath 属性。
更好的是,你可以在codeBehing中做到这一点,然后使用selectedPath属性在viewModel上调用一些方法。这为您省去了所有的麻烦。
这是一个古老的线程,但对于初学者(像我一样)来说,获得正确的示例很重要。
就个人而言,我不喜欢上面的任何示例,因为我觉得在 ViewModel 中声明对话框违反了 MVVM 模式。恕我直言,ViewModel 不得知道任何与 UI 相关的控件,例如对话框或消息框。
在谷歌搜索时,我发现这个:恕我直言,http://www.matt.digital/mvvm-light-communicating-across-layers-with-services/这是如何以类似 MVVM 的方式呈现对话框的完美示例。