【问题标题】:WPF Command usage beyond MVVMMVVM 之外的 WPF 命令用法
【发布时间】:2014-03-14 20:09:49
【问题描述】:

我一直在尝试通过 WPF 学习正确的 MVVM 用法。我基本上明白了,但我在使用命令的特定问题上有点卡住了。我知道一个命令允许我执行可由多个 UI 元素触发的相同操作,但我见过的所有示例都在视图模型或视图中定义了命令。

当操作范围超出视图模型时,您如何使用命令?例如,也许我想定义一个关闭应用程序的命令。这将允许按钮和菜单项都触发关闭,但如果关闭应用程序还需要其他操作,例如关闭数据库连接和写入文件,该怎么办?这些类型的任务通常发生在模型或视图之外的某个地方。

在这种情况下,我是否只是将 UI 元素连接到一个事件?我是否在视图模型中声明事件并让命令触发事件?

【问题讨论】:

    标签: wpf events mvvm command


    【解决方案1】:

    我一直在尝试学习如何使用 WPF 正确使用 MVVM。

    在实践中,MVVM 可以用一句话来概括:视图模型负责提供其视图所需的所有数据和功能

    当操作范围超出视图模型时,您如何使用命令?

    在 MVVM 中,每个视图都有一个视图模型,因此没有比视图模型更远的。在 WPF 中处理 ICommands 使用的一种好方法是使用基于 delegateCommands 之一,例如流行的 RelayCommand。您可以在 MSDN 上的WPF Apps With The Model-View-ViewModel Design Pattern 页面中找到详细信息。

    在 UI 中,它们的使用与任何其他 ICommand 一样,但在视图模型中,您可以只使用方法甚至内联 delegates 来处理 ICommand.ExecuteICommand.CanExecute 方法。我使用我自己的RelayCommand 版本,对于您的场景,我会这样做:

    public ICommand CloseCommand
    {
        get
        {
            return new ActionCommand(action => Close(null), canExecute => CanClose(null));
        }
    }
    
    private void Close(object commandParameter)
    {
        if (SomeDataItem.HasChanges)
        {
            if (WindowManager.AskUserIfTheyWantToSave(SomeDataItem)) 
                DataProvider.Save(SomeDataItem);
        }
        HardDriveManager.SaveSettings();
        WindowManager.CloseMainWindow();
    }
    

    现在很明显,你没有我的 ...Manager 课程,但你如何保存这个和那个并不重要......这只是向你展示了什么是可能的。

    如果关闭应用程序还需要其他操作,例如关闭数据库连接,该怎么办?

    现代数据库技术实际上并不需要您在任何时候手动关闭数据库连接。

    在这一点上,我想明确一点,当使用 MVVM 时,在UserControls 甚至MainWindow 后面的代码中处理事件是完全可以接受的.所以像这样在MainWindow.xaml.cs中保存设置绝对没有问题:

    Loaded += MainWindow_Loaded;
    Closing += MainWindow_Closing;
    

    ...

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        Settings.Default.Reload();
        WindowStartupLocation = WindowStartupLocation.Manual;
        Left = Settings.Default.ApplicationLocation.X;
        Top = Settings.Default.ApplicationLocation.Y;
        Width = Settings.Default.ApplicationSize.Width;
        Height = Settings.Default.ApplicationSize.Height;
        WindowState = Settings.Default.IsApplicationMaximised ? WindowState.Maximized : WindowState.Normal;
    }
    
    private void MainWindow_Closing(object sender, EventArgs e)
    {
        Settings.Default.ApplicationLocation = new Point(Left, Top);
        Settings.Default.ApplicationSize = new Size(Width, Height);
        Settings.Default.IsApplicationMaximised = WindowState == WindowState.Maximized;
        Settings.Default.Save();
    }
    

    我是否在视图模型中声明事件并让命令触发事件?

    事件是 UI 对象,不应该在视图模型中看到......我们不希望在视图模型项目引用中出现任何与 UI 相关的 dll。当你真的需要处理一个事件时,你可以在后面的代码中处理它,记住你可以像这样从后面的视图代码中访问数据绑定视图模型:

    SomeViewModel viewModel = (SomeViewModel)DataContext;
    

    或者更好的方法是在附加属性中处理事件,您可以在 XAML 中简单地附加该事件。我建议您不要扩展这个相当长的答案来解释如何做到这一点,而是在线搜索,因为有很多例子。哦,实际上,我只记得我在 Stack Overflow 上的 What's the best way to pass event to ViewModel? 帖子中向某人解释了如何执行此操作。

    所以这比我预期的要长,但希望它回答了你的(一些)问题。

    【讨论】:

    • 感谢所有这些,请原谅我的多问题帖子。为清楚起见,我的数据库连接注释只是我为视图和模型无法直接控制的东西而随机想到的。此外,我对您发布的 MSDN 链接中的源代码链接损坏感到难过,但它仍然有一些很好的信息。关于您定义 CloseCommand 和 Close() 的示例,这是您在视图模型中定义的东西,还是在某处的管理器类中定义并注入到视图模型中的东西?
    • 是的,Commands 总是在视图模型中......希望你会看到使用这些基于delegates 的Commands 是多么容易。我还发现将所有功能逻辑保留在它所属的视图模型中要好得多,而不是在一堆单独的 RoutedCommand 实现中。
    • 仅供未来的访问者使用,这里有一个对我很有帮助的帖子:stackoverflow.com/a/3540895/764371。有明显不同的意见,但我认为这个问题设法更简洁地问了我想表达什么。
    【解决方案2】:

    我是否只是将 UI 元素连接到事件?

    没有。

    如果您的命令需要执行任何操作,只需在 Execute() 方法中调用这些操作即可。

    如何执行 ViewModel 与应用程序的其余部分(数据访问层、业务逻辑、服务)之间的交互完全取决于您。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-02
      • 2010-11-06
      • 2013-04-25
      • 1970-01-01
      • 1970-01-01
      • 2015-06-07
      相关资源
      最近更新 更多