如何将 SaveAsDialog 的结果传递给 RelayCommand

本文关键字:RelayCommand 结果 SaveAsDialog | 更新日期: 2023-09-27 18:30:26

这是我的问题流程:

  1. 用户点击"保存"按钮

  2. 出现"另存为"对话框,用于选择目标路径

  3. 执行"保存"按钮的点击事件的中继命令

目前我对以下方面一无所知:

  1. 如何打开对话框并使用 EventToCommand 绑定执行 RelayCommand

  2. 如何将"另存为"对话框的选定路径传递到中继命令中

我正在使用 MVVM Light 库。

如何将 SaveAsDialog 的结果传递给 RelayCommand

虽然我认为 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;
}
我已经基于 MVVM Light

库中的 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就绪并不罕见,而且"追逐"可能需要一些时间,而客户会失去价值。

无论如何,对于当前的问题,有这种方法:

  1. 在视图模型中创建一个名为 OnSelectSavePath 的事件,该事件返回字符串 (filePath)
  2. 单击视图模型引发事件
  3. Page/UserControl/Window 使用运行 OpenFileDialog 或其他内容的方法订阅事件
  4. 页面/用户控件/窗口从该方法返回选定的字符串
  5. ViewModel 从事件中接收字符串并充分利用它。

通过这种方式,您可以正确地将 viewModel 与表示层分离(不给出选择表单的详细信息),同时仍提供所需的功能。

另一种方法是定义支持 MVVM 的自定义 openFileDialog。您将当前视图模型传递给对话框,对话框将更新视图模型上的 selectedPath 属性。

更好的是,你可以在codeBehing中做到这一点,然后使用selectedPath属性在viewModel上调用一些方法。这为您省去了所有的麻烦。

我知道

这是一个古老的线程,但对于初学者(像我一样)来说,获得正确的示例很重要。

就个人而言,我不喜欢上面的任何示例,因为我觉得在 ViewModel 中声明对话框违反了 MVVM 模式。恕我直言,ViewModel 不得知道任何与 UI 相关的控件,例如对话框或消息框。

在谷歌搜索时,我发现这个:恕我直言,http://www.matt.digital/mvvm-light-communicating-across-layers-with-services/这是如何以类似 MVVM 的方式呈现对话框的完美示例。