【问题标题】:Passing models to viewmodels when using a viewmodellocator使用 viewmodellocator 时将模型传递给 viewmodel
【发布时间】:2013-11-14 16:05:41
【问题描述】:

我正在尝试掌握 ViewModelLocator 的概念(在 MVVM Light 中,尽管问题通常适用于 ViewModelLocator 的概念,无论使用哪种 MVVM 框架),但我很难弄清楚如何使用它。

据我了解,您的视图使用定位器单例实例上的属性之一作为其数据上下文。定位器定义了这些不同的属性,并为每个属性返回正确的视图模型实例。

没关系,但我很难理解您是如何使用视图应该呈现的模型数据实际填充这些视图模型的。

例如,假设我有一个显示员工列表的视图。我可以创建一个EmployeesView 和一个EmployeesViewModel。在 ViewModelLocator 中,我可以创建一个返回此EmployeesViewModel 的属性:

public EmployeesViewModel Employees
{
    get
    {
        return ServiceLocator.Current.GetInstance<EmployeesViewModel>();
    }
}

现在,viewmodel 需要一个员工列表,所以我可以创建某种返回所有员工的数据服务,并将其注册到 ViewModelLocator 的构造函数中的 Servicelocator:

public ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    SimpleIoc.Default.Register<IDataService, AllEmployeesDataService>();
}

所以,如果我实例化EmployeesView,这将起作用,EmployeesViewModel 将被实例化并注入一个返回所有员工的数据服务。

但是,现在我想查看我刚刚在EmployeesView 中单击的某个员工的详细信息。该员工大概有某种 ID,可以通过该 ID 从数据库或其他任何地方检索他/她。

我可以创建一个 EmployeeDetailsView 和一个 EmployeeDetailsViewModel,并向 ViewModelLocator 添加一个属性:

public EmployeeDetailsViewModel EmployeeDetails
{
    return ServiceLocator.Current.GetInstance<EmployeeDetailsViewModel>();
}

也许在 ViewModelLocator 的构造函数中注册某种数据服务:

SimpleIoc.Default.Register<IDataService, EmployeeDetailsDataService>();

但是我如何告诉数据服务或视图模型他们应该为哪个员工提供详细信息?我在哪里传递员工 ID?

我看错了吗?有人知道任何好的例子吗?我能找到的所有示例都只返回每个视图模型的同一个实例。

【问题讨论】:

    标签: wpf mvvm inversion-of-control mvvm-light viewmodellocator


    【解决方案1】:

    我使用的一个简单示例是使用注入到视图模型的构造函数中的数据服务,就像您所做的那样。该数据服务返回我的对象​​(在您的情况下为员工)的可观察集合。我在同一视图中创建了一个列表框和详细信息网格。因此,我将列表框绑定到可观察的集合,我可以使用 collectionviewsource 对其进行样式设置和排序。对于详细信息,我创建了一个包含我想要显示的必填字段的网格。我在视图模型中为列表框的选定项 (SelectedEmployee) 创建了一个属性,并将它们绑定在一起。然后我将详细信息网格绑定到 SelectedEmployee。这将导致字段显示来自所选员工的值。

    现在您可以将它用于所有 CRUD 操作,您可以将列表框的 slecteditemchanged 事件绑定到中继命令,并根据需要添加您的业务逻辑。另一件需要注意的是,您可以将其拆分以支持异步操作。我有另一个实现,我获取列表框的选定项更改事件并执行异步获取函数来获取选定项。

    希望对你有帮助

    【讨论】:

    • 但是你只有一个视图/视图模型。如果您不能将列表框保留在屏幕上,例如因为您必须导航到 Windows 应用商店应用程序中的不同页面,该怎么办?或者,如果没有包含要绑定的选定项的列表框怎么办?
    • 嗯,那是应用程序设计问题。在其他页面上你需要什么,在员工数据服务中创建一个 selectedEmployee 属性。您可以从不同的视图模型中使用它,而无需将它们直接耦合在一起。当您使用 iNotifyPropertyChanged 浏览应用程序时,您必须确保更新 ui 中的值。
    【解决方案2】:

    我发现 J King 的回答非常好,但我希望提供更多的信息和选项。

    您在评论中询问如果视图不同会发生什么?
    这是我的一个实现:

    <ListBox x:Name="YourListView"  
              ItemsSource="{Binding SomeCollection}"
              SelectedItem="{Binding SelectedItemObject, 
                                UpdateSourceTrigger=PropertyChanged}"
             ToolTip="Double click to edit"
              >
        <ListBox.ContextMenu>
            <ContextMenu>
                <MenuItem Header ="Edit me" Command="{Binding Edit_Command}" 
                          CommandParameter="{Binding SelectedItemObject}"
                />
                <MenuItem Header ="Delete me" Command="{Binding Delete_Command}" 
                           CommandParameter="{Binding SelectedItemObject}"
                />
            </ContextMenu>
        </ListBox.ContextMenu>
    
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDoubleClick">
                <Command:EventToCommand Command="{Binding Edit_Command}" 
                                CommandParameter="{Binding ElementName=YourListView, 
                                                    Path=SelectedItem}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    
    </ListBox>
    

    请注意,双击该列表中的对象,或右键单击并选择两个选项之一(即editdelete),将调用带有参数的命令。

    现在,当您使用ContextMenu 时,因为您右键单击并反对,它被选中,您可以发送它。

    在双击的情况下,我使用列表框的名称来获取项目。

    从这里我将在视图模型中执行的操作类似于:

    private void Execute_Edit(object param)
    {
        var your_object = (cast_to_your_type)param;
        Messenger.Default.Send(new SwitchView(new SomeViewModel(_dataService,your_object)));
    }
    

    ICommand 将调用Execute_Edit,然后使用 Messenger 发送消息。

    这就是我定义SwitchView的方式:

    /// <summary>
    /// Used as message, to switch the view to a different one.
    /// </summary>
    public class SwitchView
    {
        public SwitchView(MyViewModelBase viewmodel)
        {
            ViewModel = viewmodel;
        }
    
        public MyViewModelBase ViewModel { get; set; }
    }
    

    我的 MainWindow 已注册以收听这些消息,并且我们知道要更改什么(显然,对于给定的视图模型:SomeViewModel)。主类上的SwitchView 会将 ViewModel 属性更改为消息传递的属性。

    这是我的主要观点:

    <Border >
        <ContentControl Content="{Binding Current_VM}" />
    </Border>
    

    因此,无论Current_VM 属性设置为什么,都会向您显示该视图。

    希望有所帮助:)

    【讨论】:

    • 这不是将视图模型紧密结合在一起,而视图模型定位器应该防止这种情况发生吗?在这种情况下,您的视图模型必须知道 SomeViewModel 才能将其发送到 SwitchView 消息。这很可能是一种有效的方法,但你那时还需要 VMLocator 吗?
    • 或者,等等,也许我很困惑。您将 contentcontrol 的 content 属性绑定到当前视图模型,但不应该是当前视图吗?我不确定我是否理解你在做什么。
    • 主视图主要是&lt;border&gt;&lt;content control&gt;&lt;/border&gt;。当我需要切换到一个视图时(我显然需要知道它是哪一个),我只是发送一条消息说“切换到这个”(其中 this 是 SomeViewModel)。他们的主视图模型收到该消息,并且有逻辑说好的,让我们将 Current_VM 属性更改为我们接下来需要使用的属性。接下来是 WPF & Locator 魔术。视图模型更改,这会触发视图的更改。
    猜你喜欢
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 2020-11-17
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多