Topic: C# – CoerceValueCallback
标题: C# – CoerceValueCallback合并、替换元数据值
像在the PropertyMetadta.CoerceValueCallback property中说道
As in the references page the PropertyMetadta.CoerceValueCallback property
属性元数据的该回调方法一般不是公有方法,所以, 这个值通常情况下对于只是使用dependency property元数据的条件下不是很重要。使用该属性的一个可能原因是元数据的子类可以应用他们合并逻辑,如果基类和重载类有这个CoerceCallback属性。一般的,CoerceValueCallback的默认合并逻辑是替换。
The callbacks in property metadata are not typically public members on the containing type, so the value of this property is not important for most scenarios that just consume an existing dependency property's metadata. One reason this property is exposed is so that metadata subclasses can perform their desired merge logic if both base metadata and overriding/adding metadata specify a CoerceValueCallback. However, the default merge logic for aCoerceValueCallback is to replace the previous one.
在CoerceValueCallback Delegate一文中:
And in the CoerceValueCallback Delegate
CoerceValueCallback调用时间可以是属性系统的任何时间或者是DependencyObjects上显示的调用CoerceValue 方法,调用时,该属性的标识符作为参数dp的值。
The CoerceValueCallback for a dependency property is invoked any time that the property system or any other caller calls CoerceValue on aDependencyObject instance, specifying that property's identifier as the dp.
使用CoerceValueCallback的最大可能原因是当你希望在子类中重载基类并提供一个计算在子类中计算重载的dependency property的值的方法。(比如通过OverrideMetaData(Type, PropertyMetadata)...重载父类中dependency property的值).
The biggest reason that you would use a CoerceValueCallback is that when you try to override a base class and try to provide new method to calculate the value of a dependency property that the derived classes override from the base class (e.g. with the OverrideMetaData(Type, PropertyMetadata)...
你可以按照如下的情况,设置CoerceValueCallback值;
You can set the instance of CoerceValueCallback on the following conditions.
· 在新类中定义新dependency property, 调用 Register.
· 当继承一个已有dependency property的基类上继承子类,在该已有的 dependency property上重载元数据(调用 OverrideMetadata(Type, PropertyMetadata)),
· 在新类上调用AddOwner(Type, PropertyMetadata), 在已有的dependency property上加入新的元数据
· Define a new dependency property on a new class, using either signature of Register, giving the metadata as the typeMetadata value.
· Override the metadata (call OverrideMetadata(Type, PropertyMetadata)) for an existing dependency property, when you derive from the class that owns the dependency property.
· Add an existing dependency property to a new DependencyObject class, using new metadata, by calling AddOwner(Type, PropertyMetadata).
为什么我门需要CoerceValueCallback,我们来看一个例子:
Why we should use the CoerceValueCallback? Given an example,
假如我们有一个ListView的继承类,叫做CustomListView,我们把GridViewColumn替换成CustomGridViewColumn,在CustomGridViewColumn中,我们重载该dependency property
We have a ListView derived class, called CustomListView, and we replace the GridViewColumn with CustomGridViewColumn. And in the CustomGridViewColumn, we have the following override
static CustomGridViewColumn() { WidthProperty.OverrideMetadata( typeof(CustomGridViewColumn), new FrameworkPropertyMetadata(WidthProperty.DefaultMetadata.DefaultValue, null, OnCoerceWidthValue)); }
在OnCoerceWithValue方法中,我们可以这么做
And the OnCoerceWithValue function is as follow.
private static object OnCoerceWidthValue(DependencyObject d, object basevalue) { var column = d as CustomGridViewColumn; if (column == null) { return basevalue; } var width = (double)basevalue; if (column.IsFixedSize) { if (column.FixedWidth > 0) { return column.FixedWidth; } } width = column.MinWidth >= 0 ? Math.Max(width, column.MinWidth) : width; width = column.MaxWidth >= 0 ? Math.Min(width, column.MaxWidth) : width; if (column.IsFixedSize) { if (column.FixedWidth < 1 || double.IsNaN(column.FixedWidth)) { column.FixedWidth = width; } } return width; }
如你所示例,扩展类定义了如何确定该列宽度的方法。
As you can see, the extended class has its own way of determine the width of a column;
我们还准备了一个例子,该例中,假设我们要写一个名字为Shirt的自定义控件/用户控件,并且当该控件表示的类型没有扣子时,我们提供了该控件扣子类型的默认值。(比如说, ButtonColors.Black – 初始值)。
For another example, suppose that we are writing a UserControl/CustomControl, whose name is Shirt, and we have default value for those kind of Shirt w/ buttons (those Shirt has no buttons, we will set the ButtonColor to ButtonColors.Black -- the starting value).
public partial class Shirt : UserControl { #region Dependency Properties public static readonly DependencyProperty ButtonColorProperty = DependencyProperty.Register( "ButtonColor", typeof(ButtonColors), typeof(Shirt), new FrameworkPropertyMetadata(ButtonColors.White, null, CoerceButtonColor) ); private static object CoerceButtonColor(DependencyObject d, object basevalue) { ShirtTypes newShirtType = (d as Shirt).ShirtType; if (newShirtType == ShirtTypes.Dress || newShirtType == ShirtTypes.Bowling) { return ButtonColors.Black; } return ButtonColors.None; } public ButtonColors ButtonColor { get { return (ButtonColors)GetValue(ButtonColorProperty); } set { SetValue(ButtonColorProperty, value); } } public static readonly DependencyProperty ShirtTypeProperty = DependencyProperty.Register( "ShirtType", typeof(ShirtTypes), typeof(Shirt), new FrameworkPropertyMetadata(ShirtTypes.Polo, null) ); public ShirtTypes ShirtType { get { return (ShirtTypes)GetValue(ShirtTypeProperty); } set { SetValue(ShirtTypeProperty, value); } } #endregion Dependency Properties #region Properties #endregion Properties #region Constructors public Shirt() { InitializeComponent(); ButtonColor = ButtonColors.White; ShirtType = ShirtTypes.Polo; } #endregion Constructors }
注意上面的CoerceButtonColor函数名,结果是用Shirt的类型决的。
Note the code on name CoerceButtonColor,fthe resulting value is determined by the type of Shirt the control represent.
<UserControl x:Class="DemoCoerceValueCallback.Shirt" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DemoCoerceValueCallback.Converters" xmlns:constants="clr-namespace:DemoCoerceValueCallback" x:Name="_Shirt" > <UserControl.Resources> <local:ButtonColorConverter x:Key="ButtonColorConverter" /> </UserControl.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <!-- do this: Background="{Binding RelativeSource={RelativeSource AncestorType={x:Type constants:Shirt}}, Path=ButtonColor, Converter={StaticResource ButtonColorConverter}}" do NOt do this: Background="{Binding Source={RelativeSource AncestorType={x:Type constants:Shirt}}, Path=ButtonColor, Converter={StaticResource ButtonColorConverter}}" or do this: Background="{Binding ElementName=_Shirt, Path=ButtonColor, Converter={StaticResource ButtonColorConverter}}" --> <Button x:Name="Button" Background="{Binding RelativeSource={RelativeSource AncestorType={x:Type constants:Shirt}}, Path=ButtonColor, Converter={StaticResource ButtonColorConverter}}" Content="" Grid.Row="0"/> <Label x:Name="Label" Content="Label" Grid.Row="1" /> </Grid> </UserControl>
该例子中用到的其他类型是
The Supporting classes include
public enum ButtonColors { Black, White, None, } public enum ShirtTypes { Dress, Bowling, Polo, } And Converters. public class ButtonColorConverter : System.Windows.Data.IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var buttonColor = (ButtonColors)value; var color = Colors.Red; switch (buttonColor) { case ButtonColors.Black: color = Colors.Aqua; break; case ButtonColors.White: color = Colors.Beige; break; case ButtonColors.None: color = Colors.Blue; break; default: color = Colors.Red; break; } return new SolidColorBrush(color); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
所以总的来说,CoerceValueCallback是一个以元数据方式提供给DependencyProperty的方法,利用它可以让程序员合并(或者是替换)元数据的子类DependencyProperty值。 一般来说你可以在如下三总方法中设置回调 1。在新类中定义新的DependencyProperty;2。OverrideMetadata(Type, ProeprtyMetadata)重载已有DependencyProperty的元数据;3。在新类中加入已有类。
So, in conclusion: CoerceValueCallback is the mean provided as a MetaData to the DependencyProperty which allows developer to merge (or replace, which is the default merge logic) in metadata subclass. Generally you can pass in your own Coerce logic in three conditions, 1. Defining a new dependency property on a new class. 2. Override the metadata for an existing dependency with OverrideMetadata(Type, ProeprtyMetadata) property. 3. Add a existing dependency Property to a new DependencyObject class.
参考:
References
the PropertyMetadta.CoerceValueCallback property
相关推荐
此外,可以利用`CoerceValueCallback`来强制属性值保持在特定范围内,或者在属性值改变时进行额外的检查。 4. **实现绑定** 数据绑定是依赖属性的主要应用场景。在WPF中,可以将控件的依赖属性与数据模型的某个...
验证规则可以定义在`ValidationRule`子类中,然后附加到依赖属性的元数据上。当属性值更改并尝试绑定时,会触发验证。 ```csharp public class MyValidationRule : ValidationRule { public override ...
- **元数据**:每个依赖属性都有元数据,可以存储附加信息,如默认值、更改回调等。 4. **依赖属性注册** - 注册过程包含了属性名、属性类型、所属类以及可能的元数据。注册后,依赖属性可以在整个应用程序范围内...
尽管不能直接改变只读属性的值,但可以通过动画改变其数据源的值来间接实现动画效果。 6. **样式和模板**:只读依赖属性也可以在样式和模板中使用,以决定UI元素的外观和行为。 在“WpfApplication2”项目中,...
依赖属性是WPF中数据绑定的基础,它提供了属性值的改变通知和值验证等功能。在自定义控件中,我们常常需要定义依赖属性以便与外部数据源进行绑定。定义依赖属性的步骤如下: 1. 使用DependencyProperty.Register方法...
3. **回调函数**:如`PropertyChangedCallback`和`CoerceValueCallback`,这些函数会在属性值变化时被调用,提供了自定义逻辑的机会。 4. **数据绑定到依赖属性**:依赖属性天然支持数据绑定,无需额外工作即可实现...
该方法接收多个参数,如属性名、属性类型、默认值以及属性元数据,其中元数据可以包含回调函数,用于处理属性更改时的逻辑。 依赖项属性的存储机制基于一个全局的哈希表,这个哈希表由`DependencyProperty`类维护。...
Wpf分析仪 用于WPF的Roslyn分析仪。 1.x版本适用于Visual Studio ... CoerceValueCallback的名称应与注册名称匹配。 ValidateValueCallback的名称应与注册名称匹配。 [DependsOn(target)]必须存在。 默认值类型