Using a base class with XAML custom user controls

Here is another thing which took some time to research, so I wanted to write it down.

In this case, I have a Windows Store 8.1 page, which has a GridView. I want to centralize the look/feel of the items in the GridView, so I created a custom control. I want to be able to set properties on that user control, which will affect various elements inside of the user control.

You can accomplish this by using regular, and dependency properties. For example, I create a user control which has properties like this:

/// <summary>
/// Gets or sets the height of the main item
/// </summary>
public Int32 ItemHeight
{
    get { return (Int32)GetValue(ItemHeightProperty); }
    set { SetValue(ItemHeightProperty, value); }
}

/// <summary>
/// Using a DependencyProperty as the backing store for ItemHeight.  This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty ItemHeightProperty =
    DependencyProperty.Register("ItemHeight", typeof(Int32), typeof(ConfigurableUserControl), new PropertyMetadata(0));

/// <summary>
/// Gets or sets the width of the main item.
/// </summary>
public Int32 ItemWidth
{
    get { return (Int32)GetValue(ItemWidthProperty); }
    set { SetValue(ItemWidthProperty, value); }
}

/// <summary>
/// Using a DependencyProperty as the backing store for ItemHeight.  This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty ItemWidthProperty =
    DependencyProperty.Register("ItemWidth", typeof(Int32), typeof(ConfigurableUserControl), new PropertyMetadata(0));

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Then, in that same user control, I can bind to these dependency properties like this:

<Grid HorizontalAlignment="Left" 
        Height="{Binding ItemHeight, ElementName=userControl}"
        Width="{Binding ItemWidth, ElementName=userControl}">

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

and finally, on the actual page where I’m using this user control, I can now pass in the height and width, which the control elements will bind to:

<GridView ItemsSource="{Binding Entities}" SelectionMode="Extended" >
    <GridView.ItemTemplate>
        <DataTemplate>
            <gridViewItems:LegalEntitiesGridViewItemTemplateControl 
                ItemHeight="100" ItemWidth="290" />
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

OK, so the next step of refactoring is that I really have a handful of common dependency properties which I’d want to re-use. SO, what if I put these in a common base class and have my user controls inherit from that common base class!

Well, when I modify the code-behind of the user control to point to a different base class, I get:

Partial declarations of ‘{namespace}.MyUserControl1′ must not specify different base classes

See, the user control is defined as “partial” and you can’t get to the automatic “other side” of that class. So, how DO you change the base class? I ran across this blog post – it applies to WPF, but it was easily adapted to Windows Store XAML:

Partial declarations of must not specify different base classes
http://mrpmorris.blogspot.com/2012/08/partial-declarations-of-must-not.html

Solution:
What I came up with is a base class like this:

/// <summary>
/// Abstract UserControl class to expose properties which can be set
/// by the consumer, and bound-to by the inheriting user control.
/// </summary>
[ContentProperty(Name = "Content")]
[MarshalingBehavior(MarshalingType.Agile)]
[Threading(ThreadingModel.Both)]
[Version(100794368)]
[WebHostHidden]
public abstract class ConfigurableUserControl : UserControl
{
    /// <summary>
    /// Creates a new instance of this type.
    /// </summary>
    protected ConfigurableUserControl()
        : base()
    {
        // Set default values.
        this.ItemHeight = 100;
        this.ItemWidth = 290;
    }

    /// <summary>
    /// Gets or sets the height of the main item
    /// </summary>
    public Int32 ItemHeight
    {
        get { return (Int32)GetValue(ItemHeightProperty); }
        set { SetValue(ItemHeightProperty, value); }
    }

    /// <summary>
    /// Using a DependencyProperty as the backing store for ItemHeight.  This enables animation, styling, binding, etc...
    /// </summary>
    public static readonly DependencyProperty ItemHeightProperty =
        DependencyProperty.Register("ItemHeight", typeof(Int32), typeof(ConfigurableUserControl), new PropertyMetadata(0));

    /// <summary>
    /// Gets or sets the width of the main item.
    /// </summary>
    public Int32 ItemWidth
    {
        get { return (Int32)GetValue(ItemWidthProperty); }
        set { SetValue(ItemWidthProperty, value); }
    }

    /// <summary>
    /// Using a DependencyProperty as the backing store for ItemHeight.  This enables animation, styling, binding, etc...
    /// </summary>
    public static readonly DependencyProperty ItemWidthProperty =
        DependencyProperty.Register("ItemWidth", typeof(Int32), typeof(ConfigurableUserControl), new PropertyMetadata(0));
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Now, for a user control who wants to inherit from this, I have it inherit it in code – like you’d expect:

public sealed partial class LegalEntitiesGridViewItemTemplateControl : ConfigurableUserControl
{
    public LegalEntitiesGridViewItemTemplateControl()
    {
        this.InitializeComponent();
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Then, in the XAML – this is where the magic happens. Instead of declaring the control with UserControl, declare it as the base type, then set the x:Class to the actual subclass. Huh? OK, instead of doing this:

<UserControl x:Name="userControl"
    x:Class="Division42.PropertyManager.Win8.UI.Controls.GridViewItems.LegalEntitiesGridViewItemTemplateControl"
    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:designer="using:Division42.PropertyManager.Models.Designer"
    mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="290"
    d:DataContext="{d:DesignInstance Type=designer:LegalEntitySample, IsDesignTimeCreatable=True}">

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Declare the XAML like this instead – note the “controls” prefix now:

<controls:ConfigurableUserControl x:Name="userControl"
    xmlns:controls="using:Division42.PropertyManager.Win8.UI.Controls"
    x:Class="Division42.PropertyManager.Win8.UI.Controls.GridViewItems.LegalEntitiesGridViewItemTemplateControl"
    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:designer="using:Division42.PropertyManager.Models.Designer"
    mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="290"
    d:DataContext="{d:DesignInstance Type=designer:LegalEntitySample, IsDesignTimeCreatable=True}">

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Now, you have designer support for the control and it works as expected.

Posted in Uncategorized, Visual Studio, Windows Store, XAML

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 5 other followers

%d bloggers like this: