将 WPF 视图分解为组件并维护数据绑定

Isa*_*son 0 c# data-binding mvvm wpf-controls

我正在 WPF/MVVM 中开发一个标准数据输入应用程序。我有一个名为 LabSetupView.xaml 的文件,其中有三个控件:

  1. 用于显示可用实验室的组合框
  2. 用于显示技术人员的组合框
  3. 用于测试的列表视图

这些控件旨在协同工作 - 当用户选择技术人员时,仅显示该技术人员 ID 的测试。这是通过技术人员到测试的导航属性完成的。

我正在考虑将这些控件分别作为 a 移动到它们自己的 .xaml 文件中<UserControl>,然后将它们嵌套在 中,<Window>因为 LabSetupView.xaml 的 DataContext 只能设置一次。因此,我想如果我将控件组件化,我可以为每个控件创建 ViewModel(即 LabView.xaml/LabViewModel.xaml 等...),并且每个控件都继承自实现必要的INotifyPropertyChanged.

我的问题是:拥有一个本身没有数据上下文但包含单独的 UserControls 的底板 .xaml 文件是否是一种常见或正常的做法,每个 UserControls 都有自己的数据上下文?

我问这个问题是因为我担心如果我尝试这样的事情,那么数据绑定和 INotifyProperChanged 将无法一起工作,因为这些视图是组件化的。换句话说,我上面的用例(为技术人员获取测试)不会绑定或更新属性。

Bri*_*n S 5

您当然可以让您的主视图(底板 .xaml 文件,如上面所描述的)没有 a DataContext,然后分配DataContext特定的子控件,无论它们是UserControls或否。

但是,根据您的描述,您可能需要考虑将 Composition 与 ViewModel 一起使用。在这种情况下,您将有一个 MainViewModel,它具有表示 ChildViewModel(技术人员和测试)的属性。这些子 ViewModel 可以通过 MainViewModel 进行通信,也可以不进行通信,具体取决于您想要如何组合事物。然后,DataContext主视图的 将会是 MainViewModel,而 将会UserControls只是DataBind该视图模型的属性。

它可能看起来像这样:

public class MainViewModel : BaseViewModel
{
    public TechnicianViewModel Technician { get { return _technician; } }
    public TestViewModel Test { get { return _test; } }
    ...
}
Run Code Online (Sandbox Code Playgroud)

XAML 的缩写示例如下所示(假设 MainViewModel 已设置为窗口的 DataContext):

<Window x:Class="MainView"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

      <StackPanel>
           <local:TechnicianView DataContext="{Binding Technician}"/>
           <local:TestView DataContext="{Binding Test}"/>
      </StackPanel>

</Window>
Run Code Online (Sandbox Code Playgroud)

这就是我解决上述场景类型的方法,并且效果很好。当您可以使用 IoC/DI 通过 IoC 容器注入子视图模型时,它的效果非常好。

编辑

根据您下面的评论/问题,一些额外的细节。您询问如何在选定的技术人员和该技术人员可用的测试之间建立关系。处理此问题的一种方法是让 TestsViewModel 成为 TechnicanViewModel 的子级。因此,例如,您可以有以下内容:

public class MainViewModel : BaseViewModel
{
    public IEnumerable<TechnicianViewModel> AvailableTechnicians { get { return _technicians; } }
    public TechnicianViewModel SelectedTechnician 
    { 
        get { return _selected; } 
        set 
        { 
            _selected = value; 
            RaiseNotifyPropertyChanged("SelectedTechnician");
        } 
    }
    ...
}

public class TechnicianViewModel : BaseViewModel
{
    public IEnumerable<TestViewModel> Tests { get { return _tests; } }
}
Run Code Online (Sandbox Code Playgroud)

然后在您的 XAML 中:

<StackPanel>
     <ListBox ItemsSource="{Binding AvailableTechnicians}" SelectedItem="{Binding SelectedTechnician, Mode=TwoWay}"/>
     <ListBox ItemsSource="{Binding SelectedTechnician.Tests}"/>
</StackPanel>
Run Code Online (Sandbox Code Playgroud)

这将使测试ListBox与技术人员中选定的技术人员同步ListBox。这只是一个示例(用文本编辑器编写,如果我有任何错误,则不是 VS),而是处理您正在讨论的关系的一种方法。