1

这是我的问题的流程:

  1. 用户点击“保存”按钮

  2. 出现“另存为”对话框以选择目标路径

  3. 执行“保存”按钮的点击事件的 RelayCommand

目前我对以下内容一无所知:

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

  2. 如何将“另存为”对话框的选定路径传递到 RelayCommand

我正在使用 MVVM Light 库。

4

4 回答 4

2

虽然我认为 Dmitriy Reznik 的回答相当不错,但另一个解决方案是使用按钮的命令来完成 ViewModel 中的大部分工作。这并不严格遵循 MVVM 模式,但它可能更容易实现。

将 Button 上的 Command 设置为 ViewModel 上的 ICommand。ICommand 启动 SaveFileDialog 并在对话框关闭后将文件写入磁盘。由于您使用的是 MVVM Light,我将使用 RelayCommand 来实现 ICommand。

xml:

<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;
}
于 2012-04-10T21:03:04.630 回答
1

我基于 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);
        }
    }

然后在你的 ViewModel 你有这样的东西:

    var dialog = new SaveFileDialogMessage("Title", "XML Files" + "|" + ".xml", ProcessSaveFileDialog);
    Messenger.Default.Send(dialog);

    private void ProcessSaveFileDialog(bool? dialogResult, string fileName)
    {
            ..........
    }

并在您的 View 构造函数中:

    /// <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);
                                                                });
    }
于 2013-01-22T17:31:04.257 回答
0

就个人而言,我不会通过在 viewModel 中完成所有事情来追求 MVVM 的纯度,并且会作弊,为代码隐藏节省一些代码。组件不是开箱即用的 MVVM 就绪的情况并不少见,而且“追逐”可能需要时间,而客户会失去价值。

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

  1. 在 viewModel 中创建一个名为 OnSelectSavePath 的事件,该事件返回字符串 (filePath)
  2. 单击时 viewModel 引发事件
  3. Page/UserControl/Window 使用运行 OpenFileDialog 之类的方法订阅事件
  4. Page/UserControl/Window 从该方法返回选定的字符串
  5. ViewModel 从事件接收字符串并充分利用它。

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

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

更好的是,您可以在 codeBehin 中执行此操作,然后使用 selectedPath 属性在 viewModel 上调用一些方法。这为您省去了所有麻烦。

于 2012-04-09T18:47:55.973 回答
0

我知道这是一个旧线程,但对于初学者(如我)来说,获得正确的示例很重要。

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

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

于 2014-12-30T13:00:10.077 回答