Why Custom Controls are underrated

by Morten 6. February 2012 22:48

I’m a big fan of building controls. I love writing them, designing them, trying to make it work in as many scenarios I can while keeping them simple, extensible and most importantly reusable. In fact for the past 6 years, it’s all I’ve been doing full time (first ASP.NET and later XAML), and frequently in my spare time as well.

If you dabble in XAML, you have most likely already been building some controls, by going “File -> New -> User Control” in Visual Studio. You probably do this because you want to create a new page in your app, or you just want to encapsulate some of the UI in a separate section. Or perhaps it’s because you realize that this little tidbit can be used over and over again in your application. Or maybe you have even considered it can be used again and again across many DIFFERENT applications. If you have tried those two last categories (or if you will one day), this blog post is for you! It will apply to any of the XAML techs there is: WPF, Silverlight, Windows Phone and the future Windows 8 Runtime.

Despite the title in this blog post, User Controls are awesome. They are quick to throw together and reuse over and over - and there’s a lot of value in that. But what if I told you there’s another control type that has even MORE power, better performance, and can be way more flexible and reusable than a user control, and where the clean code will make most developers fall in love?

The thing is you already know this because you’ve been using them all the time: Button, ListBox, ItemsControls, Grid, StackPanel etc. are all controls harnessing the same power you can! And you have probably seen XAML styles that completely changed the look and feel of a control, without touching any of its code. To give you an idea of how powerful this is, look at this Silverlight Sample below. On the left you will see a ListBox binding to a list of planets. You have probably already done something like this. On the right, you see a solar system. But in fact this is ALSO a ListBox. And there is NO extra code involved here. It’s done entirely by restyling the template. Notice how selection and up/down keys work just like it does with the “normal” ListBox. So I got to reuse all the code that has this, and all I had to do was restyle the ListBox a bit. Something I could have done entirely in Blend without ever touching code.

Let me repeat that: I didn’t add any code to the ListBox do this. In fact the code behind for this page is completely empty. If you don’t believe me, here’s the source code. You can also see more about this technique in this presentation from Mix’08, or read David Ansons blogpost on it.

So at this point hopefully I have won you over to learning more about Custom Controls (if not I’m amazed you have read this far :-).

The Anatomy of A User Control

To start, let’s first look at the anatomy of a typical UserControl and try and fully understand how that works first. Below here we have the XAML portion of our control that defines the layout. We’ll keep it simple and have a Grid with a Button inside it:

<UserControl x:Class="MyApp.SilverlightControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Click Me" Click="Button_Click" Opacity=".5" />
    </Grid>
</UserControl>

And we have the code-behind that loads up the control, handles user interaction etc.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace SolarSystemRetemplate
{
    public partial class SilverlightControl1 : UserControl
    {
        public SilverlightControl1()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            LayoutRoot.Background = new SolidColorBrush(Colors.Red);
        }
    }
}

The things worth noting here are two things: “LayoutRoot” is defined in the XAML using x:Name, and we automatically get a variable by that name in code behind. Also the event handler hooked to the Button’s Click event is magically linked to the code-behind. All this is really handled by the compiler and the “InitializeComponent” call - a method that interestingly doesn’t exist here. The reason this works is really because this is a partial class as indicated, and Visual Studio creates a little ‘secret’ file under the covers for you. You can get to if if you right-click the method and select “Go To Definition”. Here’s what the contents of that file looks like:

namespace MyApp {    
    
    public partial class SilverlightControl1 : System.Windows.Controls.UserControl {
        
        internal System.Windows.Controls.Grid LayoutRoot;
        
        private bool _contentLoaded;
        
        /// <summary>
        /// InitializeComponent
        /// </summary>
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public void InitializeComponent() {
            if (_contentLoaded)
                return;
            _contentLoaded = true;
            System.Windows.Application.LoadComponent(this, 
new System.Uri("/MyApp;component/SilverlightControl1.xaml",
System.UriKind.Relative)); this.LayoutRoot = ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot"))); } } }

You’ll notice that the LayoutRoot is defined here as internal, and it’s assigned using the “FindName” method.

This is one of the nice things about UserControls: A lot of the work is automatically done for you, but with Custom Controls you will have to do this yourself! (but this isn’t so bad considering the power you get!). And here’s the kicker: A UserControl is just another custom control!

The Anatomy of A Custom Control

A custom control doesn’t have a XAML and a code-behind component in the same way UserControl does. Instead it’s ALL code along with a default XAML template. You can consider the template the equivalent of the XAML in the User Control, but the important part to remember here is that this template can be changed by ANYONE, which is what I did to the ListBox in the solar system sample. Another thing to note is that since the template doesn’t have a corresponding code-behind where Visual Studio generates a partial class for you, any event handlers cannot be defined in the template. So how do we go about recreating the user control above as a custom control?

For Silverlight this is easy. Right-click your project and select “File -> Add New -> Silverlight Templated Control”. WPF and Windows Phone doesn’t come with this template so you’ll have to do it manually there, by creating a class and a generic template file. After you do this, you’ll notice two new files: First a simple C# class, and second a new file in \Themes\Generic.xaml. The second file is where you place all templates for all your controls in that assembly. It HAS to have this name and live in this folder for the custom control to pick up the template.

Below is what this template looks like. I’ve added the grid and the button inside the suggested border that was created for me.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyApp">

    <Style TargetType="local:TemplatedControl1">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:TemplatedControl1">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid x:Name="LayoutRoot">
                            <Button x:Name="ClickButton" Content="Click me!" Opacity=".5" />
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

First notice the TemplateBinding statements on the border. This is an important feature of controls. You can bind straight to a dependency property defined in your control code. Since a custom control inherits from “Control”, you automatically get Background, BorderBrush, BorderThickness and many other general properties from the inheritance. The great thing is that you can just write <my:TemplatedControl Border=”Red” /> and the border will automatically be bound into this template (and anywhere else where you have a TemplateBinding to that property). This beats UserControl, where to accomplish this in Silverlight you will have resolve to a hack by setting the DataContext of the control to itself, breaking the DataContext flow.

Second, notice that I didn’t add a click-handler to Button. If I did, this template would fail to load. We’ll hook the click handler up later.

Next let’s look at the code for the control:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace MyApp
{
    [TemplatePart(Name="LayoutRoot", Type=typeof(Control))]
    [TemplatePart(Name = "ClickButton", Type = typeof(ButtonBase))]
    public class TemplatedControl1 : Control
    {
        Control layoutRoot;
        ButtonBase button;
        public TemplatedControl1()
        {
            this.DefaultStyleKey = typeof(TemplatedControl1);
        }
        public override void OnApplyTemplate()
        {
            if (button != null) //unhook from previous template part
            {
                button.Click -= new RoutedEventHandler(button_Click);
            }    
            button = GetTemplateChild("ClickButton") as ButtonBase;
            if (button != null)
            {
                button.Click += new RoutedEventHandler(button_Click);
            }
            layoutRoot = GetTemplateChild("LayoutRoot") as Panel;
            base.OnApplyTemplate();
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            layoutRoot.Background = new SolidColorBrush(Colors.Red);
        }
    }
}

First I declare the “TemplatePart” attributes on the control. They tell what parts (ie controls) are expected to be in my template. In this case LayoutRoot of type Panel (Grid is a Control), and ClickButton of type ButtonBase. These are not strictly required, but they help Expression Blend understand the template requirements when you later customize the control. I always declare the lowest needed type in the control hierarchy to make the template more flexible. For instance I use ButtonBase and not Button, because I only rely on the Click event which is declared on the ButtonBase base class. That way I don’t lock a user of the control into using “Button” but they can place ANY control that inherits from ButtonBase here. Same thing applies for the LayoutRoot, where I just need the Background property.

Next the control inherits from “Control”. Custom controls must inherit from this.

In the constructor I define the “DefaultStyleKey”. This tells the framework that I have a default template defined in Themes\Generic.xaml. If I didn’t the user would always have to explicitly defined a control template for the control.

Lastly, the most important part is “OnApplyTemplate”. This method is called when the control has loaded the template. This is our earliest opportunity to grab references to controls in the template, ie. the TemplateParts. In this case I grab a reference to the ButtonBase defined in the template. If it’s found, I’ll add a click handler to it. Also if a new template gets applied, I must remember to unhook from the previous instance (this is a rare scenario though, and you could probably get away with skipping that bit). It’s also important to note that Template Parts are always optional! So always do the null check anywhere you rely on a reference to a template part.

And that’s really it! I kept the sample simple, so it is easier to go through the individual parts of a control, therefore the differences between a custom control and a user control doesn’t really stand out. If this was all you needed to do, a custom control is probably overkill. But think of scenarios where you have a lot of code-behind that you want to reuse, but you don’t want to lock the design in. The major next parts you will want to add to this now is more Dependency Properties you can bind into the template, as well as VisualStates - ie. storyboards that triggers on certain events. The great thing about Visual States is that the code-behind doesn’t define the storyboard or what it does - only when it starts. This gives the user even more flexibility to customize the behavior.

Adding Visual States to the control

Let’s add some mouse over states to our control, and have the control animate when that happens. In the code-behind where we defined the TemplateParts let’s add two TemplateVisualState attributes:

[TemplateVisualState(GroupName = "HoverStates", Name = "MouseOver")]
[TemplateVisualState(GroupName = "HoverStates", Name = "Normal")]

Again these are optional, but great for Blend integration.

Next add the code that triggers the visual state to the control:

bool isMouseOver;
protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
{
    isMouseOver = true;
    ChangeVisualState(true);
    base.OnMouseEnter(e);
}
protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
{
    isMouseOver = false;
    ChangeVisualState(true);
    base.OnMouseLeave(e);
}

private void ChangeVisualState(bool useTransitions)
{
    if (isMouseOver)
    {
        GoToState(useTransitions, "MouseOver");
    }
    else
    {
        GoToState(useTransitions, "Normal");
    }
}

private bool GoToState(bool useTransitions, string stateName)
{
    return VisualStateManager.GoToState(this, stateName, useTransitions);
}

This is really all the code we need. It’s pretty simple. If the mouse is over, trigger the MouseOver state, else trigger the Normal state. Note how we don’t really define what “MouseOver” looks like. That’s the job of the template. Let’s define that (you might already be very familiar with this when overriding templates - it’s exactly the same thing, except we get to define the default state):

<ControlTemplate TargetType="local:TemplatedControl1">
    <Border Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="HoverStates">
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation
                            Storyboard.TargetName="BackgroundElement"
                            Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
                            To="Yellow" Duration="0:0:.5" />
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Normal">
                    <Storyboard>
                        <ColorAnimation
                            Storyboard.TargetName="BackgroundElement"
                            Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
                            To="Transparent" Duration="0:0:.5" />
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Grid x:Name="LayoutRoot">
            <Rectangle x:Name="BackgroundElement" Fill="Transparent" />
            <Button x:Name="ClickButton" 
                    Content="Click me!" Opacity=".5" />
        </Grid>
    </Border>
</ControlTemplate>

So the changes here is adding a rectangle in the background that we animate into yellow when the mouse hovers over.

You now have a control that sets a background on some Panel when some ButtonBase is clicked, as well as running an animation on MouseEnter/Leave. This could serve the purpose for quite a lot of controls, without you having to rewrite the code!

Here’s a few resources you will want to read if you want to learn more about this:

A couple of other controls I’ve built over the time and described on this blog:

If you want to go even more hardcore, wrap your head around the ArrangeOverride and MeasureOverride methods. This is where you can get some really amazing control over how the contents are laid out, but this is outside the scope of this article, but I urge you to read into it. Here’s one article to get your started on that: http://www.switchonthecode.com/tutorials/wpf-tutorial-creating-a-custom-panel-control

Tags:

XAML | WPF | Windows Runtime | Silverlight | Windows Phone

Comments (7) -

2/7/2012 11:35:14 AM

Filip &#39;xyzzer&#39; Skakun

I like the ability of custom controls to switch their templates and I have used them a little bit, but in some 95% of the cases - building a UserControl is a better idea. It takes a lot less time and while it seems like a poor following of the DRY principle - if you want to reuse your code and restyle the UI - it is easier to create a copy of a UserControl and change some bits than designing a reusable custom control and predicting all the things people might change in the template and misuse your code. I am saying all this out of a bit of frustration because sometimes I set out on a venture to create a custom control to share with a community and by focusing on trying to figure out which pieces of the control might be customized and how - I end up running out of time in which I could release a perfectly useful UserControl.

Filip 'xyzzer' Skakun United States

2/8/2012 7:28:07 PM

ct

Thanks for the article.  Learned about the DefaultStyleKey and relation to Generic.xaml which is cool, and also how to setup visual states.  Have an idea for a custom control to build based on this.

ct United States

2/15/2012 5:40:12 AM

Stas

Hi, Morten

It seems to me, that line
button.Click -= new RoutedEventHandler(button_Click);
will never work as expected.

Stas Belarus

2/15/2012 7:52:05 AM

Morten

Stas: And why is that?

Morten United States

2/15/2012 12:49:41 PM

Stas

I just double checked my opinion and realized that I was wrong for a long time regarding delegates. I thought that:
var a = new SomeDelegate(MethodName);  and
var b = new SomeDelegate(MedhodName);
are NOT equal... In that case button.Click -= new RoutedEventHandler(button_Click); would  try to unsubscribe a completely new method pointer. But again, I was wrong.

Sorry for this Smile

Stas Belarus

4/14/2012 1:00:14 AM

red bottom shoes

Miuccia Prada’s clever blend of sophistication and whimsy has made Miu Miu an outstanding fashion brand since it launched in 1992, utterly modern on-trend purse design with a wide series of feminine charming colors make Miu Miu bags a modern simple yet amazing classic to lend every look a dose of lady signature glamour! With a special wide popularity, Miu Miu purses seems to be a hottest craft initial in Miu Miu outlet! Get the chance to win cheap Miu Miu sale online now and own the season trendiest Miu Miu bags with discount prices and free home delivery! http://www.miumiu-outlets.com/
Every trendy lady wants to wear her pretty feet a pair of just gorgeous, noble and high-end fashion Christian Louboutin shoes as they are sexy, seductive, fascinating and fun. Worked by craftsmen and finished by hand to result a perfectly carful and elaborate shoes selection, its professional design brings your feet the comfort feel and also feminine, trendy and beautiful look! Whether you want a stunning lovely style or a slender stylish fit, Louboutin Shoes will never come to disappoint you! The designs of Christian Louboutin outlet shoes are very unique and perfect that always come to bring ladies with great surprise and happiness, once you see them, you’ll get to love them! Yet, when it comes to choosing a place to shop Louboutin Shoes online, you can never miss to check Christian Louboutin sale store online, where a vast varieties of Louboutin Shoes are provided, including Christian Louboutin Boots, pumps, wedges, slingbacks, peep toes, sandals, flats and sneakers. What’s more, all shoes on new season promotion sale now only need wholesale price with free shipping offered to world-wide!  http://www.redbottomshoes-style.com
Naming the brand after a sport embodied a world of classic style Polo Ralph Lauren was founded in 1967 and has since grown into a complete lifestyle brand defining classic American style! World-wide popular with its famous essential Ralph Lauren Polo Shirts, it’s now a brilliant signature preppy menswear. Available in a wide series of color options, classic designs and offering both quality construction and wide varieties, Ralph Lauren is loved and appreciated with longevity, timelessness and style! Ralph Lauren Outlet online store make this luxurious leisure collection of Polo Ralph Lauren men’s sweaters, long/short sleeve polo shirts, hoodies, jackets and sweatshirts available with very cheap price, up to 65% off, free shipping! http://www.poloralphlauren4sale.com/

red bottom shoes People's Republic of China

4/26/2012 1:10:04 AM

dr dre beats

DRE's affordable to make an make an effort to do to possess the ability to permit the listener to listen to how the music performers and producers to think it could be heard. And commit a week, the studio stated to me, What could be the goal concerning the <a href="australia-beatsbydre.org/...nes-p-39.html">; beats dr dre detox </a> television set may be effectively created. undoubtedly is undoubtedly a obvious voice, could make any listener beat, grooving riffs. Their truth, like a definitely distinctive actual appearance for that newest movement image from MTV to eradicate stuff can be considered a draw. January 25, 2011, for other amazing suggestions DRE wholesale affordable beat some additional Boses concerning the future, <a href="www.drdrebeatsaustralias.org/...2.html">Dr Dre Beats Studio</a>, 13,201 monster beat cult.Cheap DRE beat solo artist Dr. the DRE newest music craze. We have utilized the newest headset check and is also ready to provide you with just one of those amazing products,beats by dre we think it is amazing we think we are able to improve. The <a href="www.beatsbydrdre-france.net/casque-monster-beats-by-drdre-studio-& by dre studio noir</a> is undoubtedly a producer well-known rap / hip-hop performers of truth, of DRE and Lovine wheat, Interscope documents chairman who beat the cheap. The producer presently has 6 about a comparable producer of headset products, the purchase of affordable beat, 6 types of distinctive <a href="cheapbeatsbydrdreuk.org/...-18.html">Cheap Dr.Dre Beats Solo HD Red Diamond Headphones - Red</a>.



dr dre beats People's Republic of China

4/26/2012 2:35:51 AM

Wedding Abayas

Very interesting,
www.aabuk.com

Wedding Abayas United Kingdom

4/26/2012 2:36:12 AM

Wedding Abayas

Very interesting,
www.aabuk.com

Wedding Abayas United Kingdom

5/4/2012 4:18:25 AM

Erotic Teens Tits | Tiny Tits

Welcome to kettysmalltits.com - collection of erotic teens. Tons of erotic teens can be found on these handpicked teen galleries. We add new Young Tits galleries every day. So bookmark us and come back tomorrow to see Tiny Tits galleries.

Erotic Teens Tits | Tiny Tits United States

5/4/2012 4:18:47 AM

Erotic Teens Tits | Tiny Tits

Welcome to kettysmalltits.com - collection of erotic teens. Tons of erotic teens can be found on these handpicked teen galleries. We add new Young Tits galleries every day. So bookmark us and come back tomorrow to see Tiny Tits galleries.www.kettysmalltits.com

Erotic Teens Tits | Tiny Tits United States

Pingbacks and trackbacks (2)+

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About the author

Morten Nielsen

Silverlight MVP

Morten Nielsen
<--That's me
E-mail me Send mail

Twitter @dotMorten 

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2005-2011

Month List

RecentComments

Comment RSS