Skip to main content

WPF Tutorials - Custom Control Templates

Almost every control in WPF uses a control template to define how it displays itself and its contents. In fact, you can override the control template on every built-in control to make it appear and act exactly like you want. Rather than overriding an existing template, however, this tutorial is going to demonstrate how to build a custom content control and use a control template to define how it looks.
MSDN has tons of examples that override the control template for WPF controls, however they don't have any great examples on building your own. What I've needed on several occasions is a custom control that can be populated using XAML and contain any number of custom elements. That's what we're going to build today. Below are a couple of examples of what this this tutorial will cover.
Thought Bubble Example 1
<Window x:Class="CustomContentControl.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:my="clr-namespace:CustomContentControl"
   Title="Window1" Height="300" Width="300" Background="LightBlue">
  <Grid>
    <my:ThoughtBubble Width="200" Height="200" BubbleBackground="White">
      <StackPanel Orientation="Vertical"
                 HorizontalAlignment="Center"
                 VerticalAlignment="Center">
        <TextBlock Text="Here's a button!" Margin="0,0,0,5" />
        <Button Content="My Button"/>
      </StackPanel>
    </my:ThoughtBubble>
  </Grid>
</Window>
Thought Bubble Example 2
<Window x:Class="CustomContentControl.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:my="clr-namespace:CustomContentControl"
   Title="Window1" Height="400" Width="300" Background="LightBlue">
  <Grid>
    <my:ThoughtBubble Width="200" Height="300" BubbleBackground="LightGray">
      <Image Source="picard.jpg" />
    </my:ThoughtBubble>
  </Grid>
</Window>
The first thing we're going to need to do is build ourselves the thought bubble control. Unfortunately, WPF makes this a little more difficult than I'd like. We can't make a UserControl and use its XAML to define the look. If we did and we attempted to populate our bubble with named elements, we'd get a compile error that looks something like this:
Cannot set Name attribute value 'name' on element 'element'. 'element' is under the scope of element 'control', which already had a name registered when it was defined in another scope.
What we have to do instead is create a UserControl subclass without an attached XAML file and define the look in a style. We'll start with the class.
using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;

namespace CustomContentControl
{
  public class ThoughtBubble : UserControl
  {
    /// <summary>
    /// Gets or sets the background color of the thought bubble.
    /// </summary>
    public SolidColorBrush BubbleBackground
    {
      get { return (SolidColorBrush)GetValue(BubbleBackgroundProperty); }
      set { SetValue(BubbleBackgroundProperty, value); }
    }

    public static readonly DependencyProperty BubbleBackgroundProperty =
        DependencyProperty.Register("BubbleBackground",
        typeof(SolidColorBrush),
        typeof(ThoughtBubble),
        new UIPropertyMetadata(Brushes.White));
  }
}
As you can see, the amount of C# code required to do this is very small. I only have one dependency property that lets users change the background color of the thought bubble. If you're not familiar with dependency properties, I'd recommend reading our introductory tutorial. Your object will have to extend some sort of display control - I typically just extend UserControl. That's it for code. The rest is entirely done in XAML.
Chunking up the XAML will probably make it difficult to read, so I'm going to post the entire thing and explain it afterwards.
<Application x:Class="CustomContentControl.App"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:my="clr-namespace:CustomContentControl"
   StartupUri="Window1.xaml">
  <Application.Resources>
    <Style TargetType="{x:Type my:ThoughtBubble}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type my:ThoughtBubble}">
            <Grid>
              <Border CornerRadius="24"
                     Background="{TemplateBinding BubbleBackground}"
                     BorderBrush="Black"
                     BorderThickness="2"
                     Margin="0,0,0,30"
                     Padding="24">
                  <ContentPresenter />
              </Border>
              <Grid VerticalAlignment="Bottom"
                   HorizontalAlignment="Right"
                   Margin="0,0,30,0" >
                <Polygon Points="10,0 40,0 0,30"
                        Fill="{TemplateBinding BubbleBackground}"
                        VerticalAlignment="Bottom"
                        HorizontalAlignment="Right" />
                <Line X1="10" Y1="0" X2="0" Y2="30"
                     Stroke="Black" StrokeThickness="2" />
                <Line X1="10" Y1="0" X2="40" Y2="0"
                     Stroke="{TemplateBinding BubbleBackground}"
                     StrokeThickness="3" />
                <Line X1="40" Y1="0" X2="0" Y2="30"
                     Stroke="Black" StrokeThickness="2" />
              </Grid>
            </Grid>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Application.Resources>
</Application>
I'm using WPF's powerful styling system to do most of the work for me. This ControlTemplate is set using a style defined in App.xaml. App.xaml is automatically created whenever you create a new WPF project in Visual Studio. By giving my style a TargetType and not a key, this style will be applied to every instance of ThoughtBubble added to my app. This isn't the most elegant way to create the shape of a thought bubble, but it gets the job done. And besides, creating the shape isn't the important part of this tutorial.
When creating our ControlTemplate, we need to specify the type of object that this template is intended for. This is required if the definition uses a content presenter, which ours does, and I'll explain what it is a little later. The type is simply the same type as the style {x:Type my:ThoughtBubble}.
When we create the Border, you might notice the TemplateBinding markup extension. This lets us bind to properties on the object that this template is being applied to. This is how our BubbleBackground property is used.
The last oddity in this style is the ContentPresenter. This object is pretty simple - it displays the contents of a ContentControl. Since UserControl extends ContentControl, and our object extends UserControl, we get to use this object. This is what the user populates when they add elements inside our object.

Comments

Popular posts from this blog

ASP .NET - XML Files

An XML File Here is an XML file named "countries.xml": <?xml version="1.0" encoding="ISO-8859-1"?> <countries> <country>   <text>Norway</text>   <value>N</value> </country> <country>   <text>Sweden</text>   <value>S</value> </country> <country>   <text>France</text>   <value>F</value> </country> <country>   <text>Italy</text>   <value>I</value> </country> </countries> Bind a DataSet to a List Control First, import the "System.Data" namespace. We need this namespace to work with DataSet objects. Include the following directive at the top of an .aspx page: <%@ Import Namespace="System.Data" %> Next, create a DataSet for the XML file and load the XML file into the DataSet when the page is first loaded: <script runat="server"> sub Page_Load if Not Page.IsPos...

Net FrameWork (Basic Concepts)

Net FrameWork (Basic Concepts) See more presentations by aSGuest51943  |  Upload your own PowerPoint presentations .Net Framework (Basic Concepts) : .Net Framework (Basic Concepts) 1 Amrita Academy For Career Excellence Overview of the .NET Framework : Overview of the .NET Framework Software development environment which helps developers to develop applications quickly and gives optimum, efficient, scalable, performance oriented applications in different languages like Visual Basic .NET, C#, ASP.NET, and Jscript .NET etc... 2 Amrita Academy For Career Excellence Slide 3: Services NET Framework provides the following services: Tools for developing software applications , run-time environments for software application to execute, server infrastructure, value added intelligent software which helps developers to do less coding and work efficiently. The .Net Framework will enable developers to develop applications for various devices and platforms like window...

Crystal and Reporting Services Interview Questions - Part 2

Introduction   This FAQ will give you a quick start for two report giant's crystal and reporting services. We have two IIS application 'Reports' and 'Reportserver' what do they do ?   When you install reporting services there are two virtual directories created as shown in the figure below. Figure : - IIS Applications for reports The 'Reports' virtual directory is like an admin. If you browse to http://yourpcname/reports/home.aspx   page you can view, edit properties and display reports. We have numbered the below figure. 1 à We can browse reports and see the output of reports. 2 à We can upload a RDL file directly in the report's database. 3 à Using the report builder link we build new reports. 4 à Using the properties we can do role assignment. 5 à Using subscription link we can add new outputs (FTP, Folder etc) to subscribe to the reports. 6 à Site settings help us to decide how many numbers of snapshots we want in the history, report time out...