Inheritance of UserControl WPF

logo-dotnetIn this article I will speak about how inherite behavior of user controls. I will show differents ways to do it.

 

First and wrong

You may want to try to do this:

  • -Define a base UserControl
  • -Define a new UserControl that inherite the base one:
<base:BaseExemple1 xmlns:base="clr-namespace:ArticleUserControl.Controls"
 x:Class="ArticleUserControl.Controls.InheriteExemple1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 mc:Ignorable="d" 
 d:DesignHeight="300" d:DesignWidth="300">
 <Grid>
 
 </Grid>
</base:BaseExemple1>

However this doesn’t work.

Error 2 'ArticleUserControl.Controls.BaseExemple1' cannot be the root of a XAML file because it was defined using XAML. Line 1 Position 20. C:\Users\formation\Desktop\Perso\Article UserControl\ArticleUserControl\ArticleUserControl\Controls\InheriteExemple1.xaml 1 20 ArticleUserControl

WPF doesn’t support inheritence between XMAL genereted. http://support2.microsoft.com/kb/957231

 

With code behind

A workaround for this is to use a code behind:

public class BaseUserControl:UserControl
 {
 public static RoutedCommand myButton = new RoutedCommand();
public BaseUserControl()
 {
 this.Loaded += BaseUserControl_Loaded;
 this.CommandBindings.Add(new CommandBinding(myButton, myButton_Executed));
 }
void BaseUserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
 {
 Console.WriteLine(string.Format("My user control {0}",(sender as FrameworkElement).ToString()));
 }
private void myButton_Executed(object sender, ExecutedRoutedEventArgs e)
 {
 MessageBox.Show("My button clicked!");
 }
 }

So in this base behavior we have a common Loaded event and a RoutedCommand.

In the control that inherite this class we will have this:

<base:BaseUserControl xmlns:base="clr-namespace:ArticleUserControl.Controls"
 x:Class="ArticleUserControl.Controls.BaseExemple1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 mc:Ignorable="d" 
 d:DesignHeight="300" d:DesignWidth="300">
 <Grid>
 <Button Content="My button" Command="{x:Static base:BaseUserControl.myButton}"/>
 </Grid>
</base:BaseUserControl>

 

Now to use this :

<Window x:Class="ArticleUserControl.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:base="clr-namespace:ArticleUserControl.Controls"
 Title="MainWindow" Height="400" Width="525">
 <Grid>
 <base:BaseExemple1 /> 
 </Grid>
</Window>

This works well, but you just get the behavior of the base UserControl, you are not getting a base User Interface.

 Inherite also the Design!

You may want to ineherite the base view of a User Control. To do this, we will take the BaseUserControl class created in the example above and bring some change. In fact the only modification is to add a static constructor:

static BaseUserControl()
 {
 DefaultStyleKeyProperty.OverrideMetadata(typeof(BaseUserControl),
 new FrameworkPropertyMetadata(typeof(BaseUserControl)));
 }

This line just tell which design to take for this class.

The design must be declare inside a « Generic.xaml » in other hand , it will not be found.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:local="clr-namespace:ArticleUserControl.Controls">
 <Style TargetType="{x:Type local:BaseUserControl}">
 <Setter Property="Template">
 <Setter.Value>
 <ControlTemplate TargetType="{x:Type local:BaseUserControl}">
 <Grid>
 <Grid.RowDefinitions>
 <RowDefinition Height="100"/>
 <RowDefinition Height="100*"/>
 </Grid.RowDefinitions>
 <StackPanel Grid.Row="0">
 <Button Content="Click Me!" Command="{x:Static local:BaseUserControl.myButton}"/>
 </StackPanel>
 <Grid Grid.Row="1">
 <ContentPresenter />
 </Grid>
 </Grid>
 </ControlTemplate>
 </Setter.Value>
 </Setter>
 </Style>
</ResourceDictionary>

This style defines a simple grid with 2 rows. The first row contains a simple button (like the previous example) and a ContentPresenter. It’s the ContentPresenter that will old the « child » control.

The child control doesn’t inherit of UserControl but of your base UserControl, in our case BaseUserControl.

<local:BaseUserControl x:Class="ArticleUserControl.Controls.ChildControl"
 xmlns:local="clr-namespace:ArticleUserControl.Controls"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 mc:Ignorable="d" 
 Height="300" Width="300"
 d:DesignHeight="300" d:DesignWidth="300">
 <Grid>
 <Rectangle Fill="Blue"/>
 <TextBlock Text="I'm your son"/>
 </Grid>
</local:BaseUserControl>

And then you just have to use you new control:

<Window x:Class="ArticleUserControl.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:base="clr-namespace:ArticleUserControl.Controls"
 Title="MainWindow" Height="400" Width="525">
 <Grid>
 <base:ChildControl /> 
 </Grid>
</Window>

And that’s all 🙂

2 commentaires

  1. Do you have any running example for this? I’m trying to apply it but I cannot make it running properly. I’ve got first to obviously define the resource into the user control to be able to apply the general style but then even if I’m able to get the control being applied into a main window, it remains empty and no controls are shown.

Laisser un commentaire