代码之家  ›  专栏  ›  技术社区  ›  JP Richardson

MVVM疯狂:命令

  •  62
  • JP Richardson  · 技术社区  · 15 年前

    我喜欢MVVM。我不喜欢,但喜欢。大部分都是有道理的。但是,我一直在阅读鼓励您编写大量代码的文章,这样您就可以编写XAML,而不必在代码后面编写任何代码。

    让我举个例子。

    最近,我想在视图模型中将一个命令连接到一个listview mousedoubleclickevent。我不太确定该怎么做。幸运的是,谷歌对一切都有答案。我发现了以下文章:

    虽然这些解决方案有助于我理解命令,但也存在一些问题。前面提到的一些解决方案使WPF设计器不可用,因为在依赖属性后面附加“internal”是一种常见的攻击;WPF设计器找不到它,但CLR可以。有些解决方案不允许同一控件使用多个命令。有些解决方案不允许使用参数。

    经过几个小时的试验,我决定这样做:

    private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
        ListView lv = sender as ListView;
        MyViewModel vm = this.DataContext as MyViewModel;
    
        vm.DoSomethingCommand.Execute(lv.SelectedItem);
    }
    

    所以,MVVM纯化论者,请告诉我这是怎么回事?我仍然可以单元测试我的命令。这似乎很实际,但似乎违背了“佐毫克…你的代码背后有代码!!!!!“请分享您的想法。

    事先谢谢。

    7 回复  |  直到 12 年前
        1
  •  37
  •   Greg D    15 年前

    我认为错误在于纯度要求。设计模式,包括MVVM,是工具箱中的一个工具,而不是它们自身的一个终结。如果打破一个经过充分考虑的案例的模型的纯粹性更有意义(而且很明显你已经考虑过这个案例),那么就打破模型。

    如果这对你有用,而且你不认为这是不适当的维护负担,那么我会说你所做的一切都没有错。我认为,尽管纯MVVM实现可能是什么,但您显然已经遇到了证明这是解决问题的合理解决方案的举证责任。

    (我认为这个论点类似于多半径语言的论点。虽然可以应用纯OO方法,但有时以更实用的方式进行操作更为合适。虽然可以应用纯功能方法,但有时权衡表明OO技术的价值远远大于此。)

        2
  •  13
  •   Heinzi    15 年前

    我同意你的观点,许多MVVM命令解决方案都太复杂了。就个人而言,我使用混合方法,并使用来自ViewModel的方法和属性,在视图中而不是在ViewModel中定义我的命令。

    XAML:

    <Window.Resources>
        <RoutedCommand x:Key="LookupAddressCommand" />
    </Window.Resources>
    <Window.CommandBindings>
        <CommandBinding Command="{StaticResource LookupAddressCommand}" x:Name="cmdLookupAddress" />
    </Window.CommandBindings>
    

    代码(视图):

    Private Sub cmdLookupAddress_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs) Handles cmdLookupAddress.CanExecute
        e.CanExecute = myViewModel.SomeProperty OrElse (myViewModel.SomeOtherProperty = 2)
    End Sub
    
    Private Sub cmdLookupAddress_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs) Handles cmdLookupAddress.Executed
        myViewModel.LookupAddress()
    End Sub
    

    它不是纯的MVVM,但它很简单,可以工作,不需要特殊的MVVM命令类,而且它使非MVVM专家(我的同事)更容易阅读您的代码。

        3
  •  10
  •   Thomas Levesque    15 年前

    虽然我不喜欢在使用MVVM模式时编写代码,但我认为只要代码纯粹与UI相关,就可以编写代码。

    但这里并不是这样的:您从代码后面调用了一个视图模型命令,因此它不完全与用户界面相关,并且视图和视图模型命令之间的关系在XAML中并不明显。

    我认为你可以很容易地在XAML中使用 attached command behavior . 这样你就可以把 MouseDoubleClick 视图模型命令的事件:

    <ListView ItemSource="{Binding Items}">
       <local:CommandBehaviorCollection.Behaviors>
          <local:BehaviorBinding Event="MouseDoubleClick" Action="{Binding DoSomething}" />
       </local:CommandBehaviorCollection.Behaviors>
    
        ...
    </ListView>
    

    您还可以轻松访问 ListView 不直接参考,使用 ICollectionView 接口:

    private ICommand _doSomething;
    
    public ICommand DoSomething
    {
        get
        {
            if (_doSomething == null)
            {
                _doSomething = new DelegateCommand(
                    () =>
                    {
                        ICollectionView view = CollectionViewSource.GetDefaultView(Items);
                        object selected = view.CurrentItem;
                        DoSomethingWithItem(selected);
                    });
            }
            return _doSomething;
        }
    }
    
        4
  •  5
  •   Reed Copsey    15 年前

    我相信“代码背后没有代码”的目标正是这样,一个要达到的目标——而不是你应该把它当作绝对教条的东西。视图中有适当的代码位置——这不一定是一个坏例子,说明代码在何处或如何比其他方法更简单。

    您列出的其他方法(包括附加属性或附加事件)的优点是它们是可重用的。当您直接挂接一个事件,然后执行您所做的操作时,很容易在整个应用程序中复制该代码。通过创建一个附加属性或事件来处理该连接,您可以在管道中添加一些额外的代码,但对于任何需要双击处理的ListView,该代码都是可重用的。

    尽管如此,我倾向于使用更“纯粹”的方法。将所有的事件处理保持在视图之外可能不会影响测试场景(您特别提到的场景),但它确实会影响总体的可设计性和可维护性。通过在代码背后引入代码,您将限制您的视图始终使用有线事件处理程序的ListView,这确实将您的视图与代码绑定,并限制了设计者重新设计视图的灵活性。

        5
  •  2
  •   dthrasher    15 年前

    @jp在原始问题和@heinzi在回答中提到的是一种处理困难命令的实用方法。当您需要在调用命令之前做一点UI工作时,在代码隐藏中使用一点点事件处理代码尤其方便。

    考虑OpenFileDialog的经典案例。在按钮上使用click事件、显示对话框,然后将结果发送到ViewModel上的命令,比采用MVVM工具包使用的任何复杂消息传递例程都要容易得多。

    在你的XAML中:

    <Button DockPanel.Dock="Left" Click="AttachFilesClicked">Attach files</Button>
    

    在您的代码背后:

        private void AttachFilesClicked(object sender, System.Windows.RoutedEventArgs e)
        {
            // Configure open file dialog box
            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.FileName = "Document"; // Default file name
            dlg.DefaultExt = ".txt"; // Default file extension
            dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension
    
            // Show open file dialog box
            bool? result = dlg.ShowDialog();
    
            // Process open file dialog box results
            if (result == true)
            {
                string filename = dlg.FileName;
    
                // Invoke the command.
                MyViewModel myViewModel = (MyViewModel)DataContext;
                if (myViewModel .AttachFilesCommand.CanExecute(filename))
                {
                    noteViewModel.AttachFilesCommand.Execute(filename);  
                }
            }
        }
    

    计算机程序设计缺乏灵活性。我们的程序员必须灵活处理这一问题。

        6
  •  2
  •   D J    12 年前

    解耦是MVVM的主要特性之一。如果您想更改视图或绑定模型。你的应用程序有多简单?

    举个例子,其中view1和view2共享同一个viewModel。现在,您将为这两种方法实现代码隐藏方法。

    另外,假设您需要在后期更改视图的ViewModel,那么当视图模型和语句发生更改时,您的命令将失败。

    MyViewModel vm = this.DataContext as MyViewModel;
    

    将返回空值,从而导致代码崩溃。因此,更改后面的代码也会带来额外的负担。如果你这样做,就会出现这种情况。

    当然,在编程中有很多方法可以实现相同的事情,但是哪一种方法是最好的将导致最好的方法。

        7
  •  1
  •   Pierreten    15 年前

    指挥是为了笨蛋。真正的男人将他们的整个用户界面连接到codebehind中的事件。