WPF-Property Systems (3 of 4)

Source: Internet
Author: User

dependency property Metadata

In the previous chapters, we have covered two components in WPF dependency property metadata: The Coercevaluecallback callback and the Propertychangedcallback. In this section, we will explain the other metadata attributes.

First let's look at the metadata support for default values. In the metadata constructor, the software developer can specify the default value of the dependency property through its defaultvalue parameter. If the default value for a dependency property is not specified in the metadata, the WPF property system automatically assigns a default value to the dependency property based on the type of the dependency attribute:

Private StaticDependencyProperty Registercommon (...., PropertyMetadata defaultmetadata, ...). {Fromnamekey key=NewFromnamekey (name, ownertype); ...if(!Defaultmetadata.defaultvaluewasset ()) {        //slightly changed, but the specific logic is to detect whether the default value is set, and if not, then//Autogeneratedefaultvalue () generates a default value. It internally calls the//activator.createinstance () functionDefaultmetadata.defaultvalue =Autogeneratedefaultvalue (PropertyType);    } validatemetadatadefaultvalue (Defaultmetadata, PropertyType, name, Validatevaluecallback); ...returnDP;}

The above code generates default values for the metadata through Autogeneratedefaultvalue (). In the next logic, WPF detects the default values. These detections exist in the setup logic of the DefaultValue property and in calls to the Validatemetadatadefaultvalue () function listed in the preceding code. The setting logic for the DefaultValue property is as follows:

 Public Objectdefaultvalue{Set    {        if( This. Sealed) {...//throws metadata that has been used, cannot change the exception        }        if(Value = =dependencyproperty.unsetvalue) {...//the default value for throwing metadata cannot be an Dependencyproperty.unsetvalue exception        }         This. _defaultvalue =value;  This.    SetModified (Metadataflags.defaultvaluemodifiedid); }}

First, in the settings of the DefaultValue property, it first detects whether the current dependency property already has an instance through the IsSealed property. If it is, then the defaultvalue of the metadata will not be changed. Next, it detects if the setting for the default value is unset. If so, then the WPF property system will throw exceptions that cannot be unsetvalue by default values.

The first of these tests is a problem that many people encounter when manipulating the default values of metadata. After a dependency property is created, a large number of information about the metadata that it passes in will not be changed. If you need to change the information that is recorded in the metadata in some circumstances, such as providing a new default value in a derived class, we need to recreate the metadata and complete the metadata changes through the interfaces provided by the WPF property system such as OverrideMetadata ().

The second test is used to ensure that the Unsetvalue value is not recorded in the WPF property system. Unsetvalue This value is a special value that is defined by the WPF property system to indicate an invalid assignment to a dependency property. Within a multitude of mechanisms that can assign values to dependency properties, such as bindings, if these functions return Unsetvalue, the assignment to that dependency property is ignored.

In the Validatemetadatadefaultvalue () function, it performs more testing:

Private Static voidValidatedefaultvaluecommon (...) {    if(!Isvalidtype (DefaultValue, PropertyType)) {...//throws an exception with a default value that does not match the type of the dependency property    }    if(DefaultValue isExpression) {...//throwing a default value cannot be an expression exception    }    if(checkthreadaffinity) {...//if the default value is a derived class of the Dispatcherobject class, then it needs to be a freezable class//(at least now), and it can freeze the instance through a function call freeze () to make the//dependency properties can be used in multiple threads. Otherwise, WPF throws a default value that requires an exception that can be frozen    }    if((Validatevaluecallback! =NULL) &&!Validatevaluecallback (DefaultValue)) {...//exception with illegal default value thrown without validation    }}

The above function shows a series of tests performed to set the dependency property metadata, as well as a list of causes for errors when using dependency properties:

    1. The WPF property system will give an error when the provided default value does not match the type of the dependency property. This is a very obvious mistake, so it doesn't happen very often.
    2. If the default value of a dependency property is an expression, the WPF property system will also have an error. In fact, WPF does not allow the registration of a dependency property of an expression type. This is because
    3. If the type of the dependency property derives from the DependencyObject class, it needs to ensure that the default value is an instance of the Freezable type that can be frozen. This is because the use of this dependency property can be done in more than one thread, and only after it has been frozen can a derived class of the DependencyObject class be used across threads. This constraint is not explicitly noted in MSDN, so this is a very common problem when writing custom WPF dependency properties, but it is very difficult to solve in the first encounter.
    4. Finally, if the default value of a dependency property does not conform to the validation logic indicated by the Validatevaluecallback function, the WPF property system also throws an exception. This problem usually occurs when a dependency property is overridden using a function such as OverrideMetadata (). Since the Validatevaluecallback function provided by each base class is still valid at the time of property rewriting, the newly supplied default value needs to satisfy the validation logic provided by all previously provided validatevaluecallback functions.

The next thing to look at is the individual flag bits that are identified in the metadata.

In the usual WPF programming process, the most commonly used flags are affectsmeasure, Affectsarrange, and Affectsrender. The AFFECTSMEASURE flag bit indicates that a change to the property will cause the UI component containing the property to change in demand for the space provided by the parent element, thereby re-triggering the Measure-arrange layout calculation of the UI. One of the most obvious examples is the Width property.

It is important to note that the AFFECTSMEASURE flag bit is only used to indicate that the UI consists of "a change in the demand for the space provided by the parent element", and does not include cases where the child element layout changes affect the parent element. As the width of a child element in a longitudinal stackpanel changes, so does the breadth of the StackPanel. This is because we cannot assume that the composition of the UI that contains the current dependency property will be added to which parent element. These parent elements can be stackpanel, at which time the change in width causes a change in the width of the StackPanel, and the parent element can also be a canvas,width change without causing the canvas to change. Therefore, how to change the layout of the parent element based on the child element dependency property value cannot be specified by the flag bit of the dependency property.

How does the StackPanel change in width when it changes? This is actually done through the member functions onchilddesiredsizechanged () provided by the UIElement and Icontenthost interfaces. UIElement provides the default execution logic for this function. The default execution logic determines whether a control needs to invoke its own Measure-arrange layout logic, depending on the current situation. So unless you want to customize a new control that can contain UIElement, you don't need to care about it.

The properties that have the AFFECTSARRANGE flag bit change do not cause the UI composition to change the space requirement, but it can affect the placement of the UI in that space. At this point it will trigger the UI composed of the Arrange layout u calculation. The Alignment property is set with this flag bit.

The most lightweight flag bit is affectsrender. The flag bit indicates that the current UI composition needs to be redrawn itself. The flag bit is set by the property background we use.

If the AFFECTSMEASURE flag bit is indicated in the registration of a dependency property, then it is no longer necessary to indicate the AFFECTSARRANGE flag bit. This is because, in general, measure layout calculations are often followed by a arrange layout calculation. But when the UI composition needs to be redrawn itself, the software developer still needs to indicate the AFFECTSRENDER flag bit.

Take the Text property of TextBlock as an example:

 public  static  readonly  DependencyProperty textproperty = Dependencyproperty.register (   text   ", 
typeof (string ), typeof (TextBlock), new Frameworkpropertymetadata (string | Frameworkpropertymetadataoptions.affectsmeasure, ...));

Can imagine. When a TextBlock's Text property changes, the space it needs to display the text may change, and its appearance needs to be refreshed. When the required space changes, it does not know whether the space provided by the previous parent UI element is still sufficient, so it needs to request WPF to re-measure and allocate the space it contains. This is why the AFFECTSMEASURE flag bit is indicated in the metadata of the Text property. Also, because the layout calculation process does not include a refresh of UI elements, the software developer also needs to indicate the AFFECTSRENDER flag bit in the metadata.

In addition to these three properties, WPF allows you to use the flag bits affectsparentmeasure and affectsparentarrange in the metadata for dependency properties. After these flags are marked, changes to the dependency property cause the parent element of the type that contains the property to perform measure and arrange operations.

Many people have this concern: in a piece of code, WPF often sets a set of properties to complete functionality. There may be more than one attribute that adds a flag bit such as affectsmeasure. So when you set these properties, does the frequent changes to the layout affect the execution performance of the program? There is actually no need to worry about this problem. WPF uses several methods to avoid performance degradation.

The first is the combination of the flag bit and the BeginInvoke () function. When a dependency property is set that indicates the AFFECTSMEASURE flag bit, WPF internally sets the flag bit to true for whether the measure process needs to be re-executed and adds a request to a queue to execute the measure process. Next, it registers a callback that handles the re-layout with the BeginInvoke () function to the WPF system internally. When other dependency properties change, WPF will no longer insert callbacks that handle re-layout because the flag bits of the measure process have been set. That is, the setting of many dependency properties that indicate the AFFECTSMEASURE flag bit in the current code will only trigger the execution of the measure process once.

During this execution, WPF needs to process all the layout refresh requests. Here, the second method is used to improve performance: first, the top-level UI elements are refreshed in a layout, so that their child elements can be refreshed during their layout calculations. After the child element has been refreshed, the corresponding request that was added to the request queue is removed. In this way, WPF improves performance by merging layout refresh requests made by many different UI elements into only a few layout refresh requests.

Finally, WPF also records in each UIElement element the flag bits that currently require a re-layout calculation. In the case where the flag bit is false, the layout calculation of these elements will no longer be raised.

In summary, the various layout flags supported by WPF metadata do not significantly degrade program performance. Therefore, when registering a dependency property, you can decide whether you want to use the flag bit based on the actual behavior of the dependency property.

Another type of metadata option is inheritance of dependency property values. This type of metadata option includes inherits and Overridesinheritancebehavior. When a dependency property is registered with the inherits flag bit, the read of the dependency property in any child element causes it to look up from the current element in the WPF element tree, starting at the same time, until it finds an element that performs an assignment to that element. or the default value of the dependency property is used when it reaches the root of the search tree and is not found.

One of the most common dependency properties that use the inheritance feature is DataContext. This property is used as the default data source for data binding. Because it uses the inherits flag bit at registration time, each child element of the element that is marked with the DataContext attribute will be the default data source for the binding, unless the child element overrides the value recorded by the parent element by setting the value of the DataContext property.

Support for dependency property inheritance is straightforward: in the Getvalueentry () function called by the GetValue () function, it first determines whether the current instance sets the dependency property. If so, the value of the dependency property will be returned, otherwise the property system will look up along the inheritance tree:

 if   (metadata. isinherited) {DependencyObject inheritanceparent  = this  .    Inheritanceparent;  if  (inheritanceparent! = null  Span style= "color: #000000;" ) {entryindex  = Inheritanceparent.lookupentry (DP.        Globalindex);  if   (Entryindex.found) {entry  = Inheritanceparent.geteffectivevalue (Entryindex, DP, requests &            Requestflags.deferredreferences); Entry.        basevaluesourceinternal  = basevaluesourceinternal.inherited; }    }}

WPF does not search up the visual tree as part of the search process. But if a software developer wants a property to inherit along the visual tree, the software developer needs to add a flag bit overridesinheritancebehavior to the metadata option.

The remaining metadata options are simpler: The notdatabindable metadata option is used to specify that a dependency property does not support data binding. The Bindstwowaybydefault metadata option is used to specify that bindings that are used on a dependency property will use the TwoWay mode by default. The journal flag bit is used to specify that the value of the dependency property is serialized during the navigation process, so that the input provided by the user is still present when the page jumps back.

dependency property Precedence

In WPF, a software developer can assign a value to a dependency property in several ways, such as assigning a value to a dependency property through a style, and the declaration of the control itself is also assigned a property:

<Stylex:key="{x:type Button}"TargetType="{x:type Button}">    <Setter Property= "Background"Value= "Red"/></Style><ButtonBackground= "Green">I am Green</Button>

In this case, WPF can only select one of these assignments as the value of the property. Because a style is specified for a common appearance of a type of control, a control that uses these general-purpose skins may specify its own appearance, so in the example above, the style's designation of the background property will be overridden by the property assignment within the button. It will eventually appear green. It can be said that the property value setting in WPF follows the more special, the more temporary the property setting has the higher priority this feature.

How does the property system complete support for dependency property precedence? All the secrets are hidden in the functionality provided by the Effectivevalueentry class:

Internal struct effectivevalueentry{    private object _value;    Private short _propertyindex;    Private System.Windows.FullValueSource _source;    Internal Effectivevalueentry (DependencyProperty dp,        basevaluesourceinternal valuesource)    {        this._ Propertyindex = (short) DP. Globalindex;        This._value = Dependencyproperty.unsetvalue;        This._source = (System.Windows.FullValueSource) valuesource;    }    ......}

Let's start with a look at how the type stores the data. This type records data _value the member data of an object type. In a simpler case, the data will record the actual value of the dependency property. But things don't always seem so good. The dependency property supports multiple methods of operation, and some changes to its value are only temporary, so _value will record a numeric value of type Modifiedvalue if the value of the dependency property is more complex. The definition of the type is as follows:

 internal  class   modifiedvalue{ private  object  Span style= "color: #000000;"    > _animatedvalue;  private  object   _basevalue;  private  object   _coercedvalue;  private  object   _expressionvalue; ...}  

What can you see from the data structure above? That is support for valid values and how to support animation features. What is a valid value? In WPF, we can set a value for a dependency property. However, the value may be constrained to another value due to limitations of other properties. For example, if you set the Min and Max properties of a Rangecontrol to 0 and 100 respectively, the value setting will not exceed the constraints of these two values. If a software developer tries to set the Value property to 200, the value is forced to 100 because it exceeds the maximum limit. However, when you change the Max property to 300, the value of the Value property reverts to 200.

Why provide valid values for this function? This is because, in a property-driven system such as WPF, the setting of property values should not be affected by the order in which the properties are set: Assume that the default value for the Max property is 100, and that the value to be set for the 300,value property is the target value of 200. The property is set in the order that the Value property is set first, and then the Max property is set. In the absence of support for a valid value feature, the Value property will be coerced to 100 by the default value of the Max property, not 200 of the actual valid in this case.

To solve this problem, WPF introduces type Modifiedvalue. This type is used to record the property values that the user wishes to set, as well as the temporary values that are set due to other constraints or feature support. In cases where the constraint has changed or the temporary value is no longer valid, WPF can recalculate the value it needs to display based on the target value recorded by _basevalue.

When the Coercevaluecallback or animation needs to set these properties, it will record the values in the structure:

 internal  void  Setcoercedvalue ( Span style= "color: #0000ff;" >object  value, object   BaseValue,  bool   Skipbasevaluechecks) { //  Ensuremodifiedvalue () A function call creates a Modifiedvalue structure  this . Ensuremodifiedvalue ().    Coercedvalue = value;  this .    iscoerced = true   this . Isdeferredreference = false   

But the property system provides the priority of the 10 Yu Pe property, and here only provides support for attribute constraints with the highest precedence and animations, and how are other properties supported? Support for other priorities is indicated by the Basevaluesourceinternal enumeration:

Internal enumBasevaluesourceinternal: Short{Default=1, Implicitreference=8, inherited=2, Local= One, Parenttemplate=9, Parenttemplatetrigger=Ten, Style=5, Styletrigger=7, Templatetrigger=6, Themestyle=3, Themestyletrigger=4, Unknown=0}

In a effectivevalueentry, it records the current value and records the source of the current value through the enumeration type above basevaluesourceinternal. When a mechanism attempts to assign a dependency property, if the mechanism has a higher priority, the WPF property system updates the value recorded in Effectivevalueentry and the source of merit, thus completing the support for setting the priority of the dependency property.

But why is support for dependency property precedence broken into two different ways? This is because WPF supports temporary changes to the dependency property value of the system and the ability to recover the original value of the dependency property after the change has ended. For features such as style, theme, and so on, because their set logic on dependency properties is fixed, assigning values to dependency properties on those levels no longer requires the ability to restore the dependency property's original value as an animation.

OK, let's get here first. In the next article, we'll cover actions such as overriding these dependency properties.

Reprint please specify the original address: http://www.cnblogs.com/loveis715/p/4343364.html

Commercial reprint please contact me in advance:[email protected], I will only ask to add the author name and blog Home link.

WPF-Property Systems (3 of 4)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.