I think in your case you need to look in the direction of DataTemplate
, to make the dynamic content. I made a few examples that show this. The general meaning of this examples:
Given two buttons, one for the user and one for the administrator. If you choose the
User
, the content is displayed to the user, if theAdmin
, then for administrator.
Clearly, this is not the most realistic example, but it's just a way to show the dynamic selection of content. For you, you will determine the condition of replacement content.
Example A
This example demonstrates the dynamic replacing DataTemplates
, depending on the input values. If we think in terms of style patterns, it is very similar to the abstract factory, where instead of classes - DataTemplate
, and factory method is a dynamic DataTemplate
selector. Example is fully suitable for MVVM
pattern. Below is an example:
MainWindow.xaml
<Grid>
<ContentControl Name="MainView"
ContentTemplate="{StaticResource MainView}">
<ViewModels:MainViewModel />
</ContentControl>
</Grid>
MainView.xaml
It's a DataTemplate
in ResourceDictionary
. There are two templates: UserTemplate and AdminTemplate. One for the user and one for the administrator. In the style of ContentControl
defined ContentTemplateSelector, and a collection of templates that will be installed by the condition. In property Content
for DynamicContentControl is set content string that can be: User
or Admin
.
<DataTemplateSelectors:DynamicTemplateSelector x:Key="MyTemplateSelector" />
<DataTemplate x:Key="UserTemplate">
<StackPanel>
<TextBlock Text="Content for user"
FontSize="20"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<Button Content="Yes" />
<Button Content="No" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="AdminTemplate">
<StackPanel>
<TextBlock Text="Content for admin"
FontSize="20"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<TextBox Text="Whom banned?" />
<Button Content="Ban" />
<Button Content="AntiBan" />
</StackPanel>
</DataTemplate>
<Style x:Key="DynamicContentStyle" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplateSelector" Value="{StaticResource MyTemplateSelector}" />
<Setter Property="Width" Value="200" />
<Setter Property="Height" Value="200" />
<Setter Property="DataTemplateSelectors:DynamicTemplateSelector.Templates">
<Setter.Value>
<DataTemplateSelectors:TemplateCollection>
<DataTemplateSelectors:Template Value="User"
DataTemplate="{StaticResource UserTemplate}" />
<DataTemplateSelectors:Template Value="Admin"
DataTemplate="{StaticResource AdminTemplate}" />
</DataTemplateSelectors:TemplateCollection>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="MainView" DataType="{x:Type ViewModels:MainViewModel}">
<Grid>
<Button Name="UserButton"
Content="Are you user?"
Width="100"
Height="30"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Command="{Binding UserButtonCommand}" />
<Button Name="AdminButton"
Content="Are you admin?"
Width="100"
Height="30"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Command="{Binding AdminButtonCommand}" />
<ContentControl Name="DynamicContent"
Style="{StaticResource DynamicContentStyle}"
Content="{Binding Path=MainModel.ContentType,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
MainViewModel.cs
Here in commands set the content type.
public class MainViewModel
{
#region MainModel
private MainModel _mainModel = null;
public MainModel MainModel
{
get
{
return _mainModel;
}
set
{
_mainModel = value;
}
}
#endregion
#region UserButton Command
private ICommand _userButtonCommand = null;
public ICommand UserButtonCommand
{
get
{
if (_userButtonCommand == null)
{
_userButtonCommand = new RelayCommand(param => this.UserButton(), null);
}
return _userButtonCommand;
}
}
private void UserButton()
{
MainModel.ContentType = "User";
}
#endregion
#region AdminButton Command
private ICommand _adminButtonCommand = null;
public ICommand AdminButtonCommand
{
get
{
if (_adminButtonCommand == null)
{
_adminButtonCommand = new RelayCommand(param => this.AdminButton(), null);
}
return _adminButtonCommand;
}
}
private void AdminButton()
{
MainModel.ContentType = "Admin";
}
#endregion
public MainViewModel()
{
MainModel = new MainModel();
}
}
MainModel.cs
public class MainModel : NotificationObject
{
private string _contentType = "";
public string ContentType
{
get
{
return _contentType;
}
set
{
_contentType = value;
NotifyPropertyChanged("ContentType");
}
}
}
DynamicTemplateSelector
Taken and little reworked from CodeProject:
public class DynamicTemplateSelector : DataTemplateSelector
{
#region Templates Dependency Property
public static readonly DependencyProperty TemplatesProperty =
DependencyProperty.RegisterAttached("Templates", typeof(TemplateCollection), typeof(DataTemplateSelector),
new FrameworkPropertyMetadata(new TemplateCollection(), FrameworkPropertyMetadataOptions.Inherits));
public static TemplateCollection GetTemplates(UIElement element)
{
return (TemplateCollection)element.GetValue(TemplatesProperty);
}
public static void SetTemplates(UIElement element, TemplateCollection collection)
{
element.SetValue(TemplatesProperty, collection);
}
#endregion
#region SelectTemplate
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
string myStringItem = (string)item;
if (!(container is UIElement))
{
return base.SelectTemplate(item, container);
}
TemplateCollection templates = GetTemplates(container as UIElement);
if (templates == null || templates.Count == 0)
{
base.SelectTemplate(item, container);
}
foreach (var template in templates)
{
if (myStringItem.Equals(template.Value.ToString()))
{
return template.DataTemplate;
}
}
return base.SelectTemplate(item, container);
}
#endregion
}
#region TemplateCollection
public class TemplateCollection : List<Template>
{
}
#endregion
#region Template Dependency Object
public class Template : DependencyObject
{
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(Template));
public static readonly DependencyProperty DataTemplateProperty =
DependencyProperty.Register("DataTemplate", typeof(DataTemplate), typeof(Template));
public string Value
{ get { return (string)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }
public DataTemplate DataTemplate
{ get { return (DataTemplate)GetValue(DataTemplateProperty); } set { SetValue(DataTemplateProperty, value); } }
}
#endregion
Result for User
Result for Admin
Notes about this way
Advantages:
- full independence from different View
Disadvantages:
- need to create a separate data template (View), in some cases ViewModel, for each condition.
Conclusion:
This method is suitable for completely different views, if the representations are not much different, you see a second example.
Example B
This example uses a one DataTemplate
, the data is taken from the model, by default, all controls are hidden (Visibility.Collapsed
), all actions in View
are performed via DataTriggers
. Example is fully suitable for MVVM
pattern.
MainWindow.xaml
<Grid>
<ContentControl Name="MainView"
ContentTemplate="{StaticResource MainView}">
<ViewModels:MainViewModel />
</ContentControl>
</Grid>
MainView.xaml
<DataTemplate x:Key="MainView" DataType="{x:Type ViewModels:MainViewModel}">
<Grid>
<Button Name="UserButton"
Content="Are you user?"
Width="100"
Height="30"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Command="{Binding UserButtonCommand}" />
<Button Name="AdminButton"
Content="Are you admin?"
Width="100"
Height="30"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Command="{Binding AdminButtonCommand}" />
<StackPanel Name="MainViewPanel"
Tag="{Binding Path=MainModel.ContentType,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
<TextBlock Name="TitleTextBlock"
Text="{Binding Path=MainModel.TitleText,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
FontSize="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="Collapsed" />
<TextBox Name="BannedTextBlock"
Text="{Binding Path=MainModel.BannedName,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Visibility="Collapsed" />
<Button Name="YesButton"
Content="{Binding Path=MainModel.ContentYesButton,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Visibility="Collapsed" />
<Button Name="NoButton"
Content="{Binding Path=MainModel.ContentNoButton,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Visibility="Collapsed" />
</StackPanel>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=MainViewPanel, Path=Tag}" Value="User">
<Setter TargetName="TitleTextBlock" Property="Visibility" Value="Visible" />
<Setter TargetName="YesButton" Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=MainViewPanel, Path=Tag}" Value="Admin">
<Setter TargetName="TitleTextBlock" Property="Visibility" Value="Visible" />
<Setter TargetName="BannedTextBlock" Property="Visibility" Value="Visible" />
<Setter TargetName="YesButton" Property="Visibility" Value="Visible" />
<Setter TargetName="NoButton" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
MainViewModel.cs
public class MainViewModel
{
#region MainModel
private MainModel _mainModel = null;
public MainModel MainModel
{
get
{
return _mainModel;
}
set
{
_mainModel = value;
}
}
#endregion
#region UserButton Command
private ICommand _userButtonCommand = null;
public ICommand UserButtonCommand
{
get
{
if (_userButtonCommand == null)
{
_userButtonCommand = new RelayCommand(param => this.UserButton(), null);
}
return _userButtonCommand;
}
}
private void UserButton()
{
MainModel.ContentType = "User";
MainModel.TitleText = "Hello User!";
MainModel.ContentYesButton = "Yes";
MainModel.ContentNoButton = "No";
}
#endregion
#region AdminButton Command
private ICommand _adminButtonCommand = null;
public ICommand AdminButtonCommand
{
get
{
if (_adminButtonCommand == null)
{
_adminButtonCommand = new RelayCommand(param => this.AdminButton(), null);
}
return _adminButtonCommand;
}
}
private void AdminButton()
{
MainModel.ContentType = "Admin";
MainModel.TitleText = "Hello Admin!";
MainModel.BannedName = "John Doe";
MainModel.ContentYesButton = "Ban";
MainModel.ContentNoButton = "AntiBan";
}
#endregion
public MainViewModel()
{
MainModel = new MainModel();
}
}
MainModel.cs
public class MainModel : NotificationObject
{
#region ContentType
private string _contentType = "";
public string ContentType
{
get
{
return _contentType;
}
set
{
_contentType = value;
NotifyPropertyChanged("ContentType");
}
}
#endregion
#region TitleText
private string _titleText = "";
public string TitleText
{
get
{
return _titleText;
}
set
{
_titleText = value;
NotifyPropertyChanged("TitleText");
}
}
#endregion
#region BannedName
private string _bannedName = "";
public string BannedName
{
get
{
return _bannedName;
}
set
{
_bannedName = value;
NotifyPropertyChanged("BannedName");
}
}
#endregion
#region ContentYesButton
private string _contentYesButton = "";
public string ContentYesButton
{
get
{
return _contentYesButton;
}
set
{
_contentYesButton = value;
NotifyPropertyChanged("ContentYesButton");
}
}
#endregion
#region ContentNoButton
private string _contentNoButton = "";
public string ContentNoButton
{
get
{
return _contentNoButton;
}
set
{
_contentNoButton = value;
NotifyPropertyChanged("ContentNoButton");
}
}
#endregion
}
Result for User
Result for Admin
Notes about this way
Advantages:
- just one DataTemplate
Disadvantages:
- if the representations are very different from each other, in the
View
the need to manipulate a lot of controls and data.
Conclusion:
This method is suitable for Views
, which is not very different from each other, and the number of different values is not too large.
Both examples are available at this link.