Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / Universal Windows Platform

Strongly Typed Dependency Property Registration in WPF and UWP

Rate me:
Please Sign up or sign in to vote.
4.11/5 (7 votes)
5 Aug 2018MIT4 min read 13.6K   60   2   2
Strongly typed dependency property registration implemented for WPF and UWP

Introduction

Almost all Microsoft UI frameworks use the same architectural model based on DependencyObjects and DependencyProperties. The same concept is used in WPF and UWP, outdated frameworks like Silverlight and Windows Phone, even in Xamarin and other curiosities like CSHTML5 (http://cshtml5.com/). DependencyObject is a base class that represents an object participating in dependency property system. It is implemented by UI controls, geometric shapes, templates, styles, etc. DependencyProperty (BindableProperty in Xamarin) is a “backing store” for a property which enables animation, styling, data binding, etc.

The fastest way to add a dependency property is to use a code snippet. Just type “propdp” and press tab and the following code will appear:

C#
public int MyProperty
{
    get { return (int)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register("MyProperty", typeof(int),
                                 typeof(ownerclass), new PropertyMetadata(0));

The problems of this implementation are visible at first glance. First of all, the property name is hardcoded as string. This problem ceased to exist with the introduction of the nameof operator. Today, the above code should look like this:

C#
DependencyProperty.Register(nameof(MyProperty), 
                            typeof(int), typeof(ownerclass), new PropertyMetadata(0));

From now on, the tricks related to extracting property names from lambdas are no longer needed. These days, almost no one will raise a PropertyChanged event this way:

C#
RaisePropertyChanged(() => MyProperty);

It can be done this way:

C#
RaisePropertyChanged(nameof(MyProperty));

Or this way (thanks to CallerMemberNameAttribute):

C#
RaisePropertyChanged();

The main problem in registering dependency properties however, is the danger of type mismatch. What if I replace int with double in the above example? The code looks correct but it will throw a runtime exception because the default value specified in PropertyMetadata is an integer:

C#
new PropertyMetadata(0));

To avoid the exception, we will have to write it this way:

C#
new PropertyMetadata(0d));

This problem has been already pointed out in this deleted article which in my opinion, has obtained an unfair assessment. People in comments section are mainly arguing against the title of the article and they pay attention to the fact that the runtime exception is thrown which tells exactly what is the problem - in my opinion, this results from a clear misunderstanding of the purpose of strong typing which is the point of the article.

The solution that I wanted to present here is very similar to the one proposed in Xamarin some time ago:

C#
public static readonly BindableProperty SomePropertyProp = BindableProperty.Create<CustomControl, 
                       int>(c => c. SomeProperty, 1);

public int SomeProperty
{
    get { return (int)GetValue(SomePropertyProp); }
    set { SetValue(SomePropertyProp, value); }
}

For unclear reasons, Xamarin withdrew this implementation just after the introduction of the nameof operator. As I mentioned before, the nameof operator removes the problem related to hardcoded property name but the problem related to type safety still exists. If you know the reason for this change, please write it in the comments section.

In this article, I am going to present a similar implementation for WPF and UWP which is used in open source music notation control library Manufaktura.Controls (which was previously described in this article) and a code snippet that makes it easier to use.

How to Use

First of all, you can import a code snippet attached to Manufaktura.Controls.WPF library. It can be found in Snippets folder. To import a snippet in Visual Studio, go to Tools\Code Snippet Manager…\Import… and pick the .snippet file attached to the project. Now you can type “propdpx” and press tab and the following code will appear:

public static readonly DependencyProperty MyPropertyProperty = 
    DependencyPropertyEx.Register<MyDepObject, string>(v => v.MyProperty, default(string), 
    (depObject, oldValue, newValue) => { });

public string MyProperty
{
    get { return (string)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

This code is similar to the one used with Dependency.Register but the type safety is provided by generic type argument. Name safety is provided by lambda (it could be replaced with nameof these days). There is also a default property changed handler:

C#
(depObject, oldValue, newValue) => {
   //Provide implementation here
});

It’s better than old property changed handler because each parameter (dependency object, old value and new value) has the proper type.

How Does It Work

The implementation is simple. It depends on calling DependencyProperty.Register and casting callback arguments to proper types:

C#
public static class DependencyPropertyEx
{
     public delegate void PropertyChangedCallback<TControl, TProperty>
                 (TControl control, TProperty oldValue, TProperty newValue) 
       where TControl : DependencyObject;

     public static DependencyProperty Register<TControl, TProperty>
               (Expression<Func<TControl, TProperty>> property, TProperty defaultValue, 
         PropertyChangedCallback<TControl, TProperty> propertyChangedCallback = null) 
                              where TControl : DependencyObject
     {
          if (propertyChangedCallback == null)
              return DependencyProperty.Register(GetPropertyName(property), typeof(TProperty), 
                     typeof(TControl), new PropertyMetadata(defaultValue));

          return DependencyProperty.Register(GetPropertyName(property), typeof(TProperty), 
              typeof(TControl), new PropertyMetadata(defaultValue, (obj, args) =>
             {
                propertyChangedCallback(obj as TControl, (TProperty)args.OldValue, 
                                               (TProperty)args.NewValue);
             }));
     }

     private static string GetPropertyName<TControl, TProperty>
                            (Expression<Func<TControl, TProperty>> propertyLambda)
     {
          var memberExpression = propertyLambda.Body as MemberExpression;
          if (memberExpression == null) 
              throw new Exception("Lambda expression should be 
                                   in the following format: control => control.Property.");

          return memberExpression.Member.Name;
     }
}

Code Snippet

In order to make inserting dependency properties easier, I created the following snippet:

XML
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Typed dependency property</Title>
      <Author>Manufaktura programów Jacek Salamon</Author>
      <Description>Inserts strongly typed dependency property registration</Description>
      <Shortcut>propdpx</Shortcut>
    </Header>
    <Snippet>
      <Imports>
        <Import>
          <Namespace>System.Windows</Namespace>
        </Import>
        <Import>
          <Namespace>Manufaktura.Controls.WPF.Bindings</Namespace>
        </Import>
      </Imports>
      <Declarations>
        <Literal>
          <ID>PropertyName</ID>
          <ToolTip>Define property name.</ToolTip>
          <Default>MyProperty</Default>
        </Literal>
        <Literal>
          <ID>PropertyType</ID>
          <ToolTip>Define property type.</ToolTip>
          <Default>string</Default>
        </Literal>
        <Literal>
          <ID>PropertyTarget</ID>
          <ToolTip>Define property target.</ToolTip>
          <Default>MyDepObject</Default>
        </Literal>
      </Declarations>
      <Code Language="CSharp">
        <![CDATA[public static readonly DependencyProperty 
               $PropertyName$Property = DependencyPropertyEx.Register<$PropertyTarget$, 
               $PropertyType$>(v => v.$PropertyName$, default($PropertyType$), 
               (depObject, oldValue, newValue) => {});
        public $PropertyType$ $PropertyName$
        {
            get { return ($PropertyType$)GetValue($PropertyName$Property); }
            set { SetValue($PropertyName$Property, value); }
        }]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

Process of creating code snippets in Visual Studio is covered in this article.

Other Interesting Features in Manufaktura.Controls

ResetableLazy

ResetableLazy is just a façade around Lazy class that allows you to reinitialize Lazy object with Reset method:

C#
public class ResetableLazy<T>
{
    private readonly Func<T> valueFactory;
    private Lazy<T> innerLazy;

    public ResetableLazy(Func<T> valueFactory)
    {
        this.valueFactory = valueFactory;
        innerLazy = new Lazy<T>(valueFactory);
    }

    public bool IsValueCreated => innerLazy.IsValueCreated;
    public T Value => innerLazy.Value;

    public void Reset()
    {
        lock (innerLazy)
        {
            innerLazy = new Lazy<T>(valueFactory);
        }
    }

    public override string ToString() => innerLazy.ToString();

    public void Touch()
    {
        var x = Value;
    }
}

There is also a useful Touch method which enables you to initialize value without returning it. It should improve the clarity of code.

Formula Bindings

FormulaBinding is a new way of databinding in XAML frameworks that brings bindings to the simplicity and flexibility of JavaScript frameworks. It is described in this article.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Poland Poland
I graduated from Adam Mickiewicz University in Poznań where I completed a MA degree in computer science (MA thesis: Analysis of Sound of Viola da Gamba and Human Voice and an Attempt of Comparison of Their Timbres Using Various Techniques of Digital Signal Analysis) and a bachelor degree in musicology (BA thesis: Continuity and Transitions in European Music Theory Illustrated by the Example of 3rd part of Zarlino's Institutioni Harmoniche and Bernhard's Tractatus Compositionis Augmentatus). I also graduated from a solo singing class in Fryderyk Chopin Musical School in Poznań. I'm a self-taught composer and a member of informal international group Vox Saeculorum, gathering composers, which common goal is to revive the old (mainly baroque) styles and composing traditions in contemporary written music. I'm the annual participant of International Summer School of Early Music in Lidzbark Warmiński.

Comments and Discussions

 
SuggestionShould point out... Pin
SledgeHammer016-Aug-18 9:25
SledgeHammer016-Aug-18 9:25 
PraiseClean! Pin
spi6-Aug-18 2:25
professionalspi6-Aug-18 2:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.