标签:button oid tar tao value stat btn delegate 需要
MSDN中有一句话:One of the primary architectural philosophies used in building WPF was a preference for properties over methods or Depenevents.
这句话的意思就是WPF的主要设计思想之一是侧重属性远胜于方法和事件,能用属性解决问题,坚决不用方法和事件。
两类一树:DependencyProperty & DependencyObject;LogicTree(逻辑树)
依赖属性的值不是从字段内存中读取,而是实时计算得到(支持绑定,默认值,继承,资源,样式,动画等)(DependencyProperty)
WPF提供了专门的读写依赖属性的方法(DependencyObject)
实现了依赖属性与数据类型(宿主)的解耦,可以动态的为对象扩展(增加)依赖属性(Attached Property)
定义一个属性
CLR定义属性的方法:
class MyButton
{
public Brush Background { get; set; } = new SolidColorBrush(Colors.Transparent);
}
CLR读写属性的方法:
MyButton myButton = new MyButton();
// 读
Console.WriteLine($"按钮的背景色:{myButton.Background}");
// 写
myButton.Background = new SolidColorBrush(Colors.Red);
WPF有个叫DependencyProperty的类,它用以抽象依赖属性。为依赖对象抽象类定义一个依赖属性,即是用一个DependencyProperty实例表示一个依赖属性的过程;依赖属性“背后”的类型,默认值,宿主,合法性检验,属性值改变时触发事件都是DependencyProperty的构造参数。
模型:DependencyProperty实例 <=> 类的依赖属性
针对类的某个依赖属性DP,该类的所有实例读写依赖属性DP,只能用专门的实例方法读写【表示依赖属性DP的那个DependencyProperty实例】,这也是拥有依赖属性的类必须要继承DependencyObject的原因。
class MyButton : DependencyObject
{
// Using a DependencyProperty as the backing store for Brush. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BrushProperty =
DependencyProperty.Register("Brush", typeof(Brush), typeof(MyButton), new PropertyMetadata(new SolidColorBrush(Colors.Transparent)));
public static void Main()
{
MyButton myButton = new MyButton();
// 读
myButton.GetValue(MyButton.BrushProperty);
// 写
myButton.SetValue(MyButton.BrushProperty, new SolidColorBrush(Colors.Red));
}
}
为什么BrushProperty是static?
因为所有实例读取属性时,需要的是同一个DependencyProperty实例。假设我们不搞成static,每次实例化都会Register一次,返回的不是同一个DP实例。即使Register内部实现了只会注册和实例化一次,那么static更合理,保证整个应用程序只有一个栈中的引用指向DP实例,通过类名即可拿到DP实例。
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback);
public delegate object CoerceValueCallback(DependencyObject d, object baseValue);
public delegate bool ValidateValueCallback(object value);
public delegate void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e);
当为依赖属性赋值时,首先,执行CoerceValueCallback,主要目的是矫正属性值,返回一个Object,可以返回DependencyProperty.UnsetValue拒绝本次赋值。然后,执行ValidateValueCallback,true则接受本次赋值,false,则拒绝本次赋值。最后,调用PropertyChangedCallback。
ValidateValueCallback和CoerceValueCallback的区别和关系
ValidateValueCallback的实参是CoerceValueCallback的返回值,CoerceValueCallback能获知对象的其他的属性值,而ValidateValueCallback只能知道被修改的属性即将被赋予的值。
MyButton myButton = new MyButton();
// 读
myButton.GetValue(MyButton.BrushProperty);
// 写
myButton.SetValue(MyButton.BrushProperty, new SolidColorBrush(Colors.Red));
我们可以利用GetValue或SetValue读写实例的依赖属性,但是我们也可以简单封装一下提供等价的CLR属性,我们通过CLR属性读写依赖属性。
public Brush Brush
{
get { return (Brush)GetValue(BrushProperty); }
set { SetValue(BrushProperty, value); }
}
注意
不要在CLR属性包装器的访问器中,添加属性值的合理性逻辑检查或属性值改变要触发的事件,因为用户仍旧可以绕过CLR属性包装器利用GetValue和SetValue读写依赖属性。
为什么要提供CLR属性包装器?
public class Gauge : Control
{
//注册CurrentReading依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register(
"CurrentReading",
typeof(double),
typeof(Gauge),
new FrameworkPropertyMetadata(
Double.NaN,
FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(OnCurrentReadingChanged),
new CoerceValueCallback(CoerceCurrentReading)
),
new ValidateValueCallback(IsValidReading)
);
//注册MaxReading依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
public static readonly DependencyProperty MaxReadingProperty = DependencyProperty.Register(
"MaxReading",
typeof(double),
typeof(Gauge),
new FrameworkPropertyMetadata(
double.NaN,
FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(OnMaxReadingChanged),
new CoerceValueCallback(CoerceMaxReading)
),
new ValidateValueCallback(IsValidReading)
);
//注册MinReading依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
public static readonly DependencyProperty MinReadingProperty = DependencyProperty.Register(
"MinReading",
typeof(double),
typeof(Gauge),
new FrameworkPropertyMetadata(
double.NaN,
FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(OnMinReadingChanged),
new CoerceValueCallback(CoerceMinReading)
),
new ValidateValueCallback(IsValidReading));
public Gauge() : base()
{
}
//属性包装器,通过它来暴露CurrentReading的值
public double CurrentReading
{
get { return (double)GetValue(CurrentReadingProperty); }
set { SetValue(CurrentReadingProperty, value); }
}
//属性包装器,通过它来暴露MaxReading的值
public double MaxReading
{
get { return (double)GetValue(MaxReadingProperty); }
set { SetValue(MaxReadingProperty, value); }
}
//属性包装器,通过它来暴露MinReading的值
public double MinReading
{
get { return (double)GetValue(MinReadingProperty); }
set { SetValue(MinReadingProperty, value); }
}
//验证value是否有效,如果返回True表示验证通过,否则会提示异常
public static bool IsValidReading(object value)
{
Double v = (Double)value;
return (!v.Equals(Double.NegativeInfinity) && !v.Equals(Double.PositiveInfinity));
}
//在CoerceCurrentReading加入强制判断赋值
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;
}
//在CoerceMaxReading加入强制判断赋值
private static object CoerceMaxReading(DependencyObject d, object value)
{
Gauge g = (Gauge)d;
double max = (double)value;
if (max < g.MinReading) max = g.MinReading;
return max;
}
//在CoerceMinReading加入强制判断赋值
private static object CoerceMinReading(DependencyObject d, object value)
{
Gauge g = (Gauge)d;
double min = (double)value;
if (min > g.MaxReading) min = g.MaxReading;
return min;
}
//当CurrentReading值改变的时候,调用MinReading和MaxReading的CoerceValue回调委托
private static void OnCurrentReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(MinReadingProperty);
d.CoerceValue(MaxReadingProperty);
}
//当MaxReading值改变的时候,调用MinReading和CurrentReading的CoerceValue回调委托
private static void OnMaxReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(MinReadingProperty);
d.CoerceValue(CurrentReadingProperty);
}
//当OnMinReading值改变的时候,调用CurrentReading和MaxReading的CoerceValue回调委托
private static void OnMinReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(MaxReadingProperty);
d.CoerceValue(CurrentReadingProperty);
}
}
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp3"
WindowStartupLocation="CenterScreen"
Title="Callback_Validation_DPs" Height="400" Width="400">
<StackPanel Orientation="Vertical">
<local:Gauge x:Name="gauge1" MaxReading="100" MinReading="0" Height="20"/>
<StackPanel Orientation="Horizontal" Height="60">
<Label Content="当前值为 : "/>
<Label Background="Yellow" BorderBrush="Black" BorderThickness="1"
IsEnabled="False" Content="{Binding ElementName=gauge1, Path=CurrentReading}" Height="25" VerticalAlignment="Top" />
</StackPanel>
<Button x:Name="btnSetBelowMin" Content="设置为 -100"
Click="btnSetBelowMin_Click"/>
<Button x:Name="btnSetAboveMax" Content="设置为 1000"
Click="btnSetAboveMax_Click"/>
<Button x:Name="btnTest" Content="Test" Click="btnTest_Click"/>
</StackPanel>
</Window>
当相互作用的几个依赖属性其中一个发生变化时,在它的PropertyChangeCallback中调用受它影响的依赖属性的CoerceValue,这样才能保证相互作用关系的正确性。拿此例来说,无论我们对Max,Min,Current的赋值顺序如何,都能保证三者的值的合理性。CorceValueCallback虽然强制纠正了我们要赋给依赖属性的原始值,但是这个原始值仍旧会被WPF保存,在条件成立后,再次调用CorceValueCallback依赖属性的值会从纠正值变成原始值。
单独调用CorceValueCallback虽然也可以改变依赖属性的值,但不会触发PropertyChangedCallback。
class MyButton : Button
{
public int Demo
{
get { return (int)GetValue(DemoProperty); }
set { SetValue(DemoProperty, value); }
}
// Using a DependencyProperty as the backing store for Demo. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DemoProperty =
DependencyProperty.Register("Demo", typeof(int), typeof(MyButton), new FrameworkPropertyMetadata(2021) {Inherits=true});
}
class MyStackPanel: StackPanel
{
public int Demo
{
get { return (int)GetValue(DemoProperty); }
set { SetValue(DemoProperty, value); }
}
// Using a DependencyProperty as the backing store for Demo. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DemoProperty = MyButton.DemoProperty.AddOwner(typeof(MyStackPanel),new FrameworkPropertyMetadata(0) { Inherits = true});
}
截断原因:
如果为依赖属性赋予了本地值,根据优先级原则,其他的样式,动画,数据绑定,触发器都会被忽略掉,怎么干掉本地值呢?
从DependencyObject中继承的ClearValue
public void ClearValue(DependencyProperty dp);
myElement.ClearValue(FrameworkElement.MarginProperty);
WPF控件的属性值由实时的逻辑计算得到,而非存储在字段内存,WPF为什么这么做呢?
附加属性的作用就是让属性与数据类型解耦,我们可以使用附加属性动态扩展类的属性。
放置到DockPanel中的控件,必须具备属性Dock(Dock.Top,Dock.Buttom,Dock.Left,Dock.Right),才能被DockPanel读到控件的布局信息以正确布局。
放置到Grid中的控件,必须具备属性Row,Column才能被Grid读到控件的布局信息以正确布局。
放置到Canvas中的控件,必须具备属性Canvas.Top,Canvas.Left才能被Canvas读到控件的布局信息以正确布局。
每个控件都可能放到各种布局容器中,那么每个控件必须都必须带有Dock,Row,Column,Canvas.Top等属性。这样做看似没有任何毛病,但是某个控件,如果没有被放置到Grid,那么它的Row,Column就没有什么作用了,只是占有内存。所以,我们可以为对象动态添加属性,在对象需要的时候,添加属性,在对象不需要的时候,不添加即可。
class Person : DependencyObject
{
}
class School: DependencyObject
{
public static int GetGrade(DependencyObject obj)
{
return (int)obj.GetValue(GradeProperty);
}
public static void SetGrade(DependencyObject obj, int value)
{
obj.SetValue(GradeProperty, value);
}
// Using a DependencyProperty as the backing store for Grade. This enables animation, styling, binding, etc...
public static readonly DependencyProperty GradeProperty =
DependencyProperty.RegisterAttached("Grade", typeof(int), typeof(School), new PropertyMetadata(0));
}
// 使用附加属性
Person person = new Person();
School.SetGrade(person, 7);
<Grid>
<Button Grid.Column="0" Grid.Row="0"/>
</Grid>
Button button = new Button();
Grid.SetRow(button, 0);
Grid.SetColumn(button, 0);
<Button Grid.Column="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=Self}}"/>
btn.SetBinding(Grid.Row,new Binding("Value"){source = slider});
标签:button oid tar tao value stat btn delegate 需要
原文地址:https://www.cnblogs.com/LiuwayLi/p/14773822.html