使用viewmodellocator时将模型传递给视图模型

时间:2013-11-14 16:05:42

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

我正试图掌握ViewModelLocator的概念(在MVVM Light中,虽然这个问题一般适用于ViewModelLocator的概念,关于使用哪个MVVM框架),但我很难弄清楚如何使用它。

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

这一切都很好,但是我很难理解你是如何使用视图应该呈现的模型数据来实际填充这些视图模型的。

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

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

现在,viewmodel需要一个员工列表,所以我可以创建一些返回所有员工的dataservice,并在ViewModelLocator的构造函数中使用Servicelocator注册:

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

所以,这将有效,如果我实例化EmployeesView,EmployeesViewModel将被实例化并注入一个返回所有员工的dataservice。

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

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

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

并且可能在ViewModelLocator的构造函数中注册某种dataservice:

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

但是,如何告诉dataservice或viewmodel他们应该向哪个员工提供详细信息?我在哪里通过员工ID?

我看这一切都错了吗?有人知道任何好的例子吗?我能找到的所有示例都只返回每个viewmodel的相同单个实例。

2 个答案:

答案 0 :(得分:0)

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

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

我希望这会有所帮助

答案 1 :(得分:0)

我发现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,它已被选中,您可以发送它。

如果是双击,我会使用列表框的名称来获取该项目。

从这里我将在viewmodel中做的事情是:

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属性设置为什么,都会显示该视图。

希望有所帮助:)