I came to the WPF scene after spending some time as a Programmer/Writer for the Windows Forms team.  When I first started using WPF, one of the first things I tried to do was create a custom control by inheriting from System.Windows.Controls.Control.  I didn’t get very far because I didn’t understand the role of the ControlTemplate and even changing the color seemed difficult.  Actually, even getting my custom control to appear seemed difficult.

 

Consider the following class that inherits from System.Windows.Forms.Control (the base class for all controls in Windows Forms):

 

    public class WinformControl : Control

    {

        public WinformControl()

            : base()

        {

            this.BackColor = Color.Green;

        }

    }

 

 

This control is as simple as they come, and adding it to a Windows form is just as easy:

 

        public Form1()

        {

            InitializeComponent();

 

            WinformControl myControl = new WinformControl();

            myControl.Height = 30;

            myControl.Width = 30;

            myControl.Top = 10;

            myControl.Left = 10;

 

            this.Controls.Add(myControl);

        }

 

This gives you a small, green square in the Window.  Not a very useful control, but it gives you something. However, if you try the same thing in WPF, nothing appears in the app.

 

   public class MyWPFControl : Control

    {

        public MyWPFControl()

            : base()

        {

            Background = Brushes.Blue;

        }

    }

 

    public class Window1 : System.Windows.Window

    {

        public Window1()

        {

            MyWPFControl myControl = new MyWPFControl();

            myControl.Height = 30;

            myControl.Width = 30;

 

            // Give the control a position on a Canvas called root.

            Canvas.SetTop(myControl, 10);

            Canvas.SetLeft(myControl, 10);

            root.Children.Add(myControl);

 

        }

     }

 

The problem here is that although the Control class has a Background property, the control class by itself doesn’t have an appearance.  It’s the Control.Template property that determines the appearance of the control.  If you want to create a control class that inherits from Control, you must define a ControlTemplate for the class as well. 

 

At perhaps the simplest level, the ControlTemplate is a composite of UIElements. The root of the ContentTemplate can only contain one child UIElement, but since the UIElement can be a Panel, the template can be as rich or as simple as you need it to be.  WPF works from the idea that every control is made up of other elements.  For example, suppose that there is a button with its Content property set to the string, “Hello world”.  This button essentially contains a TextBlock that displays the string.  (The button contains a few other objects as well, including a ContentPresenter that creates the TextBlock, but the role of the ContentPresenter is beyond the scope of this post.) 

 

Control templates are typically created in XAML within a <Style> element.

 

<ResourceDictionary

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="clr-namespace:CustomControlWPF"

    >

    <Style TargetType="{x:Type local:MyWPFControl}">

        <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate TargetType="{x:Type local:MyWPFControl}">

                    <Border Background="Orange" >

                    </Border>

                 

                </ControlTemplate>

            </Setter.Value>

        </Setter>

    </Style>

</ResourceDictionary>

 

This ControlTemplate specifies a Border with an orange background. In order to use the new style, you must create a static constructor and override the metadata for the DefaultStyleKeyProperty to tell the control to look for a new style.

 

 

    public class MyWPFControl : System.Windows.Controls.Control

    {

        static MyWPFControl()

        {

            //This OverrideMetadata call tells the system that this element wants

            //to provide a style that is different than its base class.

            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyWPFControl),

                new FrameworkPropertyMetadata(typeof(MyWPFControl)));

 

           

        }

 

        public MyWPFControl()

            : base()

        {

        }

 

    }

 

Now when you try to add the control to the application, you get, like you’d expect, an orange square.  You can specify the height and the width of the control and the square resizes accordingly.  That’s all very well, but is pretty limiting because in the template we have specified that the square is always orange.  Any time someone wants to change the color of the square, they have to redefine the control’s template.  That’s where the Control.Background property comes in.  You can specify that the template should use the value of Control.Background to determine the Border’s background. 

 

    <Style TargetType="{x:Type local:MyWPFControl}">

        <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate TargetType="{x:Type local:MyWPFControl}">

                    <Border Background="{TemplateBinding Background}" >

                    </Border>

                 

                </ControlTemplate>

            </Setter.Value>

        </Setter>

    </Style>

 

 

Now when you set the Background property on your custom control, the control uses that value to paint the background of the Border. Control has several properties like this:  Background, BorderBrush, BorderThickness, FontFamily, FontSize, FontStretch, FontWeight, Foreground, HorizontalContentAlignment, and VerticalContentAlignment.  These common properties are supplied so that control authors don’t have to define them.  However, control authors do need to specify what the properties mean in the control’s template.  Not every control will implement all of these properties in its template.  For example, setting the Foreground property on a ScrollBar does not affect the appearance of the ScrollBar in any way.

 

Even though you need to create a template to even get the control to appear, there are advantages to using a ControlTemplate. By creating a template for your custom control, you enable your customers to customize the control’s appearance without having to inherit from your control. Templating is a powerful tool, but it takes some getting used to before someone truly understands the advantages of it.

 

Carole


About Us

We are the Windows Presentation Foundation SDK writers and editors.