Quantcast
Channel: Henry Cordes, My thoughts exactly... - Silverlight
Viewing all articles
Browse latest Browse all 2

Create a UserControl with Silverlight 2.0 (Beta x) - Part 2

$
0
0
Series  - Create a UserControl with Silverlight 2.0 Beta x

Part I   - Create a UserControl with Silverlight 2.0 Beta 1
Part II - Create a UserControl with SilverLight 2.0 Beta  

DependencyProperties

In my post Create a UserControl with Silverlight 2.0 Beta 1. I started creating a UserControl in Silverlight using Silverlight 2.0 Beta 1, Beta 2 is out now.
Because I want to get familair with Silverlight (WPF, Silverlight should be a subset of WPF and thus IMO a great way to get more knowledge of this technology).

In my first part of the story, I made a UserControl. I did it with the .NET knowledge I already had, but doing so, I completely forgot to RTFM.
Later I did and learned about DependencyProperties. A great other way to work with properties.

MSDN:
A property that is backed by the WPF property system is known as a dependency property.
The purpose of dependency properties is to provide a way to compute the value of a property based on the value of other inputs. These other inputs might include system properties such as themes and user preference, just-in-time property determination mechanisms such as data binding and animations/storyboards, multiple-use templates such as resources and styles, or values known through parent-child relationships with other elements in the element tree. In addition, a dependency property can be implemented to provide self-contained validation, default values, callbacks that monitor changes to other properties, and a system that can coerce property values based on potentially runtime information. Derived classes can also change some specific characteristics of an existing property by overriding dependency property metadata, rather than overriding the actual implementation of existing properties or creating new properties.

The concept is a little overwhelming at first, seeing DependencyProperty code the first time can be confusing.
In Part 1 the HC.Silverlight.TryOut.GenderChooser had the property Gender.

   1:  public GenderChoice Gender
   2:  {
   3:      get { return _Gender; }
   4:      set 
   5:      { 
   6:      _Gender = value;
   7:      SetColorAccordingToChoice();
   8:      OnGenderChosen(this, _Gender);
   9:      }
  10:  }

Listing 1

In the setter of the Gender property, the method SetColorAccordingToChoice is called, this sets the colors of the symbols in a particular way and it fires the OnGenderChosen event.
Every containing control can subscribe to this event and react to changes when it is fired.

Now, in WPF and Silverlight DependencyProperties are available, this property is a great candidate for this concept. But how does it work?
First we refactor our Property  to use GetValue in the setter and SetValue in the setter. We also add a GenderProperty of type DependencyProperty and call the static method Register on the DependencyProperty object:

   1:  public GenderChoice Gender
   2:  {
   3:     get { return (GenderChoice)GetValue(GenderProperty); }
   4:     set { SetValue(GenderProperty, value); }
   5:  }
   6:   
   7:  publicstaticreadonly DependencyProperty GenderProperty =
   8:     DependencyProperty.Register("Gender", typeof(GenderChoice), 
   9:  typeof(GenderChooser), 
  10:  new PropertyMetadata(
  11:  new PropertyChangedCallback(OnGenderChanged)));

Listing 2

Now we got the skeleton to use the DependencyProperty concept in the getter and setter of the property. And we also got a DependencyProperty with the name GenderProperty registered on the FrameworkElement.

Lets analyse the registering of the DependencyProperty. First we look at the signature for the public static Register method on the System.Windows.DependencyProperty class.

   1:  // Summary:
   2:  //     Registers a dependency property with the specified property name, 
   3:  //       property type, owner type, and property metadata for the property.
   4:  //
   5:  // Parameters:
   6:  //   name:
   7:  //     The name of the dependency property to register.
   8:  //
   9:  //   propertyType:
  10:  //     The type of the property.
  11:  //
  12:  //   ownerType:
  13:  //     The owner type that is registering the dependency property.
  14:  //
  15:  //   typeMetadata:
  16:  //     A property metadata instance. 
  17:  //       This can contain a System.Windows.PropertyChangedCallback
  18:  //     implementation reference.
  19:  //
  20:  // Returns:
  21:  //     A dependency property identifier that should be used to set the value of
  22:  //     a public static readonly field in your class. That identifier is then used
  23:  //     to reference the dependency property later, for operations such as setting
  24:  //     its value programmatically.
  25:  publicstatic DependencyProperty Register(string name, 
  26:                                            Type propertyType, 
  27:                                            Type ownerType, 
  28:                                            PropertyMetadata typeMetadata);

Listing 3

The first parameter in the static Register method is name (string). Name is the name of the identifier field that you use to store the name and characteristics of the dependency property must be the Name you chose for the dependency property as part of the Register call appended by the literal string Property.
It is very important to follow the naming pattern, if you fail to designers might not report your property correctly, and certain aspects of property system style application might not behave as expected.

In our example,

  • the name of the dependency property and its CLR accessor is Gender;
  • The identifier field is GenderProperty;
  • The type of the property is GenderChoice;
  • The type that registers the dependency property is GenderChooser;
  • The PropertyMetadata is used to register the PropertyChangedCallback OnGenderChanged on the DependencyProperty, whenever the value in the property changes (is set) this callback is fired. In this callback you put the code to do your validation or any code that needs to run when the value of the property changes. 

So now we need a delegate to declare our EventHandler. The delegate OnGenderChangedEventHandler returns nothing but takes an object and a GenderChoice as parameters.
Furthermore we need a public event GenderChanged and a private static void OnGenderChanged which is the callback function that is fired when the value of the Property changes.

   1:  publicdelegatevoid OnGenderChangedEventHandler(object sender, GenderChangedEventArgs e);
   2:   
   3:  publicevent OnGenderChangedEventHandler GenderChanged;
   4:   
   5:  /// <summary>
   6:  /// Raises Event for DependencyProperty
   7:  /// </summary>
   8:  /// <param name="d"></param>
   9:  /// <param name="e"></param>
  10:  privatestaticvoid OnGenderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  11:  {
  12:  if (d is GenderChooser)
  13:     {
  14:         ((GenderChooser)d).SetColorAccordingToChoice(e);
  15:         ((GenderChooser)d).OnGenderChosen((GenderChooser)d, 
  16:  new GenderChangedEventArgs((GenderChoice)e.NewValue));
  17:     }
  18:  }

Listing 4

This is all the code you need to make a DependencyProperty.
But in our UserControl from Part 1 ,the GenderChooser, we need the GenderChosen event to fire as soon as the value of our Gender property changes. As we see on line 15 of Listing 4 the OnGenderChanged method must be static, because the DependencyProperty.Register method on line 8 of Listing 3 is static and it calls the callback.

Because the callback function OnGenderChanged is static, we need to cast the DependencyObject d to the right object, GenderChooser, after we check if d is of type Genderchooser ofcourse.
Next we call the methods on the calling object itself with the help of a cast (((GenderChooser)d).SetColorAccordingToChoice(e);).

As is shown on line 15 of Listing 4 the OnGenderChosen method is called, which takes an object and a GenderChangedEventArgs, with a GenderChoice as parameter in the constructor, as parameters.
This is the method that will raise the event GenderChosen, if a class is attached to the eventhandler.

So we need the following code to make the event GenderChosen to work.

   1:  // Outside the class!
   2:  publicdelegatevoid GenderChosenEventHandler(object sender, GenderChoice gender);
   3:   
   4:   
   5:  // public partial class GenderChooser : UserControl
   6:  // {
   7:   
   8:  publicevent GenderChosenEventHandler GenderChosen;
   9:   
  10:  privatevoid OnGenderChosen(object sender, GenderChangedEventArgs e)
  11:  {
  12:  if (GenderChosen != null)
  13:      {
  14:          GenderChosen(sender, e.SelectedGender);
  15:      }
  16:  }

Listing 5

The GenderChosenEventHandler needs to be global, so needs to be outside the class and inside the namespace.
On line 10 (Listing 5) you see GenderChangedEventArgs, this is a class derived from EventArgs with nothing more than a TimeStamp and a GenderChoice property.
So it is easy to pass the SelectedGender in the Event.

GenderChangedEventArgs code

   1:  using System;
   2:   
   3:  namespace HC.Silverlight.TryOut
   4:  {
   5:  publicclass GenderChangedEventArgs: EventArgs
   6:      {
   7:  #region Properties
   8:  public DateTime Stamp { get; set; }
   9:  public GenderChoice SelectedGender { get; set; }
  10:  #endregion
  11:   
  12:  #region C'tors
  13:  public GenderChangedEventArgs(GenderChoice selectedGender)
  14:          {
  15:              Stamp = DateTime.Now;
  16:              SelectedGender = selectedGender;
  17:          }
  18:  #endregion
  19:      }
  20:  }

Listing 6

In Part I Line 36 to 55 the region 'Events' and the region 'EventHandler' can be removed! (thank you, Micheal Ashby (Microsoft) for pointing this out...).

Last but not least, the project is available and can be downloaded here.

DependencyProperties is a well thought out concept it was one of the first things designed when Microsoft started with WPF.
I cannot forget Mark Miller, I attended his session on WPF at the DevDays in Holland, he said: "WPF is designed by geniuses, but implemented by ...", I think the first part means a lot coming from Mark...

Series  - Create a UserControl with Silverlight 2.0 Beta x

Part I   - Create a UserControl with Silverlight 2.0 Beta 1
Part II - Create a UserControl with SilverLight 2.0 Beta  

Henry Cordes
My thoughts exactly...


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images