Data See, Data Do

Mike Hillberg's Blog on Wpf and Silverlight

Why is it called a "DependencyProperty"?

Why is it called a "DependencyProperty"?

Rate This
  • Comments 7

When we create new classes and members we spend a lot of time and effort to make them as usable, understandable, and discoverable as possible.  We follow the .Net Design Guidelines in general, and in particular we constantly look at how this new class relates to other classes, future plans, etc.

 

So we went through all that when naming DependencyProperty and DependencyObject.  All told we probably spent hours just on the name.  What dependency properties (DPs) boil down to in the end is property calculation and dependency tracking.  Property calculation isn’t terribly distinctive, lots of properties do that, so the essence of a DP is really the dependency tracking, thus “Dependency” properties.

 

 

Here’s an example of that, actually one piece of sample code that shows several examples of dependency tracking:

 

<StackPanel TextBlock.FontSize="22" DataContext="Hello, world">

    <StackPanel.Resources>

 

        <Style TargetType="TextBlock">

            <Setter Property="FontWeight" Value="Bold" />

            <Style.Triggers>

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

                    <Setter Property="Background" Value="Red" />

                </Trigger>

            </Style.Triggers>

        </Style>

 

    </StackPanel.Resources>

 

    <TextBlock Text="{Binding}" />

   

</StackPanel>

 

 

The properties of this TextBlock have quite a few dependencies:

·         TextBlock.Text is dependent on the Binding, and the Binding is dependent on the DataContext in this case.  The DataContext is inheriting down from the StackPanel, so the Text property is therefore also dependent on the shape of the tree; if the TextBlock is removed from the StackPanel, it’s Text property will update.

·         TextBlock.FontSize is dependent on the tree as well.  Here, you can see it’s inheriting from the StackPanel.

·         All of the TextBlock properties depend on TextBlock.Style.  For example, here TextBlock.FontWeight is coming from the Style.

·         Similarly, TextBlock.Background depends on the Style, but here it’s being set in trigger.  So TextBlock.Background in this case also depends on TextBlock.IsMouseOver.

 

Sometimes, if you write your own DP, you need to help with the dependency tracking.  You do this by calling InvalidateProperty when a property needs to be re-calculated, usually because you reference it in a CoerceValueCallback.

 

For example, here’s a DP Foo and a companion (read-only) DP named FooPlus1.  FooPlus1 just has a CoerceValueCallback that calculates “Foo+1”.  Foo therefore has a PropertyChangedCallback that invalidates FooPlus1 whenever Foo changes.

 

//

// Foo property

//

 

public int Foo

{

    get { return (int)GetValue(FooProperty); }

    set { SetValue(FooProperty, value); }

}

 

public static readonly DependencyProperty FooProperty =

    DependencyProperty.Register("Foo", typeof(int), typeof(Window1),

                                new PropertyMetadata(FooChangedCallback));

 

static void FooChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs args)

{

    // Whenever Foo changes, we need to invalidate FooPlus1, so that

    // the DependencyProperty system knows to update it (call its

    // CoerceValueCallback again).

    (d as Window1).InvalidateProperty(Window1.FooPlus1Property);

}

 

 

//

// FooPlus1 Property

//

 

public int FooPlus1

{

    get { return (int)GetValue(FooPlus1Property); }

}

 

static readonly DependencyPropertyKey FooPlus1PropertyKey =

    DependencyProperty.RegisterReadOnly("FooPlus1", typeof(int), typeof(Window1),

                                new PropertyMetadata(0, null, CoerceFooPlus1Callback));

static readonly DependencyProperty FooPlus1Property = FooPlus1PropertyKey.DependencyProperty;

 

static object CoerceFooPlus1Callback(DependencyObject d, object baseValue)

{

    return (d as Window1).Foo + 1;

}

 

 

  • I don't think that this is a good example of how to show the use of InvalidateProperty and CoerceValueCallback.  The reason for this is that FooPlus1 is on a different type, and it requires the dependency property Foo to be aware of the properties that are dependent on it.  This is like asking a base class to know about all the classes it will derive from, it won't.

    For this example, the dependency properties should be on the same type, as it would make sense that a type would know about other members on itself.

    For the InvalidateProperty example, I would think that getting the DependencyPropertyDescriptor instance for the base dependency property and calling AddValueChanged with an event handler is a better idea.

    That event handler would then call InvalidateProperty with the new read only property and then the CoerceValueCallback would be called to update the read only value.

    This would allow for the case where you have unknown types that want to use the dependency property on another type as a base for a read only property of their own.

  • In this case, though, the properties are both on the same type (Foo and FooPlus1 are both on Window1).

    For going across types, good point that you can use DependencyPropertyDescriptor to watch for changes on the source and call InvalidateProperty on the target.  Another way to watch for changes on the base class is to override the OnPropertyChanged virtual and look for the source DP (just be careful to also call base.OnPropertyChanged).

  • Thank you for submitting this cool story - Trackback from DotNetShoutout

  • @We follow the .Net Design Guidelines in general, and in particular we constantly look at how this new class relates to other classes, future plans, etc.

    Maybe... but I remember Sam Ruby and Don Box back in 2005 wondering why the notion of attached properties was implemented four times in .NET 3.0.

    With people wanting to integrate WCF/WF/WPF, the "Seperate House/Separate Bed Model" of .NET 3.0's WPF DependencyObject/DependencyProperty versus WCF's DependencyObject/DependencyProperty falls on its face and just makes XAML awkward.

    The only excuse for this "mistake" is cross-assembly security context performance overhead.  I've only been programming .NET for 11 months now, so I'm not enough of an expert to understand how that affects MSFT's API designs... and I don't recall it being mentioned Cwalina's guidelines.

    I know...I have really high expectations for API design, but that's the hallmark of a great programmer.

    I remember writing my first COM call at age 11, doing ATL, MFC, Win32, etc.  All those systems had naturally high failure rates, due to the amount of boilerplate required to accomplish a task.

    WPF reduces failure rates with dependency properties.  That's the key point.  Who cares what you call it as long as its sane and consistent - the Big Picture is it smokes the bugs out.  Understanding how to leverage DependencyProperty to smoke the bugs is IMHO what you should focus on explaining.

    ...Why isn't there a static analysis check to make sure programmers call InvalidateProperty if they've defined CoerceValueCallback...?

    Again, smoke the bugs out.

  • Also, nobody says it, but a DependencyProperty is fundamentally supposed to be used to implement *reactive* components.

    Compare this to properties in the ASP.NET object model, which are *transformational* components.

    In my experience, It is much easier to trace control flow in a reactive system than a transformational system.  Moreover, a reactive system can more easily express Charles Petzold's Ultimate Hook Pattern as described in his book Programming Windows 95. http://www.state-machine.com/devzone/Pattern_UltimateHook.pdf  This is a huge win for declarative customization, and is a pattern I use in other contexts when client's need the ability to customize things.

  • It would be nice if you can provide the sample project file.

  • Good example to understand the very basic fundamental why dependency props are called so...

Page 1 of 1 (7 items)
Leave a Comment
  • Please add 3 and 8 and type the answer here:
  • Post