MVVM:从另一个ViewModel执行命令

6

我已经苦恼了14天,因为有一个简单的任务:在数据库中,我定义了硬件类别。例如:

  1. HDD
    • 内置
    • 外置
    • 闪存

这个列表在数据库中是这样定义的:

    [ID - ParrentID - Name] : 1 - 0 - HDD, 2 - 1 - Internal, 3 - 1 - External, 4 - 1 - Flash.        

通过Entity Framework,我将这些行数据获取到我的应用程序中。从这个平面数据中,我创建了一个结构化对象,即我的DataModel。该模型的定义如下:

public class Category
{
   private int _id = -1;
   private string _name = "";
   private List<Category> _subCategories = null;
// property getters and setters, constructors, and bool HasSubCategories
}  

现在,我创建了一个名为SubCategoryViewModel的ViewModel,并将其绑定到我的TreeView。因此,我可以在TreeView中查看我的类别,并使用我定义和维护的层次结构。这很好用。在SubCategoryViewModel中定义了一个Attached Behavior for MouseDoubleClickCommand,也将其绑定到了TreeView。因此,当用户双击项目时,在SubViewCategoryModel中定义的方法将执行特定代码。 SubCategoryViewModel的列表嵌套在HWDocumentViewModel中,后者是窗口的主要ViewModel。
现在我需要的很明显:当用户在TreeView中双击项目时,我需要从数据库中加载项目并在ListView中显示它们。我认为,在HWDocumentViewModel中,我需要定义一个项目集合,并根据所选类别相应地加载它们到ListView中。但是,我不知道如何从SubCategoryViewModel执行HWDocumentViewModel上的方法。因为:TreeView绑定到SubCategoryViewModel项列表,因此发生DoubleClick时,会执行SubCategoryViewModel上的方法。我正在寻找一种方法,如何在主ViewModel(HWDocumentViewModel)上执行方法。
我尝试了这种方法:
我创建了一个属性:public static SubCategoryViewModel SelectedCategoryHWDocumentViewModel上。当发生双击时,我将此属性从SubCategoryViewModel设置为this。因此,在此属性中有一个对象,该对象执行了DoubleClick事件委托。太好了,现在我在HWDocumentView模型中拥有用户选择的对象。
因此,我需要将项目加载到ListView中。但是,我会从SubCategoryViewModel的方法中加载它们吗?我不这么认为。相反,我应该通过为它们创建一个ViewModel并将其绑定到ListView来从Main View Model中加载它们,对吧?但是,我如何从SubCategoryViewModel中调用HWDocumentViewModel中的方法?我应该写一个静态方法在HWDocumentViewModel上,以便从SubCategoryViewModel中访问它吗?
还是有一种方法可以从SubCategoryViewModel调用在HWDocumentViewModel上定义的Command吗? 或者总的来说,我是否采取了正确的方法来创建WPF中类似仓库的应用程序?
谢谢。
编辑:我的TreeView的XAML如下:
<TreeView x:Name="tvCategories" Background="White" ItemsSource="{Binding Categories}">
                    <TreeView.ItemContainerStyle>
                        <Style TargetType="{x:Type TreeViewItem}">
                            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
                            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                            <Setter Property="FontWeight" Value="Normal" />
                            <Setter Property="behaviors:MouseDoubleClick.Command"  Value="{Binding MouseDoubleClickCommand}" />
                            <Setter Property="behaviors:MouseDoubleClick.CommandParameter" Value="{Binding}" />
                            <Style.Triggers>
                                <Trigger Property="IsSelected" Value="True">
                                    <Setter Property="FontWeight" Value="Bold" />
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </TreeView.ItemContainerStyle>
                    <TreeView.Resources>
                        <HierarchicalDataTemplate DataType="{x:Type localvm:SubCategoryViewModel}" ItemsSource="{Binding Children}">
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding CategoryName}" />
                            </StackPanel>
                        </HierarchicalDataTemplate>
                    </TreeView.Resources>

                </TreeView>

你能添加你的TreeView XAML吗?你的问题让我对这个问题感到很困惑。 - Rik van den Berg
2个回答

6
我不确定我看到了问题。您有一个子类别树,当选择一个子类别时,适当的SubCategoryViewModel会将自己设置为主HWDocumentViewModel上的SelectedCategory。这似乎是一个合理的方法。
那么,为什么需要调用命令?为什么不能在HWDocumentViewModelSelectedCategory属性发生更改时(即在setter中)响应地加载新列表?
如果您真的必须使用命令来调用加载,则只需在每个SubCategoryViewModel中保留对主HWDocumentViewModel的引用,并使用简单的方式调用命令:
_mainViewModel.LoadCategoryCommand.Execute();

这就是我想要的解决方案。但我有点困惑——你说我采取的方法是合理的——即在主视图模型上具有静态属性,并由其他视图模型设置它。这是真正的MVVM方式吗? - sinkien
嗯,不是静态的。但是普通属性在我看来也可以。你可以使用事件来解耦视图模型,但对我来说似乎有点过度设计。 - GazTheDestroyer

3
使用MVVM模式,想要在View和ViewModel之间或者在不同的ViewModel之间进行通信,可以采用发布者/订阅者模式或类似于MVVMLight或Prism中使用的消息传递方式。我在MVVM Light的消息传递设置上发布了一个答案,这里是链接
在消息中,你可以发送一个对象来携带你想要在视图模型之间传递的任何数据。
我强烈推荐在使用MVVM时使用一个框架,这会使工作更加轻松。 MVVM框架比较 是一个包含一些主要框架比较的答案链接。

+1. 我看到了使用消息传递做的相当令人印象深刻的事情(比如动画等),所以性能应该很好。 - Vladislav Zorov
1
那么,您的意思是说我需要实现一个整个范例才能获得基本功能,例如从类别列表中选择类别项?还是说我需要利用整个框架来使其正常工作? - sinkien
消息传递和框架用于跨视图模型通信或从视图到视图模型的通信,而不是简单的绑定。在MVVM Light中,您可以只使用您想要使用的部分并实现它们,但是使用已经为您创建的消息范例比创建自己的发布者/订阅者模型更有意义。如果您不想要实现整个框架,那么您无需这样做,但是通过实现框架,您可能会节省大量时间。 - Erick
据我所知,框架是用于实现更复杂功能的一种方式。但在我看来,在我的情况下使用它就像用坦克打死一只蚂蚁一样;)我将仔细研究MVVM Light框架,它似乎非常强大 - 感谢您的建议。 - sinkien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接