Isaiah Nelson
Isaiah Nelson

Reputation: 2490

Breaking WPF views into components and maintaining databinding

I am working on a standard data-entry app in WPF/MVVM. I have a file called LabSetupView.xaml and in it I have three Controls:

  1. a ComboBox for displaying available Labs
  2. a ComboBox for displaying Technicians
  3. ListView for Tests

These controls are meant to work together - when a user selects a Technician, only the Tests for that TechnicianId are displayed. This is done via the navigation property on Technician to Tests.

I am considering moving these Controls into their own .xaml file each as a <UserControl> and then nesting them inside the <Window> because the DataContext for LabSetupView.xaml can only be set once. So, I thought if I componentize the controls, I could have ViewModels for each of them (i.e. LabView.xaml/LabViewModel.xaml etc...) and each inheriting from a single ViewModelBase that implements the necessary INotifyPropertyChanged.

My question is: Is it a common or normal practice to have a base-plate .xaml file that itself doesnt have a data context but rather contains separate UserControls, each of which with their own datacontext?

I am asking this because I am concerned that if I attempt something like this, then the data-binding and INotifyProperChanged wont work together because these Views are componentized. In other words, my Use Case above (getting Tests for a Techncian) won't bind or update property.

Upvotes: 0

Views: 857

Answers (1)

Brian S
Brian S

Reputation: 5785

You can certainly have your main view (base-plate .xaml file, as you describe it above) not have a DataContext and then assign the DataContext on specific child controls, whether they're UserControls or otherwise.

However, from what you describe, you may want to consider using Composition with your ViewModels. In this scenario, you'd have a MainViewModel which has properties representing the ChildViewModel (Technician & Test). These child ViewModels could communicate through the MainViewModel or not, depending on how you want to compose things. Then, the DataContext of the main view would be the MainViewModel, and the UserControls would just DataBind to the properties of that view model.

It might look something like this:

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

And an abbreviated example of the XAML would look like this (assuming MainViewModel has been set as the DataContext for the Window):

<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>

This is how I've addressed the type of scenario you have above and it works well. It works very well when you can use IoC/DI to inject the child view models through an IoC container.

EDITED

Based on your comment/question below, some additional detail. You ask how to establish the relationship between a selected technician and the tests available to that technician. One way to handle this would be to have the TestsViewModel be a child of the TechnicanViewModel. So, for example, you could have the following:

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; } }
}

And then in your XAML:

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

This will synchronize the Tests ListBox with the selected technician in the Technicians ListBox. This is just an example (written in a text editor, not VS if I've got any mistakes), but one way to handle the kind of relationship you're discussing.

Upvotes: 5

Related Questions