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

时间:2013-02-22 16:04:00

标签: c# data-binding mvvm wpf-controls

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

  1. 用于显示可用实验室的ComboBox
  2. 用于展示技术人员的ComboBox
  3. ListView for Tests
  4. 这些控件旨在协同工作 - 当用户选择技术人员时,仅显示该TechnicianId的测试。这是通过技术人员到测试的导航属性完成的。

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

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

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

1 个答案:

答案 0 :(得分:3)

你当然可以拥有你的主视图(基板.xaml文件,如上所述)没有DataContext,然后在特定的子控件上分配DataContext,无论它们是不是UserControls或其他。

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

它可能看起来像这样:

public class MainViewModel : BaseViewModel
{
    public TechnicianViewModel Technician { get { return _technician; } }
    public TestViewModel Test { get { return _test; } }
    ...
}

XAML的缩写示例如下(假设MainViewModel已设置为Window的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>

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

<强> EDITED

根据您的评论/问题,一些额外的细节。您询问如何建立选定技术人员与该技术人员可用测试之间的关系。处理此问题的一种方法是让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; } }
}

然后在你的XAML中:

<StackPanel>
     <ListBox ItemsSource="{Binding AvailableTechnicians}" SelectedItem="{Binding SelectedTechnician, Mode=TwoWay}"/>
     <ListBox ItemsSource="{Binding SelectedTechnician.Tests}"/>
</StackPanel>

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