Reputation: 1
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;
}
}
Upvotes: 0
Views: 166
Reputation:
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 DataTemplate
and make it the child of e.g. a StackPanel
at the same time. This also wouldn't work if multiple instances of the DataTemplate
are required to instantiated (for example for items in a ListBox
).
<Window.Resources>
<local:UC1UserControl x:Key="SharedUC1UserControl" />
<local:UC1UserControl x:Key="SharedUC2UserControl" />
<DataTemplate DataType="{x:Type vm:UC1ViewModel}">
<ContentControl Content={StaticResource SharedUC1UserControl}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:UC2ViewModel}">
<ContentControl Content={StaticResource SharedUC2UserControl}" />
</DataTemplate>
</Window.Resources>
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 ListBox
is doing it. This will give you more flexibility.
Upvotes: 0