Try it: Create a custom control with custom properties

Blend for Visual Studio 2012 provides a number of different system controls and simple styles that you can use in your applications. However, if these controls or styles do not meet a specific need that you have, you can create a custom control, by creating a Microsoft .NET class that inherits from one of the System.Windows.Controls classes.

The following procedures show you how to create a custom button control that includes a new property to hold the path of an image file in order to display an image.

For an example of creating a user control without using code, and therefore has no custom properties, see Create an empty user control.

For more information about custom controls, including XAML and code examples, see Control Authoring Overview and DependencyProperty Class in the Windows Presentation Foundation section on MSDN.

To define a custom control

  1. In the Projects panel, locate the file Window1.xaml and expand it to reveal your project's code-behind file, which is named Window1.xaml.cs. (If you chose Microsoft Visual Basic as the language when you created your project, the code-behind file will be named Window1.xaml.vb.) Double-click the code-behind file to open it for editing.

  2. In the Window1.xaml.cs file, paste the following class definition source code just before the last closing brace (}) in the file. (For Visual Basic, paste the appropriate code just before End Namespace or just after the last End Class.)

    public class ImageButton : Button 
    { 
      public ImageSource Source 
      { 
        get { return base.GetValue(SourceProperty) as ImageSource; } 
        set { base.SetValue(SourceProperty, value); } 
      } 
      public static readonly DependencyProperty SourceProperty = 
        DependencyProperty.Register("Source", typeof(ImageSource), typeof(ImageButton), null); 
    }
    
    Class ImageButton
      Inherits Button
      Public Property Source() As ImageSource
        Get
          Return CType(MyBase.GetValue(SourceProperty), ImageSource)
        End Get
        Set(ByVal value As ImageSource)
          MyBase.SetValue(SourceProperty, value)
        End Set
      End Property
      Public Shared ReadOnly SourceProperty As DependencyProperty = DependencyProperty.Register("Source", GetType(ImageSource), GetType(ImageButton), nothing)
    End Class
    
  3. Save the modified code-behind file, and then close the file.

    Tip

    The ImageButton class demonstrates the dependency property pattern. Externally, the class exposes a regular common language runtime (CLR, that is, .NET) property named Source, which is of type ImageSource. The class also registers and exposes a read-only dependency property field named SourceProperty, and implements the CLR accessors for the Source property in terms of the dependency property. Backing a property with a registered dependency property means that WPF or Silverlight can provide a wealth of services to your property. These services include styling, data binding, value inheritance, default values, and animation. There is also a feature called template-binding where you bind to the property's value from within a control template. This tutorial will show template-binding in action.

    Tip

    If you prefer to keep your custom control class definition source code in a separate source code file, you can do so. As an alternative to the previous two steps, if you prefer, you can create a new file called ImageButton.cs, paste the source code into this file together with the namespace declaration and the same set of using directives from your document's code-behind file, and then add the new file to your project (on the Project menu, click Add Item).

  4. You now need to build your project so that the new ImageButton control will appear in the Assets panel. On the Project menu of Blend, click Build Project (or press Ctrl+SHIFT+B). Verify that the build has completed without errors.

  5. Switch back to editing Window1.xaml in Blend.

  6. In the Tools panel along the left side of Blend, click Assets 0d8b8d29-1af9-418f-8741-be3097d76eab.

    This opens the Assets panel to show you all the controls, panels, and objects that you can place in your document.

  7. From the Project tab, select the ImageButton control from the list.

    The Tools panel icon underneath the Assets button is now set to your ImageButton control, and it is already selected so that you can draw the control on the artboard.

    Note

    Custom controls will not appear in the Project category of the Assets panel unless you have added the source code to your project and performed a build (Ctrl+Shift+B).

  8. With the ImageButton icon selected in the Tools panel, draw an ImageButton by clicking on the artboard and drawing a bounding box inside the document.

    The ImageButton is rendered on the artboard and an ImageButton object named [ImageButton] "ImageButton" is listed as a child object of LayoutRoot in the Objects and Timeline panel.

To modify the style of your custom control

  1. To create a control template for all ImageButton objects, right-click the new ImageButton, click Edit Template, and then click Edit a Copy.

    The Create Style Resource dialog box appears.

  2. In the Resource name (Key) field of the Create Style Resource dialog box, in the text box next to the first radio button, type ImageButtonStyle.

    This sets a name for your template in the resource dictionary so you can set it as the template for any ImageButton object.

    Note

    Control templates are wrapped inside styles so that the style that is applied to a control includes both the appearance (parts) and the behavior for the control.

  3. In the Define in field, select the This document radio button and make sure that Window: Window is selected in the drop-down list.

    The Define in field indicates which resource dictionary to define your new template in. Selecting Window: Window means that the template will be visible to all ImageButton controls in the window.

  4. Click OK.

    You have now copied the default control template of all ImageButton objects and have saved the copy as a new ControlTemplate named ImageButtonStyle. The new template has been placed in the resource dictionary, which you can view in the Window node of the Resources panel.

    With the creation of a new template, Blend enters a mode in which you can view and edit the new template. In the Objects and Timeline panel, the word Template above the new object tree indicates the current scope in which you are editing. The template includes a ContentPresenter object that will automatically display the value of the Content property of the ImageButton that uses this template.

    Tip

    To exit the template-editing mode and return to the scope of your document, click Return scope to 55844eb3-ed98-4f20-aa66-a6f5b23eeb2b, which is above the object tree next to the word ImageButtonStyle.

    To return to template-editing mode for an existing template, right-click the object whose template you want to edit in the Objects and Timeline panel (in this case, the [ImageButton] "ImageButton" object), click Edit Template, and then click Edit Current.

  5. In the Objects and Timeline panel, right-click the ContentPresenter object, point to Group Into, and then click Grid.

    The ContentPresenter object is made a child object of a new Grid object. Without the grid object, you would not be able to add a second child object to the template because the chrome object can only contain one child object.

  6. With the Grid object selected in the Objects and Timeline panel, locate the Width and Height properties in the Layout category of the Properties panel. Click Advanced options 12e06962-5d8a-480d-a837-e06b84c545bb beside each of the properties and click Reset.

  7. To divide the grid into two columns, double-click the grid object in the Objects and Timeline panel to activate the grid, and then with the Selection tool 2ff91340-477e-4efa-a0f7-af20851e4daa selected in the Tools panel, move the mouse pointer over the thick blue ruler area at the top of the Grid on the artboard. An orange column ruler will follow your mouse pointer to indicate where a new column divider will be placed if you click.

    Click to create a new column divider in the middle of the Grid.

    A blue column divider appears inside the Grid.

  8. Select Image adb61060-da5f-4279-8c0d-3681bfeb145c from the Controls category of the Assets panel (click the All subcategory). With the Grid object still activated in the Objects and Timeline panel, draw a new Image inside the right column of the Grid.

  9. With the new Image object selected in the Objects and Timeline panel, look under Common Properties in the Properties panel. Click Advanced options 12e06962-5d8a-480d-a837-e06b84c545bb to the right of the Source property, point to Template Binding, and then click Source.

    You have just template-bound the Source property of the Image to the Source property of the ImageButton that uses this template.

  10. You are now done editing the template. To exit to the scope of the root object, click Scope Up in the Objects and Timeline panel.

  11. With your ImageButton selected in the Objects and Timeline panel, locate the Source property in the Miscellaneous category of the Properties panel, and set it to the path of an image file on your computer.

    The image displays on the right side of the ImageButton control.

To apply the style to another custom control

  1. From the Project category of the Assets panel, select the ImageButton control. Draw a new ImageButton control on the artboard.

  2. Right-click the new ImageButton, click Edit Template, click Apply Resource, and then click the name of your style, ImageButtonStyle.

    The ImageButtonStyle is applied to your new ImageButton control. Locate the Source property in the Miscellaneous category of the Properties panel, and set it to the path of an image file on your computer.

    Tip

    Alternatively, you could add an ImageButton to the artboard that is already styled using your template. From the Styles category of the Assets panel, select ImageButtonStyle, and then draw the styled ImageButton on the artboard.

To apply descriptions for your custom property

  1. In the top of the code-behind file (Window1.xaml.cs), add a reference to the System.ComponentModel namespace.

    The Description and Category attributes used below are defined in that namespace.

  2. Paste the following (bold) line before the class definition:

    [Description("Represents a custom button control that responds to a 
    Click event. Displays an image using a custom Source property if the 
    Source property is bound to an Image in the template.")]
    public class ImageButton : Button
    
    <Description("Represents a custom button control that responds to a 
    Click event. Displays an image using a custom Source property if the 
    Source property is bound to an Image in the template.")> _
    Class ImageButton
    
  3. Paste the following line (bolded) before the custom property definition:

    [Description("The image displayed in the button if there is an Image 
    control in the template whose Source property is template-bound to 
    the ImageButton Source property."), Category("Common Properties")] 
        public ImageSource Source
    
    <Description("The image displayed in the button if there is an Image 
    control in the template whose Source property is template-bound to 
    the ImageButton Source property."), Category("Common Properties")> _ 
        Public Property Source() As ImageSource
    

    The Category attribute configures where the property will show up in the Properties panel.

  4. Rebuild your project (Ctrl+Shift+B).

    Now, the Source property for the ImageButton appears in the Common Properties category of the Properties panel, and the descriptions appear in tooltips when you move your mouse pointer over the property and over the control in the Assets panel.

You can add code to the constructor of any custom control that will set a property to a default value when your control is drawn on the artboard, and therefore in design mode. Setting a property using the following procedure will only affect what you see on the artboard and will not affect what you see when your application is running. This is useful when content is not available for your control when in design mode, but will be available when your application is running, such as when the content comes from a database or web service. In this case, the Source property of an ImageButton on the artboard is set to the name of an image file in your project until it is explicitly set.

To configure your control to display a default image

  1. In the code-behind file (Window1.xaml.cs), paste the following lines of code (bolded) after the property definition:

    Note

    Change the file name (Sunset.jpg) in the following code to the name of an image file in your project. To add an image file to your project, right-click your project name in the Projects panel, and click Add Existing Item.

    public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(ImageSource), typeof(ImageButton));
    
    // Constructor:  
    public ImageButton()  
    {  
       if (DesignerProperties.GetIsInDesignMode(this))  
       {  
          this.Source = new BitmapImage(new Uri("Sunset.jpg", UriKind.Relative));  
       }  
    }  
    
    Public Shared ReadOnly SourceProperty As DependencyProperty = DependencyProperty.Register("Source", GetType(ImageSource), GetType(ImageButton))
    
    Public Sub New()  
        If DesignerProperties.GetIsInDesignMode(Me) Then  
            Me.Source = New BitmapImage(New Uri("Sunset.jpg", UriKind.Relative))  
        End If  
    End Sub  
    
  2. Rebuild your project (Ctrl+Shift+B).

    Now, when you add an ImageButton to the artboard that uses the style that you created, a default image appears in the button.

    Note

    You will not be able to set the Source property to any other value while in design mode.