C# – Dynamically changing UserControl content at run time with WPF/MVVM

cuser-controlswpf

The screen I am trying to create is a multi-part results viewer. When your batch job completes, you will be able to double click on it and open this screen, which will contain a top section of basic data about the batch job that just ran (top 30% of screen, full width), then the lower 70% will consist of a left aligned listbox (20% of the width) with a selection of Sub-Results and a Detail pane taking up the remaining 80% of the width.

The way I want it to behave is when you select the Sub Result on the left listbox, the right hand pane will populate with the details of the sub result. Because it is going to be complex and needs to be scalable, I would like to implement each sub result detail display panel as a UserControl.

The parent ViewModel contains an IDictionary<Enum.ResultType, IResultPanel> – and the listbox will be populated from the keys of this dictionary, and when you select an option, it will fetch the IResultPanel object from the dictionary which will be User Control, one example snippet below

public partial class SimpleCalcInfoResult : UserControl, IResultPanel
    {
        private SimpleCalcInfoResultViewModel _viewModel;

        public SimpleCalcInfoResult(SimpleCalcInfoResultViewModel viewModel)
        {
            InitializeComponent();
            _viewModel = viewModel;
        }
    }

The IResultPanel interface is a blank empty interface, used only to facilitate being able to have the Dictionary above with a common type as I felt having a dictionary of UserControls was too broad.

The problem I've got is I can't figure out what XAML to use in the parent control to have a changeable UserControl panel. Obviously you can have

<local:MyControl> ... </local:MyControl>

As a hard coded user control, but how can I have a section of XAML that will let me change which User Control is displayed, depending on which ListBox item you select?

Best Answer

This is simple to achieve with WPF. However, when using MVVM, we 'manipulate' data rather than UI controls. With that in mind, first declare a DataTemplate for each of your custom Panel controls in the Application.Resources section:

<DataTemplate DataType="{x:Type ViewModels:SimpleCalcInfoResultViewModel}">
    <Views:SimpleCalcInfoResult />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
    <Views:MainView />
</DataTemplate>

Now all you have to do is display the relating view model in your lower right display using a ContentControl:

<ContentControl Content="{Binding ViewModel}" />

Finally, add a property of type IResultPanel named ViewModel to your parent view model:

private IResultPanel viewModel = new FirstViewModel();

public IResultPanel ViewModel
{
    get { return viewModel; }
    set { if (viewModel != value) { viewModel = value; NotifyPropertyChanged("ViewModel"); } }
}

Now all you need to do to display a different Panel in your application is to set this property to a different value:

ViewModel = new SimpleCalcInfoResultViewModel();
Related Topic