Demystifying Dependency Properties

爱⌒轻易说出口 提交于 2019-12-03 03:49:23

问题


I have read about dependency properties a lot on SO and other sites. But, really haven't found a great explanation and am still confused. I use both SL and WPF. Are they different in SL and WPF, in terms of implementation? Why do we really need them? Are they static means that their values are shared? Why was the reason MS introduced dependency properties?

Bounty: I am looking for a more thorough, complete answer.


回答1:


The answer is in the name itself, though the word "dependency" is so fraught with meaning in software development that it's not especially clear what that means.

A dependency property is a property on one object whose value depends on some other object. So, for instance:

  • The value of the FontFamily property of a TextBox can (and usually does) depend on the FontFamily property of its container. If you change the property on the container, the value on the TextBox changes.

  • The value of the Text property of a TextBox can depend on a bound data source. When the value of the bound property changes, the value of the Text property changes.

  • The value of the Opacity property of a Label can depend on an animation storyboard, in the common scenario where you've set up a UI element to fade in or fade out in response to some event.

  • The value of all kinds of properties on any UI element can depend on the style that you've applied to them.

The central concept behind dependency is that the thing that's depending should get the property value from the thing it's depending on. This is why a dependency property is implemented as a CLR property whose getter calls a method.

When the TextBox, or the thing that's rendering it, needs to know what its FontFamily is, the FontFamily getter calls the GetValue method. That method finds the value from the container, or animation, or binding, or whatever.

There's a lot of complexity in that method. If the value's inherited, for instance, it works in a way that's pretty analogous to how WPF finds styles in a resource dictionary: it looks in a local dictionary to find the value, and if there's no entry it looks in its parent's dictionary, and so on all the way up until it finds a value or reaches the top of the hierarchy, in which case it uses a default value.

If you look at the implementation of dependency properties, that's what you'll find. A dependency object has a dictionary that may or may not contain an entry for a given property. The GetValue method gets values from that dictionary (which is how objects with dependency properties can have local values that override what they're inheriting from), and if it doesn't find the value, it uses the metainformation about the property to figure out where it should look.

Since that metainformation is the same for every object in the class (i.e., TextBox.Text works the same for every TextBox), the dictionary it's stored in is a static property of the class.

So when you see code like this:

static Button()
{
   // Register the property
   Button.IsDefaultProperty = 
     DependencyProperty.Register("IsDefault",
     typeof(bool), typeof(Button),
     new FrameworkPropertyMetadata(false,
        new PropertyChangedCallback(OnIsDefaultChanged)));
}

what's happening is that the metainformation that defines the IsDefault property on all Button objects is being added to that dictionary. And when you see this:

public bool IsDefault
{
  get { return (bool)GetValue(Button.IsDefaultProperty); }
  set { SetValue(Button.IsDefaultProperty, value); }
}

what you're seeing is the getter method that looks up the property's value (from the local dictionary, the parent object, or whatever) based on that metainformation.

Remember how I said that the first place the getter looks to find a property's value is in the object's local dictionary? The SetValue method in the setter is how that entry gets added to the dictionary (if it's ever called, which it will only be if you're overriding the dependency by explicitly setting the property, i.e. saying "I want this TextBox to display text in Consolas irrespective of what the other controls in the window are using.").

A hugely significant benefit that we get from this kind of apparently-complex system is that dependency properties on objects only consume memory if they're set. If I create 10,000 TextBox controls and add them to a Window, not one of them actually contains a reference to a FontFamily object. That's 10,000 object references that I'm not allocating memory for, and that the garbage collector isn't checking. In fact, if a TextBox has 100 dependency properties (and it does, just about), whenever you create a TextBox, that's 100 backing fields you aren't allocating memory for. Dependency properties only consume memory if you explicitly set them. Since the vast majority of properties on UI objects never get explictly set, these are fantastic savings.




回答2:


Other people have provided explanations of dependency properties. I'll try to provide some context around their design. (It helped me understand why these bizarre creations called dependency properties exist in the first place.) There were a few reasons that the designers of WPF added dependency properties rather than using normal properties...

  1. Being able to add a value for a property which isn't applicable to the current element, but is applicable to its children. For example, setting a font on a container and having it cascade down to any contained text elements.
  2. The cost in terms of size for unused properties. Taking the font example above... On a well-designed page only a few elements will have their font modified - likely a few top-level containers. If we stored Font as a regular C# property, each object would need a 32/64-bit pointer to a font object with most of them null or with a default value. Magnify this by the number of XAML properties and the typical number of elements on a page and you end up requiring a huge amount of space for storing nothing or default values. Compare this with dependency properties where values only occupy space if they are set. (I haven't looked at the internal implementation, but I imagine that GetValue(propName) and SetValue(propName) store the values in some sort of per-object/per-propety hashtable.)

It would be nice if Microsoft provided a nicer syntax for creating new dependency properties by hiding away the repetitive code with compiler magic, much as they do for iterators. Maybe in C# 6... :)




回答3:


  1. Are they different in SL and WPF?
    No

  2. Why do we really need them?
    The short answer is, we need them for data binding and styling and that's why they were added by MS.

  3. Are they static means that their values are shared?
    The static part is the name of the property, so it can be retrieved with GetValue(propName) and be set with SetValue(propName, value)




回答4:


Take a look at the Dependency Properties Overview on MSDN (or, the Silverlight version).

(Silverlight/WPF) provides a set of services that can be used to extend the functionality of a CLR property. Collectively, these services are typically referred to as the (Silverlight/WPF) property system. A property that is backed by the (Silverlight/WPF) property system is known as a dependency property.

For differences between the Silverlight and WPF property systems, see here.

A couple of reasons why the Dependency Property system and connecting a property with the Silverlight/WPF type system is important are as follows (see here for more detailed explanations):

  • Allows a property to be settable in a style
  • Allows a property to support data binding
  • Allows a property to be animatable
  • Allows properties of a custom control to receive designer support in Visual Studio

Note that a Dependency property is typically backed by a CLR property, meaning that you can interact with it in code the same way you would with a CLR property.




回答5:


WPF is designed around property based architecture and it required a very powerful property system supporting -

  1. Various providers for its value i.e. its value can be changed at run time by various providers like triggers, styles, animation, themes etc.(Dependency Property Value Precedence)
  2. XAML (Expressions support).
  3. Memory efficient (their static nature).
  4. Change notifications
  5. Property value inheritence (Attached properties).

articles which I have found very useful -

Overview of dependency properties in WPF: http://joshsmithonwpf.wordpress.com/2007/06/22/overview-of-dependency-properties-in-wpf/

Inside Dependency Properties : http://www.i-programmer.info/programming/wpf-workings/443-inside-dependency-properties-.html

WPF: The Static Nature of Dependency Properties: http://dotnetslackers.com/Debugger/re-126399_WPF_The_Static_Nature_of_Dependency_Properties.aspx




回答6:


Most of the questions you posed have been answered by others, but I'll give you my simple take on them.

First, they are different between Silverlight and WPF, with the WPF dependency properties having more capabilities. However, excluding these additional capabilities in WPF, they are fundamentally the same.

The big question is "when" should you use them (the "why" has mostly already been answered). You essentially need them when you need to assign a markup extension to them in XAML (such as a binding expression), which will then be resolved/evaluated at run time. For most of the time, this will only be necessary when writing custom controls that expose properties.

i.e. You drop a custom control on a surface in XAML, and assign a binding expression to one of its properties.

I see some people using them everywhere, thinking that they are required when doing Silverlight/WPF development, but this isn't the case. Different people have different lines that they draw in the sand, but I say only use them when necessary - which almost 100% of the time is within custom controls.

Blantant self promotion: I have deeper (Silverlight specific) discussion on this topic in chapter 10 of my book Pro Business Applications in Silverlight 4.




回答7:


To add to what's already been covered in other answers:

Conceptually at their core Dependency Properties are a system to allow querying to obtain a value based on a list of different sources rather than returning a single stored value. There are a lot of ramifications to this as listed in many of the other answers but what's actually happening results in something that behaves like this when getting a value (massively simplified of course):

private string _coerced;
private string _animated;
private string _local;
private string _triggered;
private string _styled;
private string _inherited;
private string _default;

public string MyDP
{
    get
    {
        return _coerced ?? _animated ?? _local ?? _triggered ?? _styled ?? _inherited ?? _default;
    }
}


来源:https://stackoverflow.com/questions/4347135/demystifying-dependency-properties

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!