DCSIMG
WPF Control Extensions with Attached Properties - Pavel's Blog
Sign in | Join | Help

Pavel's Blog

Pavel is a software guy that is interested in almost everything
software related... way too much for too little time

WPF Control Extensions with Attached Properties

Sometimes we need some new functionality from a basic control. One way to go about it is to create a custom control and add that functionality. However, sometimes creating a new control is an overkill, especially when no new behaviour is actually needed. Perhaps some new way of presenting or drawing the control is all that’s needed.

The problem is that we may want more properties on the control, but we don’t want or need to actually extend it (i.e. derive from it).

For example, suppose we want to create a button with one of several images depending on its state: enabled, disabled or focused.

Deriving from Button seems too excessive: the images are not changing anything from a behaviour perspective. However, I would like to have new properties, such as NormalImage, DisabledImage and FocusedImage.

Adding properties to an existing class in WPF is possible through attached properties. Attached properties are contextual; they are defined by one class, but may be used by any other class.

Continuing the example, I can look for some already define attached properties that match my intentions. More often than not, I won’t find any.

To facilitate my image button, I can create a new class, define the attached properties by calling DependencyProperty.RegisterAttached and simply look for them in the appropriate class (the Button in this case, when used with a style).

Here’s an example: (you can add an attached property easily using Visual Studio with the propa code snippet – just type propa and hit Tab twice)

 

public sealed class ImageStates : DependencyObject {

   public static ImageSource GetNormalImage(DependencyObject obj) {

      return (ImageSource)obj.GetValue(NormalImageProperty);

   }

 

   public static void SetNormalImage(DependencyObject obj, ImageSource value) {

      obj.SetValue(NormalImageProperty, value);

   }

 

   public static readonly DependencyProperty NormalImageProperty =

       DependencyProperty.RegisterAttached("NormalImage", typeof(ImageSource), typeof(ImageStates), new UIPropertyMetadata(null));

   public static ImageSource GetDisabledImage(DependencyObject obj) {

      return (ImageSource)obj.GetValue(DisabledImageProperty);

   }

 

   public static void SetDisabledImage(DependencyObject obj, ImageSource value) {

      obj.SetValue(DisabledImageProperty, value);

   }

   public static readonly DependencyProperty DisabledImageProperty =

       DependencyProperty.RegisterAttached("DisabledImage", typeof(ImageSource), typeof(ImageStates), new UIPropertyMetadata(null));

   public static ImageSource GetFocusedImage(DependencyObject obj) {

      return (ImageSource)obj.GetValue(FocusedImageProperty);

   }

 

   public static void SetFocusedImage(DependencyObject obj, ImageSource value) {

      obj.SetValue(FocusedImageProperty, value);

   }

   public static readonly DependencyProperty FocusedImageProperty =

       DependencyProperty.RegisterAttached("FocusedImage", typeof(ImageSource), typeof(ImageStates), new UIPropertyMetadata(null));

}

I’ve created 3 attached properties in some class acting as a simple logical container for these properties.

Now, I can create some buttons like so:

<StackPanel>

  <Button Margin="4" local:ImageStates.NormalImage="book_open.png"

      local:ImageStates.DisabledImage="book_red.png"

      local:ImageStates.FocusedImage="book_open2.png" />

  <Button Margin="4" local:ImageStates.NormalImage="book_open.png" IsEnabled="False"

      local:ImageStates.DisabledImage="book_red.png"

      local:ImageStates.FocusedImage="book_open2.png" />

  <Button Margin="4" local:ImageStates.NormalImage="book_open.png"

      local:ImageStates.DisabledImage="book_red.png"

      local:ImageStates.FocusedImage="book_open2.png" />

</StackPanel>

The attached properties are used in the correct “context”. These properties are not part of Button, but now are used by it (actually by the style and template that’s applied). The application of the templates can be done like so: (a bit crude)

<Application.Resources>

   <Style TargetType="Button">

      <Style.Triggers>

         <Trigger Property="IsEnabled" Value="True">

            <Setter Property="ContentTemplate">

               <Setter.Value>

                  <DataTemplate>

                     <Image Stretch="None" Source="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=(local:ImageStates.NormalImage)}" />

                  </DataTemplate>

               </Setter.Value>

            </Setter>

         </Trigger>

         <Trigger Property="IsFocused" Value="True">

            <Setter Property="ContentTemplate">

               <Setter.Value>

                  <DataTemplate>

                     <Image Stretch="None" Source="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=(local:ImageStates.FocusedImage)}" />

                   </DataTemplate>

               </Setter.Value>

            </Setter>

         </Trigger>

         <Trigger Property="IsEnabled" Value="False">

           <Setter Property="ContentTemplate">

              <Setter.Value>

                 <DataTemplate>

                    <Image Stretch="None" Opacity=".5" Source="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=(local:ImageStates.DisabledImage)}" />

                 </DataTemplate>

              </Setter.Value>

           </Setter>

         </Trigger>

       </Style.Triggers>

    </Style>

</Application.Resources>

 

Here’s a screenshot of the result (top button is focused, middle is disabled):

image

The attached ZIP is the complete project. Power to Attached Properties!

Comments List

# Markus Tamm &raquo; Blog Archive &raquo; Links 21.02.2010

Published at Sunday, February 21, 2010 8:54 PM by Markus Tamm » Blog Archive » Links 21.02.2010  

Pingback from  Markus Tamm  &raquo; Blog Archive   &raquo; Links 21.02.2010

# re: WPF Control Extensions with Attached Properties

Published at Wednesday, May 26, 2010 11:39 AM by Rockon  

Thanks for this suggestion

# re: WPF Control Extensions with Attached Properties

Published at Thursday, July 01, 2010 6:19 AM by Philippines properties  

Thanks for the great suggestions.

Paula M

# WPF For WinForms (and MFC) Developers (Part 5)

Published at Saturday, January 29, 2011 9:48 PM by Pavel's Blog  

Previous posts in this series: Part 1 : Why WPF? Part 2 : What is XAML? Part 3 : XAML in practice Part

# re: WPF Control Extensions with Attached Properties

Published at Tuesday, June 07, 2011 7:32 AM by Lijo  

Can you please share Silverlight code for this?

Leave a Comment

(required) 
(
required
)
 
(optional)
(required) 

Enter the numbers above: