WPF / MVVM / ContentControl
Implementing an app that uses buttons to switch between UserControls: App Diagram
The approach assigns UserControl DataTemplates to the different view models and switching view models with the buttons. Problem is each UserControl is reinstantiated, which causes problems with a legacy Windows Forms control that's integrated. Is there a way to implement this approach that caches the UserControls? I suppose I could just load everything and change visibility as a fall back, but wondering if there was something I'm missing.
MainWindow.xaml
<Window x:Class="ContentControl.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:ContentControl.ViewModel"
xmlns:local="clr-namespace:ContentControl.View"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type vm:UC1ViewModel}">
<local:UC1UserControl/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:UC2ViewModel}">
<local:UC2UserControl/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button Command="{Binding UpdateContentCommand}" CommandParameter="uc1">UC 1</Button>
<Button Command="{Binding UpdateContentCommand}" CommandParameter="uc2">UC 2</Button>
</StackPanel>
<ContentControl Grid.Column="1" Content="{Binding SelectedViewModel}"/>
</Grid>
</Window>
UpdateContentCommand.cs
internal class UpdateContentCommand : ICommand
{
public event EventHandler? CanExecuteChanged;
private MainViewModel vm;
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
if (parameter is string p)
{
if (p.ToLower() == "uc1")
{
vm.SelectedViewModel = vm.UC1VM;
}
else if (p.ToLower() == "uc2")
{
vm.SelectedViewModel = vm.UC2VM;
}
}
}
public UpdateContentCommand(MainViewModel vm)
{
this.vm = vm;
}
}
You can define the control instance you wish to reuse as a resource. This way the XAML engine will only create a single shared instance. Only keep in mind that this instance can only be added to the visual tree in a single location. this means, you can't use this particular instance in the
DataTemplateand make it the child of e.g. aStackPanelat the same time. This also wouldn't work if multiple instances of theDataTemplateare required to instantiated (for example for items in aListBox).An alternative solution would be to create a control that manages the content host and generates/recycles the actual data containers (your controls) like the
ListBoxis doing it. This will give you more flexibility.