Introduction
WPF and dependency property are indispensable parts.
Without dependency property and MVVM, you are almost nowhere in WPF development. But improper use of dependency property can create a nightmare.
Let’s take the following example.
Suppose we have created a custom media player control and we want to bind the Source property from XAML. It is to be noted that once the source property is set, we need to do some additional related thing.
Here is the initial code snippet of Source Dependency Property:
public Uri Source
{
get { return (Uri)GetValue(SourceProperty); }
set
{
SetValue(SourceProperty, value);
if (MediaElement != null)
{
SetMediaElementSource();
}
}
}
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof (Uri), typeof (MediaPlayer));
And here is our XAML:
<media:MediaPlayer Source="{Binding Source}"
x:Name="MainPlayer"></media:MediaPlayer>
Now if we run our application, we shall see that none of the code written after SetValue(SourceProperty, value);
will execute. The reason behind this is that if we set the property from XAML, the SetValue()
method is called directly. So our player will not work if we bind the source property from XAML! We can easily overcome this problem if we use the following overloaded version of DependencyProperty.Register
method.
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(Uri),
typeof(MediaPlayer), new FrameworkPropertyMetadata(OnMediaSourceChanged));
private static void OnMediaSourceChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var player = d as MediaPlayer;
if (player != null) player.Source = e.NewValue as Uri;
}
As you can see, the new FrameworkPropertyMetaData
is nothing but a method which is fired once the Source
property is changed irrespective of whether it is changed from XAML or code behind. Since we are setting the Source
property in OnMediaSourceChanged
method so now the Set
method of source will execute with all business logic! Seems our problem is solved!! But the fact is it is not solved yet rather it gives birth to another problem. If we set the Source
property from the code behind, the Set
property of Source
along with related code written in Set
method will be executed twice, one for setting it from code behind and another from OnMediaSourceChanged
method. The solution to this problem is, keep the Set
property free from any business logic (or any code!). Rather whatever code is needed to execute, keep those in some separate method and call that method from OnMediaSourceChanged
. Here comes the code that is fully compatible with XAML binding and code behind.
public Uri Source
{
get { return (Uri)GetValue(SourceProperty); }
set
{
SetValue(SourceProperty, value);
}
}
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(Uri), typeof(MediaPlayer),
new FrameworkPropertyMetadata(OnMediaSourceChanged));
private static void OnMediaSourceChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var player = d as MediaPlayer;
if (player != null)
{
player.Source = e.NewValue as Uri;
player.PostSourceSetupWork();
}
}
public void PostSourceSetupWork()
{
if (MediaElement != null)
{
mediaElement.Pause();
SetMediaElementSource();
}
}
History
- 13th December, 2010: Initial post