종속성 속성(Dependency Properties) 프로그래밍/WPF2016. 10. 24. 11:04
Dependency Properties Overview
https://msdn.microsoft.com/ko-kr/library/ms752914(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/ms752914(v=vs.110).aspx
Windows Presentation Foundation (WPF) provides a set of services that can be used to extend the functionality of a common language runtime (CLR) property. Collectively, these services are typically referred to as the WPF property system. A property that is backed by the WPF property system is known as a dependency property. This overview describes the WPF property system and the capabilities of a dependency property. This includes how to use existing dependency properties in XAML and in code. This overview also introduces specialized aspects of dependency properties, such as dependency property metadata, and how to create your own dependency property in a custom class.
Prerequisites
This topic assumes that you have some basic knowledge of the CLR and object-oriented programming. In order to follow the examples in this topic, you should also understand XAML and know how to write WPF applications. For more information, see Walkthrough: My First WPF Desktop Application.
Dependency Properties and CLR Properties
In WPF, properties are typically exposed as common language runtime (CLR) properties. At a basic level, you could interact with these properties directly and never know that they are implemented as a dependency property. However, you should become familiar with some or all of the features of the WPF property system, so that you can take advantage of these features.
The purpose of dependency properties is to provide a way to compute the value of a property based on the value of other inputs. These other inputs might include system properties such as themes and user preference, just-in-time property determination mechanisms such as data binding and animations/storyboards, multiple-use templates such as resources and styles, or values known through parent-child relationships with other elements in the element tree. In addition, a dependency property can be implemented to provide self-contained validation, default values, callbacks that monitor changes to other properties, and a system that can coerce property values based on potentially runtime information. Derived classes can also change some specific characteristics of an existing property by overriding dependency property metadata, rather than overriding the actual implementation of existing properties or creating new properties.
In the SDK reference, you can identify which property is a dependency property by the presence of the Dependency Property Information section on the managed reference page for that property. The Dependency Property Information section includes a link to the DependencyProperty identifier field for that dependency property, and also includes a list of the metadata options that are set for that property, per-class override information, and other details.
Dependency Properties Back CLR Properties
Dependency properties and the WPF property system extend property functionality by providing a type that backs a property, as an alternative implementation to the standard pattern of backing the property with a private field. The name of this type is DependencyProperty. The other important type that defines the WPF property system is DependencyObject. DependencyObject defines the base class that can register and own a dependency property.
Following is a summation of the terminology that is used in this software development kit (SDK) documentation when discussing dependency properties:
Dependency property: A property that is backed by a DependencyProperty.
Dependency property identifier: A DependencyProperty instance, which is obtained as a return value when registering a dependency property, and then stored as a static member of a class. This identifier is used as a parameter for many of the APIs that interact with the WPF property system.
CLR "wrapper": The actual get and set implementations for the property. These implementations incorporate the dependency property identifier by using it in the GetValue and SetValue calls, thus providing the backing for the property using the WPF property system.
The following example defines the IsSpinning dependency property, and shows the relationship of the DependencyProperty identifier to the property that it backs.
public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register( "IsSpinning", typeof(Boolean),
); public bool IsSpinning { get { return (bool)GetValue(IsSpinningProperty); } set { SetValue(IsSpinningProperty, value); } }
The naming convention of the property and its backing DependencyProperty field is important. The name of the field is always the name of the property, with the suffix Property appended. For more information about this convention and the reasons for it, see Custom Dependency Properties.
Setting Property Values
You can set properties either in code or in XAML.
Setting Property Values in XAML
The following XAML example specifies the background color of a button as red. This example illustrates a case where the simple string value for a XAML attribute is type-converted by the WPF XAML parser into a WPF type (a Color, by way of a SolidColorBrush) in the generated code.
<Button Background="Red" Content="Button!"/>
XAML supports a variety of syntax forms for setting properties. Which syntax to use for a particular property will depend on the value type that a property uses, as well as other factors such as the presence of a type converter. For more information on XAML syntax for property setting, see XAML Overview (WPF) and XAML Syntax In Detail.
As an example of non-attribute syntax, the following XAML example shows another button background. This time rather than setting a simple solid color, the background is set to an image, with an element representing that image and the source of that image specified as an attribute of the nested element. This is an example of property element syntax.
<Button Content="Button!"> <Button.Background> <ImageBrush ImageSource="wavy.jpg"/> </Button.Background> </Button>
Setting Properties in Code
Setting dependency property values in code is typically just a call to the set implementation exposed by the CLR "wrapper".
Getting a property value is also essentially a call to the get "wrapper" implementation:
You can also call the property system APIs GetValue and SetValue directly. This is not typically necessary if you are using existing properties (the wrappers are more convenient, and provide better exposure of the property for developer tools), but calling the APIs directly is appropriate for certain scenarios.
Properties can be also set in XAML and then accessed later in code, through code-behind. For details, see Code-Behind and XAML in WPF.
Property Functionality Provided by a Dependency Property
A dependency property provides functionality that extends the functionality of a property as opposed to a property that is backed by a field. Often, each such functionality represents or supports a specific feature of the overall WPF set of features:
Resources
A dependency property value can be set by referencing a resource. Resources are typically specified as the Resources property value of a page root element, or of the application (these locations enable the most convenient access to the resource). The following example shows how to define a SolidColorBrush resource.
<DockPanel.Resources> <SolidColorBrush x:Key="MyBrush" Color="Gold"/> </DockPanel.Resources>
Once the resource is defined, you can reference the resource and use it to provide a property value:
<Button Background="{DynamicResource MyBrush}" Content="I am gold" />
This particular resource is referenced as a DynamicResource Markup Extension (in WPF XAML, you can use either a static or dynamic resource reference). To use a dynamic resource reference, you must be setting to a dependency property, so it is specifically the dynamic resource reference usage that is enabled by the WPF property system. For more information, see XAML Resources.
Note |
---|
Resources are treated as a local value, which means that if you set another local value, you will eliminate the resource reference. For more information, see Dependency Property Value Precedence. |
Data Binding
A dependency property can reference a value through data binding. Data binding works through a specific markup extension syntax in XAML, or the Binding object in code. With data binding, the final property value determination is deferred until run time, at which time the value is obtained from a data source.
The following example sets the Content property for a Button, using a binding declared in XAML. The binding uses an inherited data context and an XmlDataProvider data source (not shown). The binding itself specifies the desired source property by XPath within the data source.
<Button Content="{Binding XPath=Team/@TeamName}"/>
Note |
---|
Bindings are treated as a local value, which means that if you set another local value, you will eliminate the binding. For details, see Dependency Property Value Precedence. |
Dependency properties, or the DependencyObject class, do not natively support INotifyPropertyChanged for purposes of producing notifications of changes in DependencyObject source property value for data binding operations. For more information on how to create properties for use in data binding that can report changes to a data binding target, see Data Binding Overview.
Styles
Styles and templates are two of the chief motivating scenarios for using dependency properties. Styles are particularly useful for setting properties that define application user interface (UI). Styles are typically defined as resources in XAML. Styles interact with the property system because they typically contain "setters" for particular properties, as well as "triggers" that change a property value based on the real-time value for another property.
The following example creates a very simple style (which would be defined inside a Resources dictionary, not shown), then applies that style directly to the Style property for a Button. The setter within the style sets the Background property for a styled Button to green.
<Style x:Key="GreenButtonStyle"> <Setter Property="Control.Background" Value="Green"/> </Style>
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>
For more information, see Styling and Templating.
Animations
Dependency properties can be animated. When an animation is applied and is running, the animated value operates at a higher precedence than any value (such as a local value) that the property otherwise has.
The following example animates the Background on a Button property (technically, the Background is animated by using property element syntax to specify a blank SolidColorBrush as the Background, then the Color property of that SolidColorBrush is the property that is directly animated).
<Button>I am animated <Button.Background> <SolidColorBrush x:Name="AnimBrush"/> </Button.Background> <Button.Triggers> <EventTrigger RoutedEvent="Button.Loaded"> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetName="AnimBrush" Storyboard.TargetProperty="(SolidColorBrush.Color)" From="Red" To="Green" Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Button.Triggers> </Button>
For more information on animating properties, see Animation Overview and Storyboards Overview.
Metadata Overrides
You can change certain behaviors of a dependency property by overriding the metadata for that property when you derive from the class that originally registers the dependency property. Overriding metadata relies on the DependencyProperty identifier. Overriding metadata does not require re-implementing the property. The metadata change is handled natively by the property system; each class potentially holds individual metadata for all properties that are inherited from base classes, on a per-type basis.
The following example overrides metadata for a dependency property DefaultStyleKey. Overriding this particular dependency property metadata is part of an implementation pattern that creates controls that can use default styles from themes.
public class SpinnerControl : ItemsControl { static SpinnerControl() { DefaultStyleKeyProperty.OverrideMetadata( typeof(SpinnerControl), new FrameworkPropertyMetadata(typeof(SpinnerControl)) ); } }
For more information about overriding or obtaining property metadata, see Dependency Property Metadata.
Property Value Inheritance
An element can inherit the value of a dependency property from its parent in the object tree.
Note |
---|
Property value inheritance behavior is not globally enabled for all dependency properties, because the calculation time for inheritance does have some performance impact. Property value inheritance is typically only enabled for properties where a particular scenario suggests that property value inheritance is appropriate. You can determine whether a dependency property inherits by looking at the Dependency Property Information section for that dependency property in the SDK reference. |
The following example shows a binding, and sets the DataContext property that specifies the source of the binding, which was not shown in the earlier binding example. Any subsequent bindings in child objects do not need to specify the source, they can use the inherited value from DataContext in the parent StackPanel object. (Alternatively, a child object could instead choose to directly specify its own DataContext or a Sourcein the Binding, and to deliberately not use the inherited value for data context of its bindings.)
<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource XmlTeamsSource}}"> <Button Content="{Binding XPath=Team/@TeamName}"/> </StackPanel>
For more information, see Property Value Inheritance.
WPF Designer Integration
A custom control with properties that are implemented as dependency properties will receive appropriate WPF Designer for Visual Studio support. One example is the ability to edit direct and attached dependency properties with the Properties window. For more information, see Control Authoring Overview.
Dependency Property Value Precedence
When you get the value of a dependency property, you are potentially obtaining a value that was set on that property through any one of the other property-based inputs that participate in the WPF property system. Dependency property value precedence exists so that a variety of scenarios for how properties obtain their values can interact in a predictable way.
Consider the following example. The example includes a style that applies to all buttons and their Background properties, but then also specifies one button with a locally set Background value.
Note |
---|
The SDK documentation uses the terms "local value" or "locally set value" occasionally when discussing dependency properties. A locally set value is a property value that is set directly on an object instance in code, or as an attribute on an element in XAML. |
In principle, for the first button, the property is set twice, but only one value applies: the value with the highest precedence. A locally set value has the highest precedence (except for a running animation, but no animation applies in this example) and thus the locally set value is used instead of the style setter value for the background on the first button. The second button has no local value (and no other value with higher precedence than a style setter) and thus the background in that button comes from the style setter.
<StackPanel> <StackPanel.Resources> <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}"> <Setter Property="Background" Value="Red"/> </Style> </StackPanel.Resources> <Button Background="Green">I am NOT red!</Button> <Button>I am styled red</Button> </StackPanel>
Why Does Dependency Property Precedence Exist?
Typically, you would not want styles to always apply and to obscure even a locally set value of an individual element (otherwise, it would be very difficult to use either styles or elements in general). Therefore, the values that come from styles operate at a lower precedent than a locally set value. For a more thorough listing of dependency properties and where a dependency property effective value might come from, see Dependency Property Value Precedence.
Note |
---|
There are a number of properties defined on WPF elements that are not dependency properties. By and large, properties were implemented as dependency properties only when there were needs to support at least one of the scenarios enabled by the property system: data binding, styling, animation, default value support, inheritance, attached properties, or invalidation. |
Learning More About Dependency Properties
An attached property is a type of property that supports a specialized syntax in XAML. An attached property often does not have a 1:1 correspondence with a common language runtime (CLR) property, and is not necessarily a dependency property. The typical purpose of a attached property is to allow child elements to report property values to a parent element, even if the parent element and child element do not both possess that property as part of the class members listings. One primary scenario is to enable child elements to inform the parent how they should be presented in UI; for an example, see Dock or Left. For details, see Attached Properties Overview.
Component developers or application developers may wish to create their own dependency property, in order to enable capabilities such as data binding or styles support, or for invalidation and value coercion support. For details, see Custom Dependency Properties.
Dependency properties should generally be considered to be public properties, accessible or at least discoverable by any caller that has access to an instance. For more information, see Dependency Property Security.
Attached Properties Overview
An attached property is a concept defined by XAML. An attached property is intended to be used as a type of global property that is settable on any object. In Windows Presentation Foundation (WPF), attached properties are typically defined as a specialized form of dependency property that does not have the conventional property "wrapper".
Prerequisites
This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on Windows Presentation Foundation (WPF) classes, and have read the Dependency Properties Overview. To follow the examples in this topic, you should also understand XAML and know how to write WPF applications.
Why Use Attached Properties
One purpose of an attached property is to allow different child elements to specify unique values for a property that is actually defined in a parent element. A specific application of this scenario is having child elements inform the parent element of how they are to be presented in the user interface (UI). One example is the DockPanel.Dock property. The DockPanel.Dock property is created as an attached property because it is designed to be set on elements that are contained within a DockPanel, rather than on DockPanel itself. The DockPanel class defines the static DependencyProperty field named DockProperty, and then provides the GetDock and SetDock methods as public accessors for the attached property.
Attached Properties in XAML
In XAML, you set attached properties by using the syntax AttachedPropertyProvider.PropertyName
The following is an example of how you can set DockPanel.Dock in XAML:
<DockPanel> <CheckBox DockPanel.Dock="Top">Hello</CheckBox> </DockPanel>
Note that the usage is somewhat similar to a static property; you always reference the type DockPanel that owns and registers the attached property, rather than referring to any instance specified by name.
Also, because an attached property in XAML is an attribute that you set in markup, only the set operation has any relevance. You cannot directly get a property in XAML, although there are some indirect mechanisms for comparing values, such as triggers in styles (for details, see Styling and Templating).
Attached Property Implementation in WPF
In Windows Presentation Foundation (WPF), most of the attached properties that exist on WPF types that are related to UI presentation are implemented as dependency properties. Attached properties are a XAML concept, whereas dependency properties are a WPF concept. Because WPF attached properties are dependency properties, they support dependency property concepts such as property metadata, and default values from that property metadata.
How Attached Properties Are Used by the Owning Type
Although attached properties are settable on any object, that does not automatically mean that setting the property will produce a tangible result, or that the value will ever be used by another object. Generally, attached properties are intended so that objects coming from a wide variety of possible class hierarchies or logical relationships can each report common information to the type that defines the attached property. The type that defines the attached property typically follows one of these models:
The type that defines the attached property is designed so that it can be the parent element of the elements that will set values for the attached property. The type then iterates its child objects through internal logic against some object tree structure, obtains the values, and acts on those values in some manner.
The type that defines the attached property will be used as the child element for a variety of possible parent elements and content models.
The type that defines the attached property represents a service. Other types set values for the attached property. Then, when the element that set the property is evaluated in the context of the service, the attached property values are obtained through internal logic of the service class.
An Example of a Parent-Defined Attached Property
The most typical scenario where WPF defines an attached property is when a parent element supports a child element collection, and also implements a behavior where the specifics of the behavior are reported individually for each child element.
DockPanel defines the DockPanel.Dock attached property, and DockPanel has class-level code as part of its rendering logic (specifically, MeasureOverride and ArrangeOverride). A DockPanel instance will always check to see whether any of its immediate child elements have set a value for DockPanel.Dock. If so, those values become input for the rendering logic applied to that particular child element. Nested DockPanelinstances each treat their own immediate child element collections, but that behavior is implementation-specific to how DockPanel processes DockPanel.Dock values. It is theoretically possible to have attached properties that influence elements beyond the immediate parent. If the DockPanel.Dock attached property is set on an element that has no DockPanel parent element to act upon it, no error or exception is raised. This simply means that a global property value was set, but it has no current DockPanel parent that could consume the information.
Attached Properties in Code
Attached properties in WPF do not have the typical CLR "wrapper" methods for easy get/set access. This is because the attached property is not necessarily part of the CLR namespace for instances where the property is set. However, a XAML processor must be able to set those values when XAML is parsed. To support an effective attached property usage, the owner type of the attached property must implement dedicated accessor methods in the form GetPropertyName and SetPropertyName. These dedicated accessor methods are also useful to get or set the attached property in code. From a code perspective, an attached property is similar to a backing field that has method accessors instead of property accessors, and that backing field can exist on any object rather than needing to be specifically defined.
The following example shows how you can set an attached property in code. In this example, myCheckBox is an instance of the CheckBox class.
DockPanel myDockPanel = new DockPanel(); CheckBox myCheckBox = new CheckBox(); myCheckBox.Content = "Hello"; myDockPanel.Children.Add(myCheckBox); DockPanel.SetDock(myCheckBox, Dock.Top);
Similar to the XAML case, if myCheckBox had not already been added as a child element of myDockPanel by the third line of code, the fourth line of code would not raise an exception, but the property value would not interact with a DockPanel parent and thus would do nothing. Only a DockPanel.Dock value set on a child element combined with the presence of a DockPanel parent element will cause an effective behavior in the rendered application. (In this case, you could set the attached property, then attach to the tree. Or you could attach to the tree then set the attached property. Either action order provides the same result.)
Attached Property Metadata
When registering the property, FrameworkPropertyMetadata is set to specify characteristics of the property, such as whether the property affects rendering, measurement, and so on. Metadata for an attached property is generally no different than on a dependency property. If you specify a default value in an override to attached property metadata, that value becomes the default value of the implicit attached property on instances of the overriding class. Specifically, your default value is reported if some process queries for the value of an attached property through the Get method accessor for that property, specifying an instance of the class where you specified the metadata, and the value for that attached property was otherwise not set.
If you want to enable property value inheritance on a property, you should use attached properties rather than non-attached dependency properties. For details, see Property Value Inheritance.
Custom Attached Properties
When to Create an Attached Property
You might create an attached property when there is a reason to have a property setting mechanism available for classes other than the defining class. The most common scenario for this is layout. Examples of existing layout properties are DockPanel.Dock, Panel.ZIndex, and Canvas.Top. The scenario enabled here is that elements that exist as child elements to layout-controlling elements are able to express layout requirements to their layout parent elements individually, each setting a property value that the parent defined as an attached property.
Another scenario for using an attached property is when your class represents a service, and you want classes to be able to integrate the service more transparently.
Yet another scenario is to receive Visual Studio 2008 WPF Designer support, such as Properties window editing. For more information, see Control Authoring Overview.
As mentioned before, you should register as an attached property if you want to use property value inheritance.
How to Create an Attached Property
If your class is defining the attached property strictly for use on other types, then the class does not have to derive from DependencyObject. But you do need to derive from DependencyObject if you follow the overall WPF model of having your attached property also be a dependency property.
Define your attached property as a dependency property by declaring a public static readonly field of type DependencyProperty. You define this field by using the return value of the RegisterAttached method. The field name must match the attached property name, appended with the string Property, to follow the established WPF pattern of naming the identifying fields versus the properties that they represent. The attached property provider must also provide static GetPropertyName and SetPropertyName methods as accessors for the attached property; failing to do this will result in the property system being unable to use your attached property.
Note |
---|
If you omit the attached property's get accessor, data binding on the property will not work in design tools, such as Visual Studio and Expression Blend. |
The Get Accessor
The signature for the GetPropertyName accessor must be:
public static object GetPropertyName(object target)
The target object can be specified as a more specific type in your implementation. For example, the DockPanel.GetDock method types the parameter as UIElement, because the attached property is only intended to be set on UIElement instances.
The return value can be specified as a more specific type in your implementation. For example, the GetDock method types it as Dock, because the value can only be set to that enumeration.
The Set Accessor
The signature for the SetPropertyName accessor must be:
public static void SetPropertyName(object target, object value)
The target object can be specified as a more specific type in your implementation. For example, the SetDock method types it as UIElement, because the attached property is only intended to be set on UIElement instances.
The value object can be specified as a more specific type in your implementation. For example, the SetDock method types it as Dock, because the value can only be set to that enumeration. Remember that the value for this method is the input coming from the XAML loader when it encounters your attached property in an attached property usage in markup. That input is the value specified as a XAML attribute value in markup. Therefore there must be type conversion, value serializer, or markup extension support for the type you use, such that the appropriate type can be created from the attribute value (which is ultimately just a string).
The following example shows the dependency property registration (using the RegisterAttached method), as well as the GetPropertyName and SetPropertyName accessors. In the example, the attached property name is IsBubbleSource. Therefore, the accessors must be named GetIsBubbleSource and SetIsBubbleSource.
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached( "IsBubbleSource", typeof(Boolean), typeof(AquariumObject), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender) ); public static void SetIsBubbleSource(UIElement element, Boolean value) { element.SetValue(IsBubbleSourceProperty, value); } public static Boolean GetIsBubbleSource(UIElement element) { return (Boolean)element.GetValue(IsBubbleSourceProperty); }
Attached Property Attributes
WPF defines several .NET Framework attributes that are intended to provide information about attached properties to reflection processes, and to typical users of reflection and property information such as designers. Because attached properties have a type of unlimited scope, designers need a way to avoid overwhelming users with a global list of all the attached properties that are defined in a particular technology implementation that uses XAML. The .NET Framework attributes that WPF defines for attached properties can be used to scope the situations where a given attached property should be shown in a properties window. You might consider applying these attributes for your own custom attached properties also. The purpose and syntax of the .NET Framework attributes is described on the appropriate reference pages:
Learning More About Attached Properties
For more information on creating an attached property, see How to: Register an Attached Property.
For more advanced usage scenarios for dependency properties and attached properties, see Custom Dependency Properties.
You can also register a property as an attached property, and as a dependency property, but then still expose "wrapper" implementations. In this case, the property can be set either on that element, or on any element through the XAML attached property syntax. An example of a property with an appropriate scenario for both standard and attached usages is FrameworkElement.FlowDirection.
Dependency Property Callbacks and Validation
https://msdn.microsoft.com/ko-kr/library/ms745795(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/ms745795(v=vs.110).aspx
This topic describes how to create dependency properties using alternative custom implementations for property-related features such as validation determination, callbacks that are invoked whenever the property's effective value is changed, and overriding possible outside influences on value determination. This topic also discusses scenarios where expanding on the default property system behaviors by using these techniques is appropriate.
Prerequisites
This topic assumes that you understand the basic scenarios of implementing a dependency property, and how metadata is applied to a custom dependency property. See Custom Dependency Properties and Dependency Property Metadata for context.
Validation Callbacks
Validation callbacks can be assigned to a dependency property when you first register it. The validation callback is not part of property metadata; it is a direct input of the Register method. Therefore, once a validation callback is created for a dependency property, it cannot be overridden by a new implementation.
public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register( "CurrentReading", typeof(double), typeof(Gauge), new FrameworkPropertyMetadata( Double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnCurrentReadingChanged), new CoerceValueCallback(CoerceCurrentReading) ), new ValidateValueCallback(IsValidReading) ); public double CurrentReading { get { return (double)GetValue(CurrentReadingProperty); } set { SetValue(CurrentReadingProperty, value); } }
The callbacks are implemented such that they are provided an object value. They return true if the provided value is valid for the property; otherwise, they return false. It is assumed that the property is of the correct type per the type registered with the property system, so checking type within the callbacks is not ordinarily done. The callbacks are used by the property system in a variety of different operations. This includes the initial type initialization by default value, programmatic change by invoking SetValue, or attempts to override metadata with new default value provided. If the validation callback is invoked by any of these operations, and returns false, then an exception will be raised. Application writers must be prepared to handle these exceptions. A common use of validation callbacks is validating enumeration values, or constraining values of integers or doubles when the property sets measurements that must be zero or greater.
Validation callbacks specifically are intended to be class validators, not instance validators. The parameters of the callback do not communicate a specific DependencyObject on which the properties to validate are set. Therefore the validation callbacks are not useful for enforcing the possible "dependencies" that might influence a property value, where the instance-specific value of a property is dependent on factors such as instance-specific values of other properties, or run-time state.
The following is example code for a very simple validation callback scenario: validating that a property that is typed as the Double primitive is not PositiveInfinity or NegativeInfinity.
Coerce Value Callbacks and Property Changed Events
Coerce value callbacks do pass the specific DependencyObject instance for properties, as do PropertyChangedCallback implementations that are invoked by the property system whenever the value of a dependency property changes. Using these two callbacks in combination, you can create a series of properties on elements where changes in one property will force a coercion or reevaluation of another property.
A typical scenario for using a linkage of dependency properties is when you have a user interface driven property where the element holds one property each for the minimum and maximum value, and a third property for the actual or current value. Here, if the maximum was adjusted in such a way that the current value exceeded the new maximum, you would want to coerce the current value to be no greater than the new maximum, and a similar relationship for minimum to current.
The following is very brief example code for just one of the three dependency properties that illustrate this relationship. The example shows how the CurrentReading property of a Min/Max/Current set of related *Reading properties is registered. It uses the validation as shown in the previous section.
public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register( "CurrentReading", typeof(double), typeof(Gauge), new FrameworkPropertyMetadata( Double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnCurrentReadingChanged), new CoerceValueCallback(CoerceCurrentReading) ), new ValidateValueCallback(IsValidReading) ); public double CurrentReading { get { return (double)GetValue(CurrentReadingProperty); } set { SetValue(CurrentReadingProperty, value); } }
The property changed callback for Current is used to forward the change to other dependent properties, by explicitly invoking the coerce value callbacks that are registered for those other properties:
private static void OnCurrentReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.CoerceValue(MinReadingProperty); d.CoerceValue(MaxReadingProperty); }
The coerce value callback checks the values of properties that the current property is potentially dependent upon, and coerces the current value if necessary:
private static object CoerceCurrentReading(DependencyObject d, object value) { Gauge g = (Gauge)d; double current = (double)value; if (current < g.MinReading) current = g.MinReading; if (current > g.MaxReading) current = g.MaxReading; return current; }
Note |
---|
Default values of properties are not coerced. A property value equal to the default value might occur if a property value still has its initial default, or through clearing other values with ClearValue. |
The coerce value and property changed callbacks are part of property metadata. Therefore, you can change the callbacks for a particular dependency property as it exists on a type that you derive from the type that owns the dependency property, by overriding the metadata for that property on your type.
Advanced Coercion and Callback Scenarios
Constraints and Desired Values
The CoerceValueCallback callbacks will be used by the property system to coerce a value in accordance to the logic you declare, but a coerced value of a locally set property will still retain a "desired value" internally. If the constraints are based on other property values that may change dynamically during the application lifetime, the coercion constraints are changed dynamically also, and the constrained property can change its value to get as close to the desired value as possible given the new constraints. The value will become the desired value if all constraints are lifted. You can potentially introduce some fairly complicated dependency scenarios if you have multiple properties that are dependent on one another in a circular manner. For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. But if that coercion is active, and Maximum coerces to Minimum, it leaves Current in an unsettable state, because it is dependent on both and is constrained to the range between the values, which is zero. Then, if Maximum or Minimum are adjusted, Current will seem to "follow" one of the values, because the desired value of Current is still stored and is attempting to reach the desired value as the constraints are loosened.
There is nothing technically wrong with complex dependencies, but they can be a slight performance detriment if they require large numbers of reevaluations, and can also be confusing to users if they affect the UI directly. Be careful with property changed and coerce value callbacks and make sure that the coercion being attempted can be treated as unambiguously as possible, and does not "overconstrain".
The property system will treat any CoerceValueCallback that returns the value UnsetValue as a special case. This special case means that the property change that resulted in the CoerceValueCallback being called should be rejected by the property system, and that the property system should instead report whatever previous value the property had. This mechanism can be useful to check that changes to a property that were initiated asynchronously are still valid for the current object state, and suppress the changes if not. Another possible scenario is that you can selectively suppress a value depending on which component of property value determination is responsible for the value being reported. To do this, you can use the DependencyProperty passed in the callback and the property identifier as input for GetValueSource, and then process the ValueSource.
Custom Dependency Properties
This topic describes the reasons that Windows Presentation Foundation (WPF) application developers and component authors might want to create custom dependency property, and describes the implementation steps as well as some implementation options that can improve performance, usability, or versatility of the property.
Prerequisites
This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on WPF classes, and have read the Dependency Properties Overview topic. In order to follow the examples in this topic, you should also understand Extensible Application Markup Language (XAML) and know how to write WPF applications.
What Is a Dependency Property?
You can enable what would otherwise be a common language runtime (CLR) property to support styling, data binding, inheritance, animations, and default values by implementing it as a dependency property. Dependency properties are properties that are registered with the WPF property system by calling the Register method (or RegisterReadOnly), and that are backed by a DependencyProperty identifier field. Dependency properties can be used only by DependencyObject types, but DependencyObject is quite high in the WPF class hierarchy, so the majority of classes available in WPF can support dependency properties. For more information about dependency properties and some of the terminology and conventions used for describing them in this SDK, see Dependency Properties Overview.
Examples of Dependency Properties
Examples of dependency properties that are implemented on WPF classes include the Background property, the Width property, and the Textproperty, among many others. Each dependency property exposed by a class has a corresponding public static field of type DependencyPropertyexposed on that same class. This is the identifier for the dependency property. The identifier is named using a convention: the name of the dependency property with the string Property appended to it. For example, the corresponding DependencyProperty identifier field for the Background property is BackgroundProperty. The identifier stores the information about the dependency property as it was registered, and the identifier is then used later for other operations involving the dependency property, such as calling SetValue.
As mentioned in the Dependency Properties Overview, all dependency properties in WPF (except most attached properties) are also CLR properties because of the "wrapper" implementation. Therefore, from code, you can get or set dependency properties by calling CLR accessors that define the wrappers in the same manner that you would use other CLR properties. As a consumer of established dependency properties, you do not typically use the DependencyObject methods GetValue and SetValue, which are the connection point to the underlying property system. Rather, the existing implementation of the CLR properties will have already called GetValue and SetValue within the get and set wrapper implementations of the property, using the identifier field appropriately. If you are implementing a custom dependency property yourself, then you will be defining the wrapper in a similar way.
When Should You Implement a Dependency Property?
When you implement a property on a class, so long as your class derives from DependencyObject, you have the option to back your property with a DependencyProperty identifier and thus to make it a dependency property. Having your property be a dependency property is not always necessary or appropriate, and will depend on your scenario needs. Sometimes, the typical technique of backing your property with a private field is adequate. However, you should implement your property as a dependency property whenever you want your property to support one or more of the following WPF capabilities:
You want your property to be settable in a style. For more information, see Styling and Templating.
You want your property to support data binding. For more information about data binding dependency properties, see How to: Bind the Properties of Two Controls.
You want your property to be settable with a dynamic resource reference. For more information, see XAML Resources.
You want to inherit a property value automatically from a parent element in the element tree. In this case, register with the RegisterAttachedmethod, even if you also create a property wrapper for CLR access. For more information, see Property Value Inheritance.
You want your property to be animatable. For more information, see Animation Overview.
You want the property system to report when the previous value of the property has been changed by actions taken by the property system, the environment, or the user, or by reading and using styles. By using property metadata, your property can specify a callback method that will be invoked each time the property system determines that your property value was definitively changed. A related concept is property value coercion. For more information, see Dependency Property Callbacks and Validation.
You want to use established metadata conventions that are also used by WPF processes, such as reporting whether changing a property value should require the layout system to recompose the visuals for an element. Or you want to be able to use metadata overrides so that derived classes can change metadata-based characteristics such as the default value.
You want properties of a custom control to receive Visual Studio 2008 WPF Designer support, such as Properties window editing. For more information, see Control Authoring Overview.
When you examine these scenarios, you should also consider whether you can achieve your scenario by overriding the metadata of an existing dependency property, rather than implementing a completely new property. Whether a metadata override is practical depends on your scenario and how closely that scenario resembles the implementation in existing WPF dependency properties and classes. For more information about overriding metadata on existing properties, see Dependency Property Metadata.
Checklist for Defining a Dependency Property
Defining a dependency property consists of four distinct concepts. These concepts are not necessarily strict procedural steps, because some of these end up being combined as single lines of code in the implementation:
(Optional) Create property metadata for the dependency property.
Register the property name with the property system, specifying an owner type and the type of the property value. Also specify the property metadata, if used.
Define a DependencyProperty identifier as a public static readonly field on the owner type.
Define a CLR "wrapper" property whose name matches the name of the dependency property. Implement the CLR "wrapper" property's getand set accessors to connect with the dependency property that backs it.
Registering the Property with the Property System
In order for your property to be a dependency property, you must register that property into a table maintained by the property system, and give it a unique identifier that is used as the qualifier for later property system operations. These operations might be internal operations, or your own code calling property system APIs. To register the property, you call the Register method within the body of your class (inside the class, but outside of any member definitions). The identifier field is also provided by the Register method call, as the return value. The reason that the Register call is done outside of other member definitions is because you use this return value to assign and create a public static readonly field of type DependencyProperty as part of your class. This field becomes the identifier for your dependency property.
Dependency Property Name Conventions
There are established naming conventions regarding dependency properties that you must follow in all but exceptional circumstances.
The dependency property itself will have a basic name, "AquariumGraphic" as in this example, which is given as the first parameter of Register. That name must be unique within each registering type. Dependency properties inherited through base types are considered to be already part of the registering type; names of inherited properties cannot be registered again. However, there is a technique for adding a class as owner of a dependency property even when that dependency property is not inherited; for details, see Dependency Property Metadata.
When you create the identifier field, name this field by the name of the property as you registered it, plus the suffix Property. This field is your identifier for the dependency property, and it will be used later as an input for the SetValue and GetValue calls you will make in the wrappers, by any other code access to the property by your own code, by any external code access you allow, by the property system, and potentially by XAML processors.
Note |
---|
Defining the dependency property in the class body is the typical implementation, but it is also possible to define a dependency property in the class static constructor. This approach might make sense if you need more than one line of code to initialize the dependency property. |
Implementing the "Wrapper"
Your wrapper implementation should call GetValue in the get implementation, and SetValue in the set implementation (the original registration call and field are shown here too for clarity).
In all but exceptional circumstances, your wrapper implementations should perform only the GetValue and SetValue actions, respectively. The reason for this is discussed in the topic XAML Loading and Dependency Properties.
All existing public dependency properties that are provided on the WPF classes use this simple wrapper implementation model; most of the complexity of how dependency properties work is either inherently a behavior of the property system, or is implemented through other concepts such as coercion or property change callbacks through property metadata.
public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register( "AquariumGraphic", typeof(Uri), typeof(AquariumObject), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnUriChanged) ) ); public Uri AquariumGraphic { get { return (Uri)GetValue(AquariumGraphicProperty); } set { SetValue(AquariumGraphicProperty, value); } }
Again, by convention, the name of the wrapper property must be the same as the name chosen and given as first parameter of the Register call that registered the property. If your property does not follow the convention, this does not necessarily disable all possible uses, but you will encounter several notable issues:
Certain aspects of styles and templates will not work.
Most tools and designers must rely on the naming conventions to properly serialize XAML, or to provide designer environment assistance at a per-property level.
The current implementation of the WPF XAML loader bypasses the wrappers entirely, and relies on the naming convention when processing attribute values. For more information, see XAML Loading and Dependency Properties.
Property Metadata for a New Dependency Property
When you register a dependency property, the registration through the property system creates a metadata object that stores property characteristics. Many of these characteristics have defaults that are set if the property is registered with the simple signatures of Register. Other signatures of Register allow you to specify the metadata that you want as you register the property. The most common metadata given for dependency properties is to give them a default value that is applied on new instances that use the property.
If you are creating a dependency property that exists on a derived class of FrameworkElement, you can use the more specialized metadata class FrameworkPropertyMetadata rather than the base PropertyMetadata class. The constructor for the FrameworkPropertyMetadata class has several signatures where you can specify various metadata characteristics in combination. If you want to specify the default value only, use the signature that takes a single parameter of type Object. Pass that object parameter as a type-specific default value for your property (the default value provided must be the type you provided as the propertyType parameter in the Register call).
For FrameworkPropertyMetadata, you can also specify metadata option flags for your property. These flags are converted into discrete properties on the property metadata after registration and are used to communicate certain conditionals to other processes such as the layout engine.
Setting Appropriate Metadata Flags
If your property (or changes in its value) affects the user interface (UI), and in particular affects how the layout system should size or render your element in a page, set one or more of the following flags: AffectsMeasure, AffectsArrange, AffectsRender.
AffectsMeasure indicates that a change to this property requires a change to UI rendering where the containing object might require more or less space within the parent. For example, a "Width" property should have this flag set.
AffectsArrange indicates that a change to this property requires a change to UI rendering that typically does not require a change in the dedicated space, but does indicate that the positioning within the space has changed. For example, an "Alignment" property should have this flag set.
AffectsRender indicates that some other change has occurred that will not affect layout and measure, but does require another render. An example would be a property that changes a color of an existing element, such as "Background".
These flags are often used as a protocol in metadata for your own override implementations of property system or layout callbacks. For instance, you might have an OnPropertyChanged callback that will call InvalidateArrange if any property of the instance reports a value change and has AffectsArrange as true in its metadata.
Some properties may affect the rendering characteristics of the containing parent element, in ways above and beyond the changes in required size mentioned above. An example is the MinOrphanLines property used in the flow document model, where changes to that property can change the overall rendering of the flow document that contains the paragraph. Use AffectsParentArrange or AffectsParentMeasure to identify similar cases in your own properties.
By default, dependency properties support data binding. You can deliberately disable data binding, for cases where there is no realistic scenario for data binding, or where performance in data binding for a large object is recognized as a problem.
By default, data binding Mode for dependency properties defaults to OneWay. You can always change the binding to be TwoWay per binding instance; for details, see How to: Specify the Direction of the Binding. But as the dependency property author, you can choose to make the property use TwoWay binding mode by default. An example of an existing dependency property is MenuItem.IsSubmenuOpen; the scenario for this property is that the IsSubmenuOpen setting logic and the compositing of MenuItem interact with the default theme style. The IsSubmenuOpen property logic uses data binding natively to maintain the state of the property in accordance to other state properties and method calls. Another example property that binds TwoWay by default is TextBox.Text.
You can also enable property inheritance in a custom dependency property by setting the Inherits flag. Property inheritance is useful for a scenario where parent elements and child elements have a property in common, and it makes sense for the child elements to have that particular property value set to the same value as the parent set it. An example inheritable property is DataContext, which is used for binding operations to enable the important master-detail scenario for data presentation. By making DataContext inheritable, any child elements inherit that data context also. Because of property value inheritance, you can specify a data context at the page or application root, and do not need to respecify it for bindings in all possible child elements. DataContext is also a good example to illustrate that inheritance overrides the default value, but it can always be set locally on any particular child element; for details, see How to: Use the Master-Detail Pattern with Hierarchical Data. Property value inheritance does have a possible performance cost, and thus should be used sparingly; for details, see Property Value Inheritance.
Set the Journal flag to indicate if your dependency property should be detected or used by navigation journaling services. An example is the SelectedIndex property; any item selected in a selection control should be persisted when the journaling history is navigated.
Read-Only Dependency Properties
You can define a dependency property that is read-only. However, the scenarios for why you might define your property as read-only are somewhat different, as is the procedure for registering them with the property system and exposing the identifier. For more information, see Read-Only Dependency Properties.
Collection-Type Dependency Properties
Collection-type dependency properties have some additional implementation issues to consider. For details, see Collection-Type Dependency Properties.
Dependency Property Security Considerations
Dependency properties should be declared as public properties. Dependency property identifier fields should be declared as public static fields. Even if you attempt to declare other access levels (such as protected), a dependency property can always be accessed through the identifier in combination with the property system APIs. Even a protected identifier field is potentially accessible because of metadata reporting or value determination APIs that are part of the property system, such as LocalValueEnumerator. For more information, see Dependency Property Security.
There is a general principle in managed code programming (often enforced by code analysis tools such as FxCop) that class constructors should not call virtual methods. This is because constructors can be called as base initialization of a derived class constructor, and entering the virtual method through the constructor might occur at an incomplete initialization state of the object instance being constructed. When you derive from any class that already derives from DependencyObject, you should be aware that the property system itself calls and exposes virtual methods internally. These virtual methods are part of the WPF property system services. Overriding the methods enables derived classes to participate in value determination. To avoid potential issues with runtime initialization, you should not set dependency property values within constructors of classes, unless you follow a very specific constructor pattern. For details, see Safe Constructor Patterns for DependencyObjects.
Dependency Property Metadata
The Windows Presentation Foundation (WPF) property system includes a metadata reporting system that goes beyond what can be reported about a property through reflection or general common language runtime (CLR) characteristics. Metadata for a dependency property can also be assigned uniquely by the class that defines a dependency property, can be changed when the dependency property is added to a different class, and can be specifically overridden by all derived classes that inherit the dependency property from the defining base class.
Prerequisites
This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on Windows Presentation Foundation (WPF) classes, and have read the Dependency Properties Overview. In order to follow the examples in this topic, you should also understand XAML and know how to write WPF applications.
How Dependency Property Metadata is Used
Dependency property metadata exists as an object that can be queried to examine the characteristics of a dependency property. This metadata is also accessed frequently by the property system as it processes any given dependency property. The metadata object for a dependency property can contain the following types of information:
Default value for the dependency property, if no other value can be determined for the dependency property by local value, style, inheritance, etc. For a thorough discussion of how default values participate in the precedence used by the property system when assigning values for dependency properties, see Dependency Property Value Precedence.
References to callback implementations that affect coercion or change-notification behaviors on a per-owner-type basis. Note that these callbacks are often defined with a nonpublic access level, so obtaining the actual references from metadata is generally not possible unless the references are within your permitted access scope. For more information on dependency property callbacks, see Dependency Property Callbacks and Validation.
If the dependency property in question is considered to be a WPF framework-level property, the metadata might contain WPF framework-level dependency property characteristics, which report information and state for services such as the WPF framework-level layout engine and property inheritance logic. For more information on this aspect of dependency property metadata, see Framework Property Metadata.
Metadata APIs
The type that reports most of the metadata information used by the property system is the PropertyMetadata class. Metadata instances are optionally specified when dependency properties are registered with the property system, and can be specified again for additional types that either add themselves as owners or override metadata they inherit from the base class dependency property definition. (For cases where a property registration does not specify metadata, a default PropertyMetadata is created with default values for that class.)The registered metadata is returned as PropertyMetadata when you call the various GetMetadata overloads that get metadata from a dependency property on a DependencyObjectinstance.
The PropertyMetadata class is then derived from to provide more specific metadata for architectural divisions such as the WPF framework-levelclasses. UIPropertyMetadata adds animation reporting, and FrameworkPropertyMetadata provides the WPF framework-level properties mentioned in the previous section. When dependency properties are registered, they can be registered with these PropertyMetadata derived classes. When the metadata is examined, the base PropertyMetadata type can potentially be cast to the derived classes so that you can examine the more specific properties.
Note |
---|
The property characteristics that can be specified in FrameworkPropertyMetadata are sometimes referred to in this documentation as "flags". When you create new metadata instances for use in dependency property registrations or metadata overrides, you specify these values using the flagwise enumeration FrameworkPropertyMetadataOptions and then you supply possibly concatenated values of the enumeration to the FrameworkPropertyMetadata constructor. However, once constructed, these option characteristics are exposed within a FrameworkPropertyMetadata as a series of Boolean properties rather than the constructing enumeration value. The Boolean properties enable you to check each conditional, rather than requiring you to apply a mask to a flagwise enumeration value to get the information you are interested in. The constructor uses the concatenated FrameworkPropertyMetadataOptions in order to keep the length of the constructor signature reasonable, whereas the actual constructed metadata exposes the discrete properties to make querying the metadata more intuitive. |
When to Override Metadata, When to Derive a Class
The WPF property system has established capabilities for changing some characteristics of dependency properties without requiring them to be entirely re-implemented. This is accomplished by constructing a different instance of property metadata for the dependency property as it exists on a particular type. Note that most existing dependency properties are not virtual properties, so strictly speaking "re-implementing" them on inherited classes could only be accomplished by shadowing the existing member.
If the scenario you are trying to enable for a dependency property on a type cannot be accomplished by modifying characteristics of existing dependency properties, it might then be necessary to create a derived class, and then to declare a custom dependency property on your derived class. A custom dependency property behaves identically to dependency properties defined by the WPF APIs. For more details about custom dependency properties, see Custom Dependency Properties.
One notable characteristic of a dependency property that you cannot override is its value type. If you are inheriting a dependency property that has the approximate behavior you require, but you require a different type for it, you will have to implement a custom dependency property and perhaps link the properties through type conversion or other implementation on your custom class. Also, you cannot replace an existing ValidateValueCallback, because this callback exists in the registration field itself and not within its metadata.
Scenarios for Changing Existing Metadata
If you are working with metadata of an existing dependency property, one common scenario for changing dependency property metadata is to change the default value. Changing or adding property system callbacks is a more advanced scenario. You might want to do this if your implementation of a derived class has different interrelationships between dependency properties. One of the conditionals of having a programming model that supports both code and declarative usage is that properties must enable being set in any order. Thus any dependent properties need to be set just-in-time without context and cannot rely on knowing a setting order such as might be found in a constructor. For more information on this aspect of the property system, see Dependency Property Callbacks and Validation. Note that validation callbacks are not part of the metadata; they are part of the dependency property identifier. Therefore, validation callbacks cannot be changed by overriding the metadata.
In some cases you might also want to alter the WPF framework-level property metadata options on existing dependency properties. These options communicate certain known conditionals about WPF framework-level properties to other WPF framework-level processes such as the layout system. Setting the options is generally done only when registering a new dependency property, but it is also possible to change the WPF framework-levelproperty metadata as part of a OverrideMetadata or AddOwner call. For the specific values to use and more information, see Framework Property Metadata. For more information that is pertinent to how these options should be set for a newly registered dependency property, see Custom Dependency Properties.
Overriding Metadata
The purpose of overriding metadata is primarily so that you have the opportunity to change the various metadata-derived behaviors that are applied to the dependency property as it exists on your type. The reasons for this are explained in more detail in the Metadata section. For more information including some code examples, see How to: Override Metadata for a Dependency Property.
Property metadata can be supplied for a dependency property during the registration call (Register). However, in many cases, you might want to provide type-specific metadata for your class when it inherits that dependency property. You can do this by calling the OverrideMetadata method. For an example from the WPF APIs, the FrameworkElement class is the type that first registers the Focusable dependency property. But the Controlclass overrides metadata for the dependency property to provide its own initial default value, changing it from false to true, and otherwise re-uses the original Focusable implementation.
When you override metadata, the different metadata characteristics are either merged or replaced.
PropertyChangedCallback is merged. If you add a new PropertyChangedCallback, that callback is stored in the metadata. If you do not specify a PropertyChangedCallback in the override, the value of PropertyChangedCallback is promoted as a reference from the nearest ancestor that specified it in metadata.
The actual property system behavior for PropertyChangedCallback is that implementations for all metadata owners in the hierarchy are retained and added to a table, with order of execution by the property system being that the most derived class's callbacks are invoked first.
DefaultValue is replaced. If you do not specify a DefaultValue in the override, the value of DefaultValue comes from the nearest ancestor that specified it in metadata.
CoerceValueCallback implementations are replaced. If you add a new CoerceValueCallback, that callback is stored in the metadata. If you do not specify a CoerceValueCallback in the override, the value of CoerceValueCallback is promoted as a reference from the nearest ancestor that specified it in metadata.
The property system behavior is that only the CoerceValueCallback in the immediate metadata is invoked. No references to other CoerceValueCallback implementations in the hierarchy are retained.
This behavior is implemented by Merge, and can be overridden on derived metadata classes.
Overriding Attached Property Metadata
In WPF, attached properties are implemented as dependency properties. This means that they also have property metadata, which individual classes can override. The scoping considerations for an attached property in WPF are generally that any DependencyObject can have an attached property set on them. Therefore, any DependencyObject derived class can override the metadata for any attached property, as it might be set on an instance of the class. You can override default values, callbacks, or WPF framework-level characteristic-reporting properties. If the attached property is set on an instance of your class, those override property metadata characteristics apply. For instance, you can override the default value, such that your override value is reported as the value of the attached property on instances of your class, whenever the property is not otherwise set.
Note |
---|
The Inherits property is not relevant for attached properties. |
Adding a Class as an Owner of an Existing Dependency Property
A class can add itself as an owner of a dependency property that has already been registered, by using the AddOwner method. This enables the class to use a dependency property that was originally registered for a different type. The adding class is typically not a derived class of the type that first registered that dependency property as owner. Effectively, this allows your class and its derived classes to "inherit" a dependency property implementation without the original owner class and the adding class being in the same true class hierarchy. In addition, the adding class (and all derived classes as well) can then provide type-specific metadata for the original dependency property.
As well as adding itself as owner through the property system utility methods, the adding class should declare additional public members on itself in order to make the dependency property a full participant in the property system with exposure to both code and markup. A class that adds an existing dependency property has the same responsibilities as far as exposing the object model for that dependency property as does a class that defines a new custom dependency property. The first such member to expose is a dependency property identifier field. This field should be a public static readonly field of type DependencyProperty, which is assigned to the return value of the AddOwner call. The second member to define is the common language runtime (CLR) "wrapper" property. The wrapper makes it much more convenient to manipulate your dependency property in code (you avoid calls to SetValue each time, and can make that call only once in the wrapper itself). The wrapper is implemented identically to how it would be implemented if you were registering a custom dependency property. For more information about implementing a dependency property, see Custom Dependency Properties and How to: Add an Owner Type for a Dependency Property.
AddOwner and Attached Properties
You can call AddOwner for a dependency property that is defined as an attached property by the owner class. Generally the reason for doing this is to expose the previously attached property as a non-attached dependency property. You then will expose the AddOwner return value as a public static readonly field for use as the dependency property identifier, and will define appropriate "wrapper" properties so that the property appears in the members table and supports a non-attached property usage in your class.
Framework Property Metadata
Framework property metadata options are reported for the properties of object elements considered to be at the WPF framework level in the Windows Presentation Foundation (WPF) architecture. In general the WPF framework-level designation entails that features such as rendering, data binding, and property system refinements are handled by the WPF presentation APIs and executables. Framework property metadata is queried by these systems to determine feature-specific characteristics of particular element properties.
Prerequisites
This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on Windows Presentation Foundation (WPF) classes, and have read the Dependency Properties Overview. You should also have read Dependency Property Metadata.
What Is Communicated by Framework Property Metadata
Framework property metadata can be divided into the following categories:
Reporting layout properties that affect an element (AffectsArrange, AffectsMeasure, AffectsRender). You might set these flags in metadata if the property affects those respective aspects, and you are also implementing the MeasureOverride / ArrangeOverride methods in your class to supply specific rendering behavior and information to the layout system. Typically, such an implementation would check for property invalidations in dependency properties where any of these layout properties were true in the property metadata, and only those invalidations would necessitate requesting a new layout pass.
Reporting layout properties that affect the parent element of an element (AffectsParentArrange, AffectsParentMeasure). Some examples where these flags are set by default are FixedPage.Left and Paragraph.KeepWithNext.
Inherits. By default, dependency properties do not inherit values. OverridesInheritanceBehavior allows the pathway of inheritance to also travel into a visual tree, which is necessary for some control compositing scenarios.
Note The term "inherits" in the context of property values means something specific for dependency properties; it means that child elements can inherit the actual dependency property value from parent elements because of a WPF framework-level capability of the WPF property system. It has nothing to do directly with managed code type and members inheritance through derived types. For details, see Property Value Inheritance.
Reporting data binding characteristics (IsNotDataBindable, BindsTwoWayByDefault). By default, dependency properties in the framework support data binding, with a one-way binding behavior. You might disable data binding if there were no scenario for it whatsoever (because they are intended to be flexible and extensible, there aren't many examples of such properties in the default WPF APIs). You might set binding to have a two-way default for properties that tie together a control's behaviors amongst its component pieces (IsSubmenuOpen is an example) or where two-way binding is the common and expected scenario for users (Text is an example). Changing the data binding–related metadata only influences the default; on a per-binding basis that default can always be changed. For details on the binding modes and binding in general, see Data Binding Overview.
Reporting whether properties should be journaled by applications or services that support journaling (Journal). For general elements, journaling is not enabled by default, but it is selectively enabled for certain user input controls. This property is intended to be read by journaling services including the WPF implementation of journaling, and is typically set on user controls such as user selections within lists that should be persisted across navigation steps. For information about the journal, see Navigation Overview.
Reading FrameworkPropertyMetadata
Each of the properties linked above are the specific properties that the FrameworkPropertyMetadata adds to its immediate base class UIPropertyMetadata. Each of these properties will be false by default. A metadata request for a property where knowing the value of these properties is important should attempt to cast the returned metadata to FrameworkPropertyMetadata, and then check the values of the individual properties as needed.
Specifying Metadata
When you create a new metadata instance for purposes of applying metadata to a new dependency property registration, you have the choice of which metadata class to use: the base PropertyMetadata or some derived class such as FrameworkPropertyMetadata. In general, you should use FrameworkPropertyMetadata, particularly if your property has any interaction with property system and WPF functions such as layout and data binding. Another option for more sophisticated scenarios is to derive from FrameworkPropertyMetadata to create your own metadata reporting class with extra information carried in its members. Or you might use PropertyMetadata or UIPropertyMetadata to communicate the degree of support for features of your implementation.
For existing properties (AddOwner or OverrideMetadata call), you should always override with the metadata type used by the original registration.
If you are creating a FrameworkPropertyMetadata instance, there are two ways to populate that metadata with values for the specific properties that communicate the framework property characteristics:
Use the FrameworkPropertyMetadata constructor signature that allows a flags parameter. This parameter should be filled with all desired combined values of the FrameworkPropertyMetadataOptions enumeration flags.
Use one of the signatures without a flags parameter, and then set each reporting Boolean property on FrameworkPropertyMetadata to truefor each desired characteristic change. If you do this, you must set these properties before any elements with this dependency property are constructed; the Boolean properties are read-write in order to allow this behavior of avoiding the flags parameter and still populate the metadata, but the metadata must become effectively sealed before property use. Thus, attempting to set the properties after metadata is requested will be an invalid operation.
Framework Property Metadata Merge Behavior
When you override framework property metadata, the different metadata characteristics are either merged or replaced.
PropertyChangedCallback is merged. If you add a new PropertyChangedCallback, that callback is stored in the metadata. If you do not specify a PropertyChangedCallback in the override, the value of PropertyChangedCallback is promoted as a reference from the nearest ancestor that specified it in metadata.
The actual property system behavior for PropertyChangedCallback is that implementations for all metadata owners in the hierarchy are retained and added to a table, with order of execution by the property system being that the callbacks of the most deeply derived class are invoked first. Inherited callbacks run only once, counting as being owned by the class that placed them in metadata.
DefaultValue is replaced. If you do not specify a PropertyChangedCallback in the override, the value of DefaultValue comes from the nearest ancestor that specified it in metadata.
CoerceValueCallback implementations are replaced. If you add a new CoerceValueCallback, that callback is stored in the metadata. If you do not specify a CoerceValueCallback in the override, the value of CoerceValueCallback is promoted as a reference from the nearest ancestor that specified it in metadata.
The property system behavior is that only the CoerceValueCallback in the immediate metadata is invoked. No references to other CoerceValueCallback implementations in the hierarchy are retained.
The flags of FrameworkPropertyMetadataOptions enumeration are combined as a bitwise OR operation. If you specify FrameworkPropertyMetadataOptions, the original options are not overwritten. To change an option, set the corresponding property on FrameworkPropertyMetadata. For example, if the original FrameworkPropertyMetadata object sets the FrameworkPropertyMetadataOptions.NotDataBindable flag, you can change that by setting FrameworkPropertyMetadata.IsNotDataBindableto false.
This behavior is implemented by Merge, and can be overridden on derived metadata classes.
Dependency Property Value Precedence
This topic explains how the workings of the Windows Presentation Foundation (WPF) property system can affect the value of a dependency property, and describes the precedence by which aspects of the property system apply to the effective value of a property.
Prerequisites
This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on WPF classes, and have read Dependency Properties Overview. To follow the examples in this topic, you should also understand Extensible Application Markup Language (XAML) and know how to write WPF applications.
The WPF Property System
The WPF property system offers a powerful way to have the value of dependency properties be determined by a variety of factors, enabling features such as real-time property validation, late binding, and notifying related properties of changes to values for other properties. The exact order and logic that is used to determine dependency property values is reasonably complex. Knowing this order will help you avoid unnecessary property setting, and might also clear up confusion over exactly why some attempt to influence or anticipate a dependency property value did not end up resulting in the value you expected.
Dependency Properties Might Be "Set" in Multiple Places
The following is example XAML where the same property (Background) has three different "set" operations that might influence the value.
<Button Background="Red"> <Button.Style> <Style TargetType="{x:Type Button}"> <Setter Property="Background" Value="Green"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Blue" /> </Trigger> </Style.Triggers> </Style> </Button.Style> Click </Button>
Here, which color do you expect will apply—red, green, or blue?
With the exception of animated values and coercion, local property sets are set at the highest precedence. If you set a value locally you can expect that the value will be honored, even above any styles or control templates. Here in the example, Background is set to Red locally. Therefore, the style defined in this scope, even though it is an implicit style that would otherwise apply to all elements of that type in that scope, is not the highest precedence for giving the Background property its value. If you removed the local value of Red from that Button instance, then the style would have precedence and the button would obtain the Background value from the style. Within the style, triggers take precedence, so the button will be blue if the mouse is over it, and green otherwise.
Dependency Property Setting Precedence List
The following is the definitive order that the property system uses when assigning the run-time values of dependency properties. Highest precedence is listed first. This list expands on some of the generalizations made in the Dependency Properties Overview.
Property system coercion. For details on coercion, see Coercion, Animation, and Base Value later in this topic.
Active animations, or animations with a Hold behavior. In order to have any practical effect, an animation of a property must be able to have precedence over the base (unanimated) value, even if that value was set locally. For details, see Coercion, Animation, and Base Value later in this topic.
Local value. A local value might be set through the convenience of the "wrapper" property, which also equates to setting as an attribute or property element in XAML, or by a call to the SetValue API using a property of a specific instance. If you set a local value by using a binding or a resource, these each act in the precedence as if a direct value was set.
TemplatedParent template properties. An element has a TemplatedParent if it was created as part of a template (a ControlTemplate or DataTemplate). For details on when this applies, see TemplatedParent later in this topic. Within the template, the following precedence applies:
Triggers from the TemplatedParent template.
Property sets (typically through XAML attributes) in the TemplatedParent template.
Implicit style. Applies only to the Style property. The Style property is filled by any style resource with a key that matches the type of that element. That style resource must exist either in the page or the application; lookup for an implicit style resource does not proceed into the themes.
Style triggers. The triggers within styles from page or application (these styles might be either explicit or implicit styles, but not from the default styles, which have lower precedence).
Template triggers. Any trigger from a template within a style, or a directly applied template.
Style setters. Values from a Setter within styles from page or application.
Default (theme) style. For details on when this applies, and how theme styles relate to the templates within theme styles, see Default (Theme) Styles later in this topic. Within a default style, the following order of precedence applies:
Active triggers in the theme style.
Setters in the theme style.
Inheritance. A few dependency properties inherit their values from parent element to child elements, such that they need not be set specifically on each element throughout an application. For details see Property Value Inheritance.
Default value from dependency property metadata. Any given dependency property may have a default value as established by the property system registration of that particular property. Also, derived classes that inherit a dependency property have the option to override that metadata (including the default value) on a per-type basis. See Dependency Property Metadata for more information. Because inheritance is checked before default value, for an inherited property, a parent element default value takes precedence over a child element. Consequently, if an inheritable property is not set anywhere, the default value as specified on the root or parent is used instead of the child element default value.
TemplatedParent
TemplatedParent as a precedence item does not apply to any property of an element that you declare directly in standard application markup. The TemplatedParent concept exists only for child items within a visual tree that come into existence through the application of the template. When the property system searches the TemplatedParent template for a value, it is searching the template that created that element. The property values from the TemplatedParent template generally act as if they were set as a local value on the child element, but this lesser precedence versus the local value exists because the templates are potentially shared. For details, see TemplatedParent.
The Style Property
The order of lookup described earlier applies to all possible dependency properties except one: the Style property. The Style property is unique in that it cannot itself be styled, so the precedence items 5 through 8 do not apply. Also, either animating or coercing Style is not recommended (and animating Style would require a custom animation class). This leaves three ways that the Style property might be set:
Explicit style. The Style property is set directly. In most scenarios, the style is not defined inline, but instead is referenced as a resource, by explicit key. In this case the Style property itself acts as if it were a local value, precedence item 3.
Implicit style. The Style property is not set directly. However, the Style exists at some level in the resource lookup sequence (page, application) and is keyed using a resource key that matches the type the style is to be applied to. In this case, the Style property itself acts by a precedence identified in the sequence as item 5. This condition can be detected by using DependencyPropertyHelper against the Styleproperty and looking for ImplicitStyleReference in the results.
Default style, also known as theme style. The Style property is not set directly, and in fact will read as null up until run time. In this case, the style comes from the run-time theme evaluation that is part of the WPF presentation engine.
For implicit styles not in themes, the type must match exactly—a MyButton Button-derived class will not implicitly use a style for Button.
Default (Theme) Styles
Every control that ships with WPF has a default style. That default style potentially varies by theme, which is why this default style is sometimes referred to as a theme style.
The most important information that is found within a default style for a control is its control template, which exists in the theme style as a setter for its Template property. If there were no template from default styles, a control without a custom template as part of a custom style would have no visual appearance at all. The template from the default style gives the visual appearance of each control a basic structure, and also defines the connections between properties defined in the visual tree of the template and the corresponding control class. Each control exposes a set of properties that can influence the visual appearance of the control without completely replacing the template. For example, consider the default visual appearance of a Thumb control, which is a component of a ScrollBar.
A Thumb has certain customizable properties. The default template of a Thumb creates a basic stucture / visual tree with several nested Bordercomponents to create a bevel look. If a property that is part of the template is intended to be exposed for customization by the Thumb class, then that property must be exposed by a TemplateBinding, within the template. In the case of Thumb, various properties of these borders share a template binding to properties such as Background or BorderThickness. But certain other properties or visual arrangements are hard-coded into the control template or are bound to values that come directly from the theme, and cannot be changed short of replacing the entire template. Generally, if a property comes from a templated parent and is not exposed by a template binding, it cannot be adjusted by styles because there is no easy way to target it. But that property could still be influenced by property value inheritance in the applied template, or by default value.
The theme styles use a type as the key in their definitions. However, when themes are applied to a given element instance, themes lookup for this type is performed by checking the DefaultStyleKey property on a control. This is in contrast to using the literal Type, as implicit styles do. The value of DefaultStyleKey would inherit to derived classes even if the implementer did not change it (the intended way of changing the property is not to override it at the property level, but to instead change its default value in property metadata). This indirection enables base classes to define the theme styles for derived elements that do not otherwise have a style (or more importantly, do not have a template within that style and would thus have no default visual appearance at all). Thus, you can derive MyButton from Button and will still get the Button default template. If you were the control author of MyButton and you wanted a different behavior, you could override the dependency property metadata for DefaultStyleKey on MyButton to return a different key, and then define the relevant theme styles including template for MyButton that you must package with your MyButton control. For more details on themes, styles, and control authoring, see Control Authoring Overview.
Dynamic Resource References and Binding
Dynamic resource references and binding operations respect the precedence of the location at which they are set. For example, a dynamic resource applied to a local value acts per precedence item 3, a binding for a property setter within a theme style applies at precedence item 9, and so on. Because dynamic resource references and binding must both be able to obtain values from the run time state of the application, this entails that the actual process of determining the property value precedence for any given property extends into the run time as well.
Dynamic resource references are not strictly speaking part of the property system, but they do have a lookup order of their own which interacts with the sequence listed above. That precedence is documented more thoroughly in the XAML Resources. The basic summation of that precedence is: element to page root, application, theme, system.
Dynamic resources and bindings have the precedence of where they were set, but the value is deferred. One consequence of this is that if you set a dynamic resource or binding to a local value, any change to the local value replaces the dynamic resource or binding entirely. Even if you call the ClearValue method to clear the locally set value, the dynamic resource or binding will not be restored. In fact, if you call ClearValue on a property that has a dynamic resource or binding in place (with no literal local value), they are cleared by the ClearValue call too.
SetCurrentValue
The SetCurrentValue method is another way to set a property, but it is not in the order of precedence. Instead, SetCurrentValue enables you to change the value of a property without overwriting the source of a previous value. You can use SetCurrentValue any time that you want to set a value without giving that value the precedence of a local value. For example, if a property is set by a trigger and then assigned another value via SetCurrentValue, the property system still respects the trigger and the property will change if the trigger’s action occurs. SetCurrentValue enables you to change the property’s value without giving it a source with a higher precedence. Likewise, you can use SetCurrentValue to change the value of a property without overwriting a binding.
Coercion, Animations, and Base Value
Coercion and animation both act on a value that is termed as the "base value" throughout this SDK. The base value is thus whatever value is determined through evaluating upwards in the items until item 2 is reached.
For an animation, the base value can have an effect on the animated value, if that animation does not specify both "From" and "To" for certain behaviors, or if the animation deliberately reverts to the base value when completed. To see this in practice, run the From, To, and By Animation Target Values Sample. Try setting the local values of the rectangle height in the example, such that the initial local value differs from any "From" in the animation. You will note that the animations start right away using the "From" values and replace the base value once started. The animation might specify to return to the value found before animation once it is completed by specifying the Stop FillBehavior. Afterwards, normal precedence is used for the base value determination.
Multiple animations might be applied to a single property, with each of these animations possibly having been defined from different points in the value precedence. However, these animations will potentially composite their values, rather than just applying the animation from the higher precedence. This depends on exactly how the animations are defined, and the type of the value that is being animated. For more information about animating properties, see Animation Overview.
Coercion applies at the highest level of all. Even an already running animation is subject to value coercion. Certain existing dependency properties in WPF have built-in coercion. For a custom dependency property, you define the coercion behavior for a custom dependency property by writing a CoerceValueCallback and passing the callback as part of metadata when you create the property. You can also override coercion behavior of existing properties by overriding the metadata on that property in a derived class. Coercion interacts with the base value in such a way that the constraints on coercion are applied as those constraints exist at the time, but the base value is still retained. Therefore, if constraints in coercion are later lifted, the coercion will return the closest value possible to that base value, and potentially the coercion influence on a property will cease as soon as all constraints are lifted. For more information about coercion behavior, see Dependency Property Callbacks and Validation.
Trigger Behaviors
Controls often define trigger behaviors as part of their default style in themes. Setting local properties on controls might prevent the triggers from being able to respond to user-driven events either visually or behaviorally. The most common use of a property trigger is for control or state properties such as IsSelected. For example, by default when a Button is disabled (trigger for IsEnabled is false) then the Foreground value in the theme style is what causes the control to appear "grayed out". But if you have set a local Foreground value, that normal gray-out color will be overruled in precedence by your local property set, even in this property-triggered scenario. Be cautious of setting values for properties that have theme-level trigger behaviors and make sure you are not unduly interfering with the intended user experience for that control.
The ClearValue method provides an expedient means to clear any locally applied value from a dependency property that is set on an element. However, calling ClearValue is not a guarantee that the default as established in metadata during property registration is the new effective value. All of the other participants in value precedence are still active. Only the locally set value has been removed from the precedence sequence. For example, if you call ClearValue on a property where that property is also set by a theme style, then the theme value is applied as the new value rather than the metadata-based default. If you want to take all property value participants out of the process and set the value to the registered metadata default, you can obtain that default value definitively by querying the dependency property metadata, and then you can use the default value to locally set the property with a call to SetValue.
Read-Only Dependency Properties
https://msdn.microsoft.com/ko-kr/library/ms754044(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/ms754044(v=vs.110).aspx
This topic describes read-only dependency properties, including existing read-only dependency properties and the scenarios and techniques for creating a custom read-only dependency property.
Prerequisites
This topic assumes that you understand the basic scenarios of implementing a dependency property, and how metadata is applied to a custom dependency property. See Custom Dependency Properties and Dependency Property Metadata for context.
Existing Read-Only Dependency Properties
Some of the dependency properties defined in the Windows Presentation Foundation (WPF) framework are read-only. The typical reason for specifying a read-only dependency property is that these are properties that should be used for state determination, but where that state is influenced by a multitude of factors, but just setting the property to that state isn't desirable from a user interface design perspective. For example, the property IsMouseOver is really just surfacing state as determined from the mouse input. Any attempt to set this value programmatically by circumventing the true mouse input would be unpredictable and would cause inconsistency.
By virtue of not being settable, read-only dependency properties aren't appropriate for many of the scenarios for which dependency properties normally offer a solution (namely: data binding, directly stylable to a value, validation, animation, inheritance). Despite not being settable, read-only dependency properties still have some of the additional capabilities supported by dependency properties in the property system. The most important remaining capability is that the read-only dependency property can still be used as a property trigger in a style. You can't enable triggers with a normal common language runtime (CLR) property; it needs to be a dependency property. The aforementioned IsMouseOver property is a perfect example of a scenario where it might be quite useful to define a style for a control, where some visible property such as a background, foreground, or similar properties of composited elements within the control will change when the user places a mouse over some defined region of your control. Changes in a read-only dependency property can also be detected and reported by the property system's inherent invalidation processes, and this in fact supports the property trigger functionality internally.
Creating Custom Read-Only Dependency Properties
Make sure to read the section above regarding why read-only dependency properties won't work for many typical dependency-property scenarios. But if you have an appropriate scenario, you may wish to create your own read-only dependency property.
Much of the process of creating a read-only dependency property is the same as is described in the Custom Dependency Properties and How to: Implement a Dependency Property topics. There are three important differences:
When registering your property, call the RegisterReadOnly method instead of the normal Register method for property registration.
When implementing the CLR "wrapper" property, make sure that the wrapper too doesn't have a set implementation, so that there is no inconsistency in read-only state for the public wrapper you expose.
The object returned by the read-only registration is DependencyPropertyKey rather than DependencyProperty. You should still store this field as a member but typically you would not make it a public member of the type.
Whatever private field or value you have backing your read-only dependency property can of course be fully writable using whatever logic you decide. However, the most straightforward way to set the property either initially or as part of runtime logic is to use the property system's APIs, rather than circumventing the property system and setting the private backing field directly. In particular, there is a signature of SetValue that accepts a parameter of type DependencyPropertyKey. How and where you set this value programmatically within your application logic will affect how you may wish to set access on the DependencyPropertyKey created when you first registered the dependency property. If you handle this logic all within the class you could make it private, or if you require it to be set from other portions of the assembly you might set it internal. One approach is to call SetValue within a class event handler of a relevant event that informs a class instance that the stored property value needs to be changed. Another approach is to tie dependency properties together by using paired PropertyChangedCallback and CoerceValueCallback callbacks as part of those properties' metadata during registration.
Because the DependencyPropertyKey is private, and is not propagated by the property system outside of your code, a read-only dependency property does have better setting security than a read-write dependency property. For a read-write dependency property, the identifying field is explicitly or implicitly public and thus the property is widely settable. For more specifics, see Dependency Property Security.
Property Value Inheritance
Property value inheritance is a feature of the Windows Presentation Foundation (WPF) property system. Property value inheritance enables child elements in a tree of elements to obtain the value of a particular property from parent elements, inheriting that value as it was set anywhere in the nearest parent element. The parent element might also have obtained its value through property value inheritance, so the system potentially recurses all the way to the page root. Property value inheritance is not the default property system behavior; a property must be established with a particular metadata setting in order to cause that property to initiate property value inheritance on child elements.
Property Value Inheritance Is Containment Inheritance
"Inheritance" as a term here is not quite the same concept as inheritance in the context of types and general object-oriented programming, where derived classes inherit member definitions from their base classes. That meaning of inheritance is also active in WPF: properties defined in various base classes are exposed as attributes for derived XAML classes when used as elements, and exposed as members for code. Property value inheritance is particularly about how property values can inherit from one element to another on the basis of the parent-child relationships within a tree of elements. That tree of elements is most directly visible when nesting elements inside other elements as you define applications in XAML markup. Trees of objects can also be created programmatically by adding objects to designated collections of other objects, and property value inheritance works the same way in the finished tree at run time.
Practical Applications of Property Value Inheritance
The WPF APIs include several properties that have property inheritance enabled. Typically, the scenario for these is that they involve a property where it is appropriate that the property be set only once per page, but where that property is also a member of one of the base element classes and thus would also exist on most of the child elements. For example, the FlowDirection property controls which direction flowed content should be presented and arranged on the page. Typically, you want the text flow concept to be handled consistently throughout all child elements. If flow direction were for some reason reset in some level of the element tree by user or environment action, it should typically be reset throughout. When the FlowDirection property is made to inherit, the value need only be set or reset once at the level in the element tree that encompasses the presentation needs of each page in the application. Even the initial default value will inherit in this way. The property value inheritance model still enables individual elements to reset the value for the rare cases where having a mix of flow directions is intentional.
Making a Custom Property Inheritable
By changing a custom property's metadata, you can also make your own custom properties inheritable. Note, however, that designating a property as inheritable does have some performance considerations. In cases where that property does not have an established local value, or a value obtained through styles, templates, or data binding, an inheritable property provides its assigned property values to all child elements in the logical tree.
To make a property participate in value inheritance, create a custom attached property, as described in How to: Register an Attached Property. Register the property with metadata (FrameworkPropertyMetadata) and specify the "Inherits" option in the options settings within that metadata. Also make sure that the property has an established default value, because that value will now inherit. Although you registered the property as attached, you might also want to create a property "wrapper" for get/set access on the owner type, just as you would for an "nonattached" dependency property. After doing so, the inheritable property can either be set by using the direct property wrapper on the owner type or derived types, or it can be set by using the attached property syntax on any DependencyObject.
Attached properties are conceptually similar to global properties; you can check for the value on any DependencyObject and get a valid result. The typical scenario for attached properties is to set property values on child elements, and that scenario is more effective if the property in question is an attached property that is always implicitly present as an attached property on each element (DependencyObject) in the tree.
Note |
---|
Although property value inheritance might appear to work for nonattached dependency properties, the inheritance behavior for a nonattached property through certain element boundaries in the run-time tree is undefined. Always use RegisterAttached to register properties where you specify Inherits in the metadata. |
Inheriting Property Values Across Tree Boundaries
Property inheritance works by traversing a tree of elements. This tree is often parallel to the logical tree. However, whenever you include a WPF core-level object in the markup that defines an element tree, such as a Brush, you have created a discontinuous logical tree. A true logical tree does not conceptually extend through the Brush, because the logical tree is a WPF framework-level concept. You can see this reflected in the results when using the methods of LogicalTreeHelper. However, property value inheritance can bridge this gap in the logical tree and can still pass inherited values through, so long as the inheritable property was registered as an attached property and no deliberate inheritance-blocking boundary (such as a Frame) is encountered.
Dependency Property Security
Dependency properties should generally be considered to be public properties. The nature of the Windows Presentation Foundation (WPF) property system prevents the ability to make security guarantees about a dependency property value.
Access and Security of Wrappers and Dependency Properties
Typically, dependency properties are implemented along with "wrapper" common language runtime (CLR) properties that simplify getting or setting the property from an instance. But the wrappers are really just convenience methods that implement the underlying GetValue and SetValue static calls that are used when interacting with dependency properties. Thinking of it in another way, the properties are exposed as common language runtime (CLR) properties that happen to be backed by a dependency property rather than by a private field. Security mechanisms applied to the wrappers do not parallel the property system behavior and access of the underlying dependency property. Placing a security demand on the wrapper will only prevent the usage of the convenience method but will not prevent calls to GetValue or SetValue. Similarly, placing protected or private access level on the wrappers does not provide any effective security.
If you are writing your own dependency properties, you should declare the wrappers and the DependencyProperty identifier field as public members, so that callers do not get misleading information about the true access level of that property (because of its store being implemented as a dependency property).
For a custom dependency property, you can register your property as a read-only dependency property, and this does provide an effective means of preventing a property being set by anyone that does not hold a reference to the DependencyPropertyKey for that property. For more information, see Read-Only Dependency Properties.
Note |
---|
Declaring a DependencyProperty identifier field private is not forbidden, and it can conceivably be used to help reduce the immediately exposed namespace of a custom class, but such a property should not be considered "private" in the same sense as the common language runtime (CLR) language definitions define that access level, for reasons described in the next section. |
Property System Exposure of Dependency Properties
It is not generally useful, and it is potentially misleading, to declare a DependencyProperty as any access level other than public. That access level setting only prevents someone from being able to get a reference to the instance from the declaring class. But there are several aspects of the property system that will return a DependencyProperty as the means of identifying a particular property as it exists on an instance of a class or a derived class instance, and this identifier is still usable in a SetValue call even if the original static identifier is declared as nonpublic. Also, OnPropertyChanged virtual methods receive information of any existing dependency property that changed value. In addition, the GetLocalValueEnumerator method returns identifiers for any property on instances with a locally set value.
Applying a demand to a ValidateValueCallback and expecting the validation failure on a demand failure to prevent a property from being set is not an adequate security mechanism. Set-value invalidation enforced through ValidateValueCallback could also be suppressed by malicious callers, if those callers are operating within the application domain.
Safe Constructor Patterns for DependencyObjects
Generally, class constructors should not call callbacks such as virtual methods or delegates, because constructors can be called as base initialization of constructors for a derived class. Entering the virtual might be done at an incomplete initialization state of any given object. However, the property system itself calls and exposes callbacks internally, as part of the dependency property system. As simple an operation as setting a dependency property value with SetValue call potentially includes a callback somewhere in the determination. For this reason, you should be careful when setting dependency property values within the body of a constructor, which can become problematic if your type is used as a base class. There is a particular pattern for implementing DependencyObject constructors that avoids specific problems with dependency property states and the inherent callbacks, which is documented here.
Property System Virtual Methods
The following virtual methods or callbacks are potentially called during the computations of the SetValue call that sets a dependency property value: ValidateValueCallback, PropertyChangedCallback, CoerceValueCallback, OnPropertyChanged. Each of these virtual methods or callbacks serves a particular purpose in expanding the versatility of the Windows Presentation Foundation (WPF) property system and dependency properties. For more information on how to use these virtuals to customize property value determination, see Dependency Property Callbacks and Validation.
FXCop Rule Enforcement vs. Property System Virtuals
If you use the Microsoft tool FXCop as part of your build process, and you either derive from certain WPF framework classes calling the base constructor, or implement your own dependency properties on derived classes, you might encounter a particular FXCop rule violation. The name string for this violation is:
DoNotCallOverridableMethodsInConstructors
This is a rule that is part of the default public rule set for FXCop. What this rule might be reporting is a trace through the dependency property system that eventually calls a dependency property system virtual method. This rule violation might continue to appear even after following the recommended constructor patterns documented in this topic, so you might need to disable or suppress that rule in your FXCop rule set configuration.
Most Issues Come From Deriving Classes, Not Using Existing Classes
The issues reported by this rule occur when a class that you implement with virtual methods in its construction sequence is then derived from. If you seal your class, or otherwise know or enforce that your class will not be derived from, the considerations explained here and the issues that motivated the FXCop rule do not apply to you. However, if you are authoring classes in such a way that they are intended to be used as base classes, for instance if you are creating templates, or an expandable control library set, you should follow the patterns recommended here for constructors.
Default Constructors Must Initialize All Values Requested By Callbacks
Any instance members that are used by your class overrides or callbacks (the callbacks from the list in the Property System Virtuals section) must be initialized in your class default constructor, even if some of those values are filled by "real" values through parameters of the nondefault constructors.
The following example code (and subsequent examples) is a pseudo-C# example that violates this rule and explains the problem:
public class MyClass : DependencyObject { public MyClass() {} public MyClass(object toSetWobble) : this() { Wobble = toSetWobble; //this is backed by a DependencyProperty _myList = new ArrayList(); // this line should be in the default ctor } public static readonly DependencyProperty WobbleProperty = DependencyProperty.Register("Wobble", typeof(object), typeof(MyClass)); public object Wobble { get { return GetValue(WobbleProperty); } set { SetValue(WobbleProperty, value); } } protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { int count = _myList.Count; // null-reference exception } private ArrayList _myList; }
When application code calls new MyClass(objectvalue), this calls the default constructor and base class constructors. Then it sets Property1 = object1, which calls the virtual method OnPropertyChanged on the owning MyClass DependencyObject. The override refers to _myList, which has not been initialized yet.
One way to avoid these issues is to make sure that callbacks use only other dependency properties, and that each such dependency property has an established default value as part of its registered metadata.
Safe Constructor Patterns
To avoid the risks of incomplete initialization if your class is used as a base class, follow these patterns:
Default constructors calling base initialization
Implement these constructors calling the base default:
public MyClass : SomeBaseClass { public MyClass() : base() { // ALL class initialization, including initial defaults for // possible values that other ctors specify or that callbacks need. } }
Non-default (convenience) constructors, not matching any base signatures
If these constructors use the parameters to set dependency properties in the initialization, first call your own class default constructor for initialization, and then use the parameters to set dependency properties. These could either be dependency properties defined by your class, or dependency properties inherited from base classes, but in either case use the following pattern:
public MyClass : SomeBaseClass { public MyClass(object toSetProperty1) : this() { // Class initialization NOT done by default. // Then, set properties to values as passed in ctor parameters. Property1 = toSetProperty1; } }
Non-default (convenience) constructors, which do match base signatures
Instead of calling the base constructor with the same parameterization, again call your own class' default constructor. Do not call the base initializer; instead you should call this(). Then reproduce the original constructor behavior by using the passed parameters as values for setting the relevant properties. Use the original base constructor documentation for guidance in determining the properties that the particular parameters are intended to set:
public MyClass : SomeBaseClass { public MyClass(object toSetProperty1) : this() { // Class initialization NOT done by default. // Then, set properties to values as passed in ctor parameters. Property1 = toSetProperty1; } }
Must match all signatures
For cases where the base type has multiple signatures, you must deliberately match all possible signatures with a constructor implementation of your own that uses the recommended pattern of calling the class default constructor before setting further properties.
These same patterns apply if you are setting a property that does not have a wrapper for property setting convenience, and set values with SetValue. Your calls to SetValue that pass through constructor parameters should also call the class' default constructor for initialization.
Collection-Type Dependency Properties
This topic provides guidance and suggested patterns for how to implement a dependency property where the type of the property is a collection type.
Implementing a Collection-Type Dependency Property
For a dependency property in general, the implementation pattern that you follow is that you define a CLR property wrapper, where that property is backed by a DependencyProperty identifier rather than a field or other construct. You follow this same pattern when you implement a collection-type property. However, a collection-type property introduces some complexity to the pattern whenever the type that is contained within the collection is itself a DependencyObject or Freezable derived class.
Initializing the Collection Beyond the Default Value
When you create a dependency property, you do not specify the property default value as the initial field value. Instead, you specify the default value through the dependency property metadata. If your property is a reference type, the default value specified in dependency property metadata is not a default value per instance; instead it is a default value that applies to all instances of the type. Therefore you must be careful to not use the singular static collection defined by the collection property metadata as the working default value for newly created instances of your type. Instead, you must make sure that you deliberately set the collection value to a unique (instance) collection as part of your class constructor logic. Otherwise you will have created an unintentional singleton class.
Consider the following example. The following section of the example shows the definition for a class Aquarium. The class defines the collection type dependency property AquariumObjects, which uses the generic List<T> type with a FrameworkElement type constraint. In the Register(String, Type, Type, PropertyMetadata) call for the dependency property, the metadata establishes the default value to be a new generic List<T>.
public class Fish : FrameworkElement { } public class Aquarium : DependencyObject { private static readonly DependencyPropertyKey AquariumContentsPropertyKey = DependencyProperty.RegisterReadOnly( "AquariumContents", typeof(List<FrameworkElement>), typeof(Aquarium), new FrameworkPropertyMetadata(new List<FrameworkElement>()) ); public static readonly DependencyProperty AquariumContentsProperty = AquariumContentsPropertyKey.DependencyProperty; public List<FrameworkElement> AquariumContents { get { return (List<FrameworkElement>)GetValue(AquariumContentsProperty); } } // ... }
However, if you just left the code as shown, that single list default value is shared for all instances of Aquarium. If you ran the following test code, which is intended to show how you would instantiate two separate Aquarium instances and add a single different Fish to each of them, you would see a surprising result:
Aquarium myAq1 = new Aquarium(); Aquarium myAq2 = new Aquarium(); Fish f1 = new Fish(); Fish f2 = new Fish(); myAq1.AquariumContents.Add(f1); myAq2.AquariumContents.Add(f2); MessageBox.Show("aq1 contains " + myAq1.AquariumContents.Count.ToString() + " things"); MessageBox.Show("aq2 contains " + myAq2.AquariumContents.Count.ToString() + " things");
Instead of each collection having a count of one, each collection has a count of two! This is because each Aquarium added its Fish to the default value collection, which resulted from a single constructor call in the metadata and is therefore shared between all instances. This situation is almost never what you want.
To correct this problem, you must reset the collection dependency property value to a unique instance, as part of the class constructor call. Because the property is a read-only dependency property, you use the SetValue(DependencyPropertyKey, Object) method to set it, using the DependencyPropertyKey that is only accessible within the class.
public Aquarium() : base() { SetValue(AquariumContentsPropertyKey, new List<FrameworkElement>()); }
Now, if you ran that same test code again, you could see more expected results, where each Aquarium supported its own unique collection.
There would be a slight variation on this pattern if you chose to have your collection property be read-write. In that case, you could call the public set accessor from the constructor to do the initialization, which would still be calling the nonkey signature of SetValue(DependencyProperty, Object)within your set wrapper, using a public DependencyProperty identifier.
Reporting Binding Value Changes from Collection Properties
A collection property that is itself a dependency property does not automatically report changes to its subproperties. If you are creating bindings into a collection, this can prevent the binding from reporting changes, thus invalidating some data binding scenarios. However, if you use the collection type FreezableCollection<T> as your collection type, then subproperty changes to contained elements in the collection are properly reported, and binding works as expected.
To enable subproperty binding in a dependency object collection, create the collection property as type FreezableCollection<T>, with a type constraint for that collection to any DependencyObject derived class.
XAML Loading and Dependency Properties
The current WPF implementation of its XAML processor is inherently dependency property aware. The WPF XAML processor uses property system methods for dependency properties when loading binary XAML and processing attributes that are dependency properties. This effectively bypasses the property wrappers. When you implement custom dependency properties, you must account for this behavior and should avoid placing any other code in your property wrapper other than the property system methods GetValue and SetValue.
Prerequisites
This topic assumes that you understand dependency properties both as consumer and author and have read Dependency Properties Overview and Custom Dependency Properties. You should also have read XAML Overview (WPF) and XAML Syntax In Detail.
The WPF XAML Loader Implementation, and Performance
For implementation reasons, it is computationally less expensive to identify a property as a dependency property and access the property system SetValue method to set it, rather than using the property wrapper and its setter. This is because a XAML processor must infer the entire object model of the backing code based only on knowing the type and member relationships that are indicated by the structure of the markup and various strings.
The type is looked up through a combination of xmlns and assembly attributes, but identifying the members, determining which could support being set as an attribute, and resolving what types the property values support would otherwise require extensive reflection using PropertyInfo. Because dependency properties on a given type are accessible as a storage table through the property system, the WPF implementation of its XAML processor uses this table and infers that any given property ABC can be more efficiently set by calling SetValue on the containing DependencyObject derived type, using the dependency property identifier ABCProperty.
Implications for Custom Dependency Properties
Because the current WPF implementation of the XAML processor behavior for property setting bypasses the wrappers entirely, you should not put any additional logic into the set definitions of the wrapper for your custom dependency property. If you put such logic in the set definition, then the logic will not be executed when the property is set in XAML rather than in code.
Similarly, other aspects of the XAML processor that obtain property values from XAML processing also use GetValue rather than using the wrapper. Therefore, you should also avoid any additional implementation in the get definition beyond the GetValue call.
The following example is a recommended dependency property definition with wrappers, where the property identifier is stored as a public static readonly field, and the get and set definitions contain no code beyond the necessary property system methods that define the dependency property backing.
public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register( "AquariumGraphic", typeof(Uri), typeof(AquariumObject), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnUriChanged) ) ); public Uri AquariumGraphic { get { return (Uri)GetValue(AquariumGraphicProperty); } set { SetValue(AquariumGraphicProperty, value); } }
Properties How-to Topics
How to: Implement a Dependency Property
https://msdn.microsoft.com/ko-kr/library/ms750428(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/ms750428(v=vs.110).aspx
This example shows how to back a common language runtime (CLR) property with a DependencyProperty field, thus defining a dependency property. When you define your own properties and want them to support many aspects of Windows Presentation Foundation (WPF) functionality, including styles, data binding, inheritance, animation, and default values, you should implement them as a dependency property.
Example
The following example first registers a dependency property by calling the Register method. The name of the identifier field that you use to store the name and characteristics of the dependency property must be the Name you chose for the dependency property as part of the Register call, appended by the literal string Property. For instance, if you register a dependency property with a Name of Location, then the identifier field that you define for the dependency property must be named LocationProperty.
In this example, the name of the dependency property and its CLR accessor is State; the identifier field is StateProperty; the type of the property is Boolean; and the type that registers the dependency property is MyStateControl.
If you fail to follow this naming pattern, designers might not report your property correctly, and certain aspects of property system style application might not behave as expected.
You can also specify default metadata for a dependency property. This example registers the default value of the State dependency property to be false.
public class MyStateControl : ButtonBase { public MyStateControl() : base() { } public Boolean State { get { return (Boolean)this.GetValue(StateProperty); } set { this.SetValue(StateProperty, value); } } public static readonly DependencyProperty StateProperty = DependencyProperty.Register( "State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false)); }
For more information about how and why to implement a dependency property, as opposed to just backing a CLR property with a private field, see Dependency Properties Overview.
How to: Add an Owner Type for a Dependency Property
This example shows how to add a class as an owner of a dependency property registered for a different type. By doing this, the WPF XAML reader and property system are both able to recognize the class as an additional owner of the property. Adding as owner optionally allows the adding class to provide type-specific metadata.
In the following example, StateProperty is a property registered by the MyStateControl class. The class UnrelatedStateControl adds itself as an owner of the StateProperty using the AddOwner method, specifically using the signature that allows for new metadata for the dependency property as it exists on the adding type. Notice that you should provide common language runtime (CLR) accessors for the property similar to the example shown in the How to: Implement a Dependency Property example, as well as re-expose the dependency property identifier on the class being added as owner.
Without wrappers, the dependency property would still work from the perspective of programmatic access using GetValue or SetValue. But you typically want to parallel this property-system behavior with the CLR property wrappers. The wrappers make it easier to set the dependency property programmatically, and make it possible to set the properties as XAML attributes.
To find out how to override default metadata, see How to: Override Metadata for a Dependency Property.
Example
public class MyStateControl : ButtonBase { public MyStateControl() : base() { } public Boolean State { get { return (Boolean)this.GetValue(StateProperty); } set { this.SetValue(StateProperty, value); } } public static readonly DependencyProperty StateProperty = DependencyProperty.Register( "State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false)); }
public class UnrelatedStateControl : Control { public UnrelatedStateControl() { } public static readonly DependencyProperty StateProperty = MyStateControl.StateProperty.AddOwner(typeof(UnrelatedStateControl), new PropertyMetadata(true)); public Boolean State { get { return (Boolean)this.GetValue(StateProperty); } set { this.SetValue(StateProperty, value); } } }
How to: Register an Attached Property
This example shows how to register an attached property and provide public accessors so that you can use the property in both Extensible Application Markup Language (XAML) and code. Attached properties are a syntax concept defined by Extensible Application Markup Language (XAML). Most attached properties for WPF types are also implemented as dependency properties. You can use dependency properties on any DependencyObjecttypes.
Example
The following example shows how to register an attached property as a dependency property, by using the RegisterAttached method. The provider class has the option of providing default metadata for the property that is applicable when the property is used on another class, unless that class overrides the metadata. In this example, the default value of the IsBubbleSource property is set to false.
The provider class for an attached property (even if it is not registered as a dependency property) must provide static get and set accessors that follow the naming convention Set[AttachedPropertyName] and Get[AttachedPropertyName]. These accessors are required so that the acting XAML reader can recognize the property as an attribute in XAML and resolve the appropriate types.
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached( "IsBubbleSource", typeof(Boolean), typeof(AquariumObject), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender) ); public static void SetIsBubbleSource(UIElement element, Boolean value) { element.SetValue(IsBubbleSourceProperty, value); } public static Boolean GetIsBubbleSource(UIElement element) { return (Boolean)element.GetValue(IsBubbleSourceProperty); }
How to: Override Metadata for a Dependency Property
This example shows how to override default dependency property metadata that comes from an inherited class, by calling the OverrideMetadatamethod and providing type-specific metadata.
Example
By defining its PropertyMetadata, a class can define the dependency property's behaviors, such as its default value and property system callbacks. Many dependency property classes already have default metadata established as part of their registration process. This includes the dependency properties that are part of the WPF API. A class that inherits the dependency property through its class inheritance can override the original metadata so that the characteristics of the property that can be altered through metadata will match any subclass-specific requirements.
Overriding metadata on a dependency property must be done prior to that property being placed in use by the property system (this equates to the time that specific instances of objects that register the property are instantiated). Calls to OverrideMetadata must be performed within the static constructors of the type that provides itself as the forType parameter of OverrideMetadata. If you attempt to change metadata once instances of the owner type exist, this will not raise exceptions, but will result in inconsistent behaviors in the property system. Also, metadata can only be overridden once per type. Subsequent attempts to override metadata on the same type will raise an exception.
In the following example, the custom class MyAdvancedStateControl overrides the metadata provided for StateProperty by MyAdvancedStateControl with new property metadata. For instance, the default value of the StateProperty is now true when the property is queried on a newly constructed MyAdvancedStateControl instance.
public class MyStateControl : ButtonBase { public MyStateControl() : base() { } public Boolean State { get { return (Boolean)this.GetValue(StateProperty); } set { this.SetValue(StateProperty, value); } } public static readonly DependencyProperty StateProperty = DependencyProperty.Register( "State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false)); }
'프로그래밍 > WPF' 카테고리의 다른 글
Element Tree and Serialization (0) | 2016.11.01 |
---|---|
Base Elements (기본 요소) (0) | 2016.10.24 |
WPF 고급 - 끌어서 놓기 (Drag and Drop) (0) | 2016.10.21 |
WPF 고급 - 입력 (Input) (0) | 2016.10.15 |
ObservableCollection<T>, INotifyPropertyChanged Interface (0) | 2016.10.15 |