导语
今天用到WPF依赖项属性,距上次看已经过来很长时间,故今天复习下做个笔记,同时希望能帮助到要学习WPF依赖项属性的陪朋友。
依赖项属性
依赖项属性是WPF架构中用C#在标准C#基础属性上做的封装。依赖项属性是WPF样式、动画、数据绑定等的基础,可以说如果没有依赖项属性就没有WPF的样式、动画、数据绑定等灵活的应用。
依赖项属性必须在继承DependencyObject的类中声名。好在大部分WPF控件类都已经继承了DependencyObject类,因为基础类Visual继承了DependencyObject 源代码如下
namespace System.Windows.Media
{
/// <summary>提供的呈现支持 WPF, ,其中包括命中测试、 坐标转换和边界框计算。</summary>
public abstract class Visual : DependencyObject, DUCE.IResource
{
internal static readonly UncommonField<BitmapEffectState> BitmapEffectStateField = new UncommonField<BitmapEffectState>();
private static readonly UncommonField<Dictionary<ICyclicBrush, int>> CyclicBrushToChannelsMapField = new UncommonField<Dictionary<ICyclicBrush, int>>();
好了,废话不多说,看看怎么写自己的依赖项属性吧。
1.声明依赖项属性
依赖项属性必须是Public 的静态属性。
public static readonly DependencyProperty TestProperty;
2.注册依赖项属性
DependencyProperty 类没有提供public构造函数,所以我们只能用DependencyProperty 的静态方法去注册依赖项属性。
注意:必须在使用依赖项属性之前注册依赖项属性,所以依赖项属性总是在所在类静态构造函数或者静态字段中注册。
static DependentClass()
{
//依赖项属性 :因为DependencyProperty无public构造函数、只能用静态方法注册。
//依赖项属性必须在使用之前注册,所以最好写在静态构造函数中,也可以直接写在静态字段中,
//总之要保证使用前要注册
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.DefaultValue = 100;
meta.CoerceValueCallback = new CoerceValueCallback(TestPropertyCoerceValueCallback);
meta.PropertyChangedCallback = new PropertyChangedCallback(TestPropertyProertyChangedCallback);
TestProperty = DependencyProperty.Register("Test", typeof(int), typeof(DependentClass),
meta, new ValidateValueCallback(TestPropertyValidateValueCallback));
}
DependencyProperty.Register()方法的参数分别为:依赖项属性名、依赖项属性数据类型、依赖项属性所在类类型、依赖项属性元数据和依赖项属性数据有效性验证回调函数。
其中回调函数行形式为
private static bool TestPropertyValidateValueCallback(object value)
{
//最先执行
return true;
}
这个回调函数最先执行,依赖项属性注册时执行,依赖项属性数据改变时执行。在这个函数中只提供属性值,并不提供依赖项属性,所以没法做一些验证,在这里主要验证依赖项属性的值的类型和格式是否有效,有效返回ture,无效返回false。
依赖项属性元数据这个对象属性有限,可以自己看看智能提示说明,都很好理解。这里主要说一下它的两个回调函数。
private static object TestPropertyCoerceValueCallback(DependencyObject sender, object value)
{
//这个回调函数必须返回object,之后执行
return value;
}
这个函数在ValidateValueCallback之后执行,在这里可以对依赖项属性值做详细验证(如依赖项属性包含的最大、最小值、newValue oldValue等)
最后是
public static void TestPropertyProertyChangedCallback(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
//最后执行
}
这个回调是属性值已经发生改变时执行。因为依赖项属性值的改变可以不通过包装器(就是咱们熟悉的get{} set{}属性,接下来说明)而通过其他(如数据绑定、动画等),所以依赖项属性值变化要触发的一些动作可以写在这里。
3.包装依赖项属性
依赖项属性也是一种属性,所以要包装一下,暴露出一般属性的特性方便使用
public int Test
{
get { return (int)GetValue(TestProperty); }
set
{
SetValue(TestProperty, value);
}
}
这样依赖项属性就OK拉。
共享依赖项属性
如果依赖类要添加的依赖项属性和既有类中依赖项属性完全相同,可直接用既有类依赖项属性,只需要在静态构造函数中注册一下就可以
class TestProperty1 : UIElement
{
//测试共享依赖属性
public int Test
{
set
{
SetValue(TestProperty, value);
}
get
{
return (int)GetValue(TestProperty) ;
}
}
public static readonly DependencyProperty TestProperty;
static TestProperty1()
{
TestProperty = DependentClass.TestProperty.AddOwner(typeof(TestProperty1));
}
}
附加属性
附加属性是一种特殊的依赖项属性。关于附加属性的说明很多,我简单明了的总结就是:被用到在注册类外的其他类中的依赖项属性。比如Grid.row Gird.column 几乎可以用在任何类中。附加属性是一种依赖属性,只有在注册时调用RegisterAttached方法。包装器稍微不一样,看如下代码
static AttachClass()
{
//依赖项属性 :因为DependencyProperty无public构造函数、只能用静态方法注册。
//依赖项属性必须在使用之前注册,所以最好写在静态构造函数中,也可以直接写在静态字段中,
//总之要保证使用前要注册
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.DefaultValue = 100;
meta.CoerceValueCallback = new CoerceValueCallback(TestPropertyCoerceValueCallback);
meta.PropertyChangedCallback = new PropertyChangedCallback(TestPropertyProertyChangedCallback);
TestProperty = DependencyProperty.RegisterAttached("Test", typeof(int), typeof(AttachClass),
meta, new ValidateValueCallback(TestPropertyValidateValueCallback));
}
private static bool TestPropertyValidateValueCallback(object value)
{
//最先执行
return true;
}
public static void TestPropertyProertyChangedCallback(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
//最后执行
}
private static object TestPropertyCoerceValueCallback(DependencyObject sender, object value)
{
//这个回调函数必须返回object,之后执行
return value;
}
}
好了,就写这么多吧。
附测试程序代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace TestDependencyProperty
{
class DependentClass:UIElement
{
public static readonly DependencyProperty TestProperty;
public int Test
{
get { return (int)GetValue(TestProperty); }
set
{
SetValue(TestProperty, value);
}
}
static DependentClass()
{
//依赖项属性 :因为DependencyProperty无public构造函数、只能用静态方法注册。
//依赖项属性必须在使用之前注册,所以最好写在静态构造函数中,也可以直接写在静态字段中,
//总之要保证使用前要注册
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.DefaultValue = 100;
meta.CoerceValueCallback = new CoerceValueCallback(TestPropertyCoerceValueCallback);
meta.PropertyChangedCallback = new PropertyChangedCallback(TestPropertyProertyChangedCallback);
TestProperty = DependencyProperty.Register("Test", typeof(int), typeof(DependentClass),
meta, new ValidateValueCallback(TestPropertyValidateValueCallback));
}
private static bool TestPropertyValidateValueCallback(object value)
{
//最先执行
return true;
}
public static void TestPropertyProertyChangedCallback(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
//最后执行
}
private static object TestPropertyCoerceValueCallback(DependencyObject sender, object value)
{
//这个回调函数必须返回object,之后执行
return value;
}
}
class TestProperty1 : UIElement
{
//测试共享依赖属性
public int Test
{
set
{
SetValue(TestProperty, value);
}
get
{
return (int)GetValue(TestProperty) ;
}
}
public static readonly DependencyProperty TestProperty;
static TestProperty1()
{
TestProperty = DependentClass.TestProperty.AddOwner(typeof(TestProperty1));
}
}
//附件属性 ,注意自己需要记住区别:附加属性不被用到定义属性的类,而是用到其他的类
class AttachClass : UIElement
{
public static readonly DependencyProperty TestProperty;
public static void SetTest(UIElement element, int value)
{
element.SetValue(TestProperty, value);
}
public static int GetTest(UIElement element)
{
return (int)element.GetValue(TestProperty);
}
public int Test
{
get { return (int)GetValue(TestProperty); }
set
{
SetValue(TestProperty, value);
}
}
static AttachClass()
{
//依赖项属性 :因为DependencyProperty无public构造函数、只能用静态方法注册。
//依赖项属性必须在使用之前注册,所以最好写在静态构造函数中,也可以直接写在静态字段中,
//总之要保证使用前要注册
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.DefaultValue = 100;
meta.CoerceValueCallback = new CoerceValueCallback(TestPropertyCoerceValueCallback);
meta.PropertyChangedCallback = new PropertyChangedCallback(TestPropertyProertyChangedCallback);
TestProperty = DependencyProperty.RegisterAttached("Test", typeof(int), typeof(AttachClass),
meta, new ValidateValueCallback(TestPropertyValidateValueCallback));
}
private static bool TestPropertyValidateValueCallback(object value)
{
//最先执行
return true;
}
public static void TestPropertyProertyChangedCallback(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
//最后执行
}
private static object TestPropertyCoerceValueCallback(DependencyObject sender, object value)
{
//这个回调函数必须返回object,之后执行
return value;
}
}
}
<Window x:Class="TestDependencyProperty.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loacl ="clr-namespace:TestDependencyProperty"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid >
<StackPanel Background="Green" loacl:AttachClass.Test="20">
<loacl:MyClass Click="mc_Click" x:Name="mc"
MyTest="{Binding Path=Value}" Content="{Binding ElementName=test2, Path=Test}"></loacl:MyClass>
<loacl:DependentClass x:Name="test1" Test="20"></loacl:DependentClass>
<loacl:TestProperty1 x:Name="test2" Test ="30"></loacl:TestProperty1>
</StackPanel>
</Grid>
</Window>
上一篇: