CLR attributes and dependency attributes
CLR attributes are very familiar to us and can be seen everywhere in DOTNET programming. The simplest and most common property accessors are private members who directly manipulate the class, as follows:
Public class person
{
Private string _ name;
Public string name
{
Get {return _ name ;}
Set {_ name = value ;}
}
}
C #3.0 provides the "Automatic attribute" feature for this common writing method, which facilitates waiting for these lazy coders.
Public class person
{
Public string name {Get; set ;}
}
The two statements are equivalent, and an instance-level private variable must be set up as the persistent storage of the property accessors. This is nothing for non-UI applications. Because first, we generally do not create too many class instances; second, there are usually not many attributes of a class, and adding a few private variables will not increase the burden on the system. However, these two reasons are not true for the UI application.
In many UI applications, we often create many class instances, and thousands of instances are common in the UI system. At the same time, the UI class usually contains a large number of attributes for designers to use, such as the background color, foreground color, Font, and margin. These attributes are maintained by default in most cases, if so many private variables are created for each instance to store the UI attribute values, it will inevitably cause a great waste and the overhead of the system will not be small.
In view of the problems mentioned above, designing an efficient property storage system is very important for the development of UI applications. Therefore, Silverlight introduces "dependencyproperty )".
Replace member variables with key-value pairs for internal attribute Storage
In traditional CLR attributes, a property corresponds to a private variable, and there are so many attributes in the UI element. Creating too many private variables is not a simple task. In addition, most attributes only use the default values. Therefore, Silverlight uses a dictionary-type member variable in each class instance to store the attributes explicitly set by the user (called the local value). The attributes that are not set are not saved. Where is the default value of the attribute stored? Since the default values of each instance are the same (or they are not the default values), they are directly stored in static member variables (static member variables of dependency properties, instead of registering the member variables of the dependent attribute class. This greatly improves the storage efficiency.
In terms of implementation, all the UI elements in Silverlight inherit from dependencyobject. This class encapsulates the storage and access operations on dependency attributes.
Register dependency attributes
Since the dependent attributes are stored in a hash structure such as key-value pairs, different hash keys must be used to obtain values of different attributes, otherwise, the values of other attributes will be read. Therefore, when we register a dependency property with the Silverlight property system, Silverlight returns a unique property identifier object with the dependencyproperty type. In the future, we will use this unique identifier object to access the value of the dependency attribute.
Because this unique identifier is common to all class instances and will not be modified, we usually save it to a static readonly member variable.
The dependencyproperty class provides two methods: one is the register method, which is used to register dependency attributes, and the other is registerattached, which is used to register additional attributes.
Public static dependencyproperty register (
String name,
Type propertytype,
Type ownertype,
Propertymetadata typemetadata
)
The Register Method signature consists of several parts. The name parameter specifies the name used by the dependency attribute. This name is very important. When defining the style and template controls, the value entered by the property attribute of setter is the name used when the dependency attribute is registered. propertytype indicates the actual type of the dependency attribute, and ownertype indicates which class registers the dependency attribute, finally, typemetadata stores metadata about dependency attributes, including the default values used for dependency attributes and notification functions when the attribute values change.
Attribute access
Unlike CLR properties, dependency properties do not directly manipulate private variables, but operate attribute values through the getvalue and setvalue methods.
The following code demonstrates how to set a center dependency attribute for the ball control and read and modify this attribute in the program:
Public class ball: Control
{
Public static readonly dependencyproperty centerproperty =
Dependencyproperty. Register ("center", typeof (point), typeof (ball), null );
}
Public class ballapp
{
Public void rollball (ball)
{
Point curcenter = (point) Ball. getvalue (ball. centerproperty );
Curcenter. x ++;
// Note that the setvalue modification must be called after the operation on the Value Type object takes effect.
Ball. setvalue (ball. centerproperty, curcenter );
}
}
Because the preceding operations on dependency attributes often involve type conversion, It is troublesome. Traditional CLR attributes are as convenient as directly manipulating common variables. Therefore, when designing dependency attributes, they are all packaged using the CLR property, which is called the enhanced CLR property.
Public class ball: Control
{
Public static readonly dependencyproperty centerproperty =
Dependencyproperty. Register ("center", typeof (point), typeof (ball), null );
Public Point Center
{
Get {return (point) getvalue (centerproperty );}
Set {setvalue (centerproperty, value );}
}
}
According to the Conventions, the dependency attribute name is usually followed by a "property" string.
In fact, using CLR to wrap dependency properties is not just for convenience (http://msdn.microsoft.com/en-us/library/cc221408%28VS.95%29.aspx#back_dependency_properties), many tools or subsystems that depend on CLR properties as the basis do not directly access dependency properties, however, dependency properties can only be indirectly accessed through CLR properties. For example, in the preceding example, if we have not set a center CLR attribute, the following XAML compilation will fail, the XAML parser cannot know that the ball class has a center dependency attribute (you can set the center attribute value in the style to compile successfully, because the style is a dynamic search attribute ).
<Ball center = "2"/>
Notification of Change of logic and value of dependency attribute
As mentioned above, dependency attributes have different storage efficiency compared with CLR attributes. In fact, dependency attributes have other practical features.
Value search logic
When obtaining a value, the CLR attribute directly reads the returned value of the member variable, while the dependent attribute obtains the value of the property through the call of the getvalue function. In fact, getvalue does more than simply read the values stored in the dictionary. He also has the logic for finding values. As shown in:
When you call getvalue to read the value of a dependency attribute, the Silverlight property system first checks the animation System for whether the current dependency attribute has an effect on the animation. If yes, returns the animation value. We can also see that dependency attributes are the basis for implementing the animation mechanism of Silverlight. Note: If the animation is stopped and fillbehavior = holdend is not set, Silverlight will not return this animation value.
If the animation value cannot be read, Silverlight will try to read the local value. There are several types of local values. One is the value that you set directly through code or XAML. One is the value obtained through resource binding, and the last is the value obtained through data binding. These are regarded as local values.
<Stackpanel X: Name = "layoutroot">
<Stackpanel. Resources>
<System: String X: Key = "textblockresource"> resource data binding text </system: String>
</Stackpanel. Resources>
<Textblock text = "{binding source = {staticresource textblockresource}"/>
<Textblock X: Name = "databindingelement" text = "{binding elementname}"/>
</Stackpanel>
If it still cannot be read, continue to try to read the value set in the control template and style.
If all these values fail to be read, The Silverlight property system returns the default value of the Dependency Property. When registering a dependency property, you can pass in a propertymetadata object that contains the default value and value change notification callback function for this dependency property. If the default value is not input during registration, null is returned for the reference type's dependency attribute. For strings, String. Empty is returned. For value types, an instance initialized with the default value is returned.
Pay special attention to the collection type. Because the default value passed in through propertymetadata is shared by all class instances, you must explicitly pass in the collection instance in the class constructor.
Public class gamoom: Control
{
Public list <ball> bils
{
Get {return (list <ball>) getvalue (ballsproperty );}
Set {setvalue (ballsproperty, value );}
}
Public static readonly dependencyproperty ballsproperty =
Dependencyproperty. Register ("bils", typeof (list <ball>), typeof (gamoom), null );
Public gamoom ()
{
Bils = new list <ball> ();
}
}
It may be because the dependency attribute of Silverlight needs to read values from multiple places when obtaining values, rather than directly reading values from member variables like the CLR attribute, so it is called the "dependency" attribute.
Value Change Notification
It is no stranger to notify us of changes to property values. When we implement it in DOTNET, we generally implement the inotifypropertychanged interface for classes. In the UI system, value change notifications are frequently used. Once the data source changes, the values of all corresponding UI elements must be adjusted accordingly. The dependency attribute of Silverlight provides built-in support for this feature. As long as you use the dependency attribute when binding, when the dependency attribute value changes, the values of all bound locations are updated synchronously. In addition, the dependency property also provides a value change notification function (passed through propertymetadata when registering the Dependency Property). You can customize a function to control the operations to be performed when the value is changed.
Public class ball: Control
{
Public static readonly dependencyproperty centerproperty =
Dependencyproperty. Register ("center", typeof (point), typeof (ball), new propertymetadata (oncenterchanged ));
Public Point Center
{
Get {return (point) getvalue (centerproperty );}
Set {setvalue (centerproperty, value );}
}
Private Static void oncenterchanged (dependencyobject D, dependencypropertychangedeventargs E)
{
Ball ball = D as ball;
// Obtain a new Ballon.
Point newcenter = (point) E. newvalue;
//...
}
}
Attached Property -- Global Dependency Property
The dependency attribute mentioned earlier serves a class like the CLR attribute. It only makes attributes more efficient to store and more powerful to use. There is also a special dependency attribute in Silverlight. This dependency attribute serves not only a specific class, but also the global, which is an additional attribute. It can also be seen from the name that the additional property is registered in a class and can then be used by other classes.
Under what circumstances do I need to use additional attributes? Take the zindex attribute of the canvas class as an example.
When adding child widgets to a container class, consider which widget is placed at the top and which is placed below.
So how does the container class know the stacked order of child controls? The brainless design is to add a zindex attribute for all controls. The attribute values represent the stacked order. But the consequence is that if my control does not participate in the layout, it will be a waste of such attributes. Therefore, the ideal design is that this attribute is available when this attribute is used, and there is no burden on this attribute when it is not needed. The append attribute is used to solve this problem. When a control requires an attribute, We can attach this attribute to the control class.
Registering an additional property is similar to a dependency property, except that the function name is registerattached. This is not much to say.
When should dependency attributes be used?
Since dependency attributes are so efficient and powerful, should we keep the habit of using dependency attributes? In fact, any benefit comes at a cost. The dependency attribute of Silverlight is not as efficient as directly accessing member variables. Therefore, we recommend that you use traditional CLR attributes for simple and frequently accessed attributes.