Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to Add User Customized Properties to Your Silverlight Control

0.00/5 (No votes)
28 Jan 2014 1  
It's easy to add user customized properties to any Silverlight control.

Introduction

Have you ever needed, somehow, to have some information passed in to a UserControl (or even a native control) and found yourself messing around with a CSV string on the Tag property of that control?
I have and, after all, it's so easy to create additional properties to any control.
For demonstration purposes, I'll use a Button but you can use this method on any control you like.
Let’s see how.
I'll be using Silverlight 4, VS 2010 and Blend 4.

The Demonstration Page

To demonstrate how it works, start by creating a Silverlight project named “PropertiesDemo“ and add 3 Buttons and a TextBox to the MainPage.

<UserControl x:Class="PropertiesDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" >

  <Grid x:Name="LayoutRoot" Background="White">
    <Button Content="Powered Up Button" Height="23" 
    HorizontalAlignment="Left" Margin="10,10,0,0" 
    x:Name="Button1" VerticalAlignment="Top" Width="126" />
    <Button Content="Value 1" Height="23" 
    HorizontalAlignment="Left" Margin="10,43,0,0" 
    Name="Button2" VerticalAlignment="Top" Width="61" />
    <Button Content="Value 2" Height="23" 
    HorizontalAlignment="Left" Margin="77,43,0,0" 
    Name="Button3" VerticalAlignment="Top" Width="59" />
    <TextBox Height="23" HorizontalAlignment="Left" 
    Margin="157,10,0,0" Name="TextBox1" 
    VerticalAlignment="Top" Width="120" />
  </Grid>
</UserControl>

Using the Code

First of all, in order to create that extra property, we need to create a new Class that inherits from the Button class and make it a base class.
Add a new Class file to the client project named “ButtonEx” and replace the code with the below lines.

using System.Windows;
using System.Windows.Controls;
namespace PropertiesDemo
{
  public class ButtonEx : Button
  {
    public ButtonEx() : base() { }
  }
}

Since Button1 will be the powered up button, we need to reference this new class we just created in the UserControl tag of the MainPage XAML.

xmlns:my="clr-namespace:PropertiesDemo"

And replace the Button1 type tag “<Button” with “<my:ButtonEx”.

<my:ButtonEx Content="Powered Up Button" Height="23" 
HorizontalAlignment="Left" Margin="10,10,0,0" x:Name="Button1" 
VerticalAlignment="Top" Width="126" />

Now, we can create that extra property on the ButtonEx class and it will become:

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

namespace PropertiesDemo
{
  public class ButtonEx : Button
  {
    private static DependencyProperty ExtraPropProperty = DependencyProperty.Register
    ("ExtraProp", typeof(string), typeof(ButtonEx), 
    new PropertyMetadata("No extra prop set"));
    public string ExtraProp
    {
      get { return (string)GetValue(ExtraPropProperty); }
      set { SetValue(ExtraPropProperty, value); }
    }

    public ButtonEx() : base() { }
  }
}

If we build the project at this point, the property will be available as any other property of the button.

Now for the Click events.
Button1 Click event will pop up a MessageBox showing the ExtraProp property value.
Button2 and Button3 will just change the value of the new property on Button1.

<my:ButtonEx Content="Powered Up Button" Height="23" 
HorizontalAlignment="Left" Margin="10,10,0,0" x:Name="Button1" 
VerticalAlignment="Top" Width="126" Click="Button1_Click"/>
<Button Content="Value 1" Height="23" HorizontalAlignment="Left" 
Margin="10,43,0,0" Name="Button2" VerticalAlignment="Top" 
Width="61" Click="Button2_Click" />
<Button Content="Value 2" Height="23" HorizontalAlignment="Left" 
Margin="77,43,0,0" Name="Button3" VerticalAlignment="Top" 
Width="59" Click="Button3_Click" />
private void Button1_Click(object sender, RoutedEventArgs e)
{
  MessageBox.Show(Button1.ExtraProp.ToString());
}

private void Button2_Click(object sender, RoutedEventArgs e)
{
  Button1.ExtraProp = "Hallo from first button";
}

private void Button3_Click(object sender, RoutedEventArgs e)
{
  Button1.ExtraProp = "Bye from second button";
}

Hit F5 and try it out! Great hum!

Now, the binding part.
As I stated before, this new property is available as any other property and will be listed in the Property window of VS and Blend.

Replace the TextBox1 line on the MainPage XAML with:

<TextBox Height="23" HorizontalAlignment="Left" 
Margin="157,10,0,0" Name="TextBox1" VerticalAlignment="Top" 
Width="120" Text="{Binding Path=ExtraProp, Mode=TwoWay, ElementName=Button1}" />

Hit F5 and enjoy.

The TwoWay binding is fully functional and you can change the property value by changing the TextBox content.

You can, of course, add as many DependencyProperties as you like and of any type you need and use it in any other type of control.

I hope this has been useful to someone.

DependencyProperty - The Key Point

The DependencyProperty, as I use it in this example, although simple, has a few things that must be done this way.

Basically, we’ll be registering a new property on the class using the Register method of the DependencyProperty class.

We have three options for that method. We'll be using the second one, “Register(String, Type, Type, PropertyMetadata)”.

private static DependencyProperty ExtraPropProperty = DependencyProperty.Register
("ExtraProp", typeof(string), typeof(ButtonEx), 
new PropertyMetadata("No extra prop set"));

Naming convention states that the DependencyProperty’s name has to be the name of the registered property plus the suffix Property.

So, if our new property will be ExtraProp (the String parameter on the Register method), the DependencyProperty's name will be ExtraPropProperty.

The first Type is the property type (our case, string) and the second one the property owner (the class where this DP is to be created, in our case it’s the name of our base class, ButtonEx).

The PropertyMetadata is used to set the default value of our new property when the base class is initialized.

The Property Wrapper

public string ExtraProp
{
  get { return (string)GetValue(ExtraPropProperty); }
  set { SetValue(ExtraPropProperty, value); }
}

There isn’t much to it except that it has the same registered name of the DP and the get;set; reference the created DP.

Change Notification

One great thing about DependencyProperty is that INotifyPropertyChange is built in it so the binding on the XAML works great and gets notified and updated on any change of the DP’s value.

You can even add additional code to execute when the value changes.

One way to do it:

public string ExtraProp
{
  get { return (string)GetValue(ExtraPropProperty); }
  set 
    { 
      SetValue(ExtraPropProperty, value); 
      //Additional code goes here
    }
}

Two Warnings About Change Notification

The change notification is raised when, and only when (this is important), the DependencyProperty’s value changes.

Works great when using native types (string, int, bool, etc.).

But, when using complex types or user defined types, you have to be aware that only the changes on the class members raise notification changes. This means that the value of the DP doesn’t change, only one of its members, so the DP doesn’t raise any change notification event.

If you want the DP to raise the notification event, you have to bubble it from your user defined class.

See the Items_Class class in this article as an example where I used that technique to bubble a notification change event from an item in an ObservableCollection.

The other thing where you’ll be struggling with is when you use a DP in a UserControl that will be used as an ItemTemplate of a binded collection.

You’ll find out that the additional code you placed after the SetValue() in the set; of the property wrapper doesn’t run. I don’t have the knowledge to explain it, but it doesn’t.

This is where I use a variation of the Register() method and use a second form of the PropertyMetadata class, “PropertyMetadata(Object, PropertyChangedCallback)”, and define a callback function that executes when the property’s value changes.

private static DependencyProperty ExtraPropProperty = DependencyProperty.Register("ExtraProp", typeof(string), typeof(ButtonEx), new PropertyMetadata("No extra prop set", new PropertyChangedCallback(ExtraPropChanged)));

private static void ExtraPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as ButtonEx).ExtraPropChanged(e); }
private void ExtraPropChanged(DependencyPropertyChangedEventArgs e)
{
  MessageBox.Show("Old value: " + (e.OldValue != null ? 
  e.OldValue.ToString() : "null") + "\n" + "New value: " + 
  (e.NewValue != null ? e.NewValue.ToString() : "null"));
}

More on DependencyProperty

Check these links for a deeper DependencyProperty knowledge:

The Full Code

MainPage.xaml

<UserControl x:Class="PropertiesDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:my="clr-namespace:PropertiesDemo"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" >

  <Grid x:Name="LayoutRoot" Background="White">
    <my:ButtonEx Content="Powered Up Button" Height="23" 
    HorizontalAlignment="Left" Margin="10,10,0,0" x:Name="Button1" 
    VerticalAlignment="Top" Width="126" Click="Button1_Click"/>
    <Button Content="Value 1" Height="23" 
    HorizontalAlignment="Left" Margin="10,43,0,0" Name="Button2" 
    VerticalAlignment="Top" Width="61" Click="Button2_Click" />
    <Button Content="Value 2" Height="23" 
    HorizontalAlignment="Left" Margin="77,43,0,0" Name="Button3" 
    VerticalAlignment="Top" Width="59" Click="Button3_Click" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="157,10,0,0" 
    Name="TextBox1" VerticalAlignment="Top" Width="120" 
    Text="{Binding Path=ExtraProp, Mode=TwoWay, ElementName=Button1}" />
  </Grid>
</UserControl>

MainPage.cs

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

namespace PropertiesDemo
{
  public partial class MainPage : UserControl
  {
    public MainPage()
    {
      InitializeComponent();
    }

    private void Button1_Click(object sender, RoutedEventArgs e)
    {
      MessageBox.Show(Button1.ExtraProp.ToString());
    }

    private void Button2_Click(object sender, RoutedEventArgs e)
    {
      Button1.ExtraProp = "Hallo from first button";
    }

    private void Button3_Click(object sender, RoutedEventArgs e)
    {
      Button1.ExtraProp = "Bye from second button";
    }
  }
}

ButtonEx.cs

using System.Windows;
using System.Windows.Controls;
namespace PropertiesDemo
{
  public class ButtonEx : Button
  {
    private static DependencyProperty ExtraPropProperty = DependencyProperty.Register
    ("ExtraProp", typeof(string), typeof(ButtonEx), 
    new PropertyMetadata("No extra prop set", new PropertyChangedCallback(ExtraPropChanged)));
    public string ExtraProp
    {
      get { return (string)GetValue(ExtraPropProperty); }
      set { SetValue(ExtraPropProperty, value); }
    }

    private static void ExtraPropChanged(DependencyObject d, 
    DependencyPropertyChangedEventArgs e) { (d as ButtonEx).ExtraPropChanged(e); }
    private void ExtraPropChanged(DependencyPropertyChangedEventArgs e)
    {
      MessageBox.Show("Old value: " + (e.OldValue != null ? 
      e.OldValue.ToString() : "null") + "\n" + 
      "New value: " + (e.NewValue != null ? e.NewValue.ToString() : "null"));
    }

    public ButtonEx() : base() { }
  }
}

Conclusion

This is a very easy way to add extra properties to your controls and brings along an easy way to learn and use DependencyProperties.

My advice: Use and abuse of DependencyProperties. It can save you a lot of lines of code.

Please don't forget to vote and/or comment!

History

  • 29-Jan-2014 - Added some explanation about DependencyProperties

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here