WPF基础——依赖属性
开始
依赖属性是WPF中的新概念,相对地,普通的属性被称为CLR属性(Common Language Runtime)
依赖属性与CLR属性最大的不同就是依赖属性的属性值可以通过Binding对象绑定到其他对象上,同时节省了UI元素实例的属性内存开销
依赖对象:拥有依赖属性的类称为依赖对象,在初始化时并不分配依赖属性的内存空间,只提供获取默认值、借用其他对象数据或实时分配空间的能力,WPF中的所有自带UI控件都是依赖对象
使用依赖属性
依赖属性的基本使用
1
2
3
4
5
6
7
8
9
10
11
public class Student : DependencyObject {
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register(nameof(Name), typeof(string), typeof(Student));
// 依赖属性的包装器,便于外界访问依赖属性
// DependencyObject类提供了GetValue方法和SetValue方法,用于访问依赖属性
public string Name {
get => (string)GetValue(NameProperty);
set => SetValue(NameProperty, value);
}
}
Register方法用于注册一个依赖属性
name
:指定哪个CLR属性作为依赖属性的包装器propertyType
:依赖属性的值类型ownerType
:依赖对象的类型typeMetadata
:DefaultMetadata
类型,表示依赖属性的默认值
依赖属性作为数据源
单纯的依赖属性通常作为Binding的目标,设置了包装器后,依赖属性可作为Binding的数据源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var student = new Student();
// 将Text1的Text属性绑定到student的NameProperty上,student作为目标
var bindingText1Student = new Binding() {
Source = Text1,
Path = new("Text")
};
BindingOperations.SetBinding(student, Student.NameProperty, bindingText1Student);
// 将student的Name绑定到Text2的TextProperty上,student作为数据源
var bindingStudentText2 = new Binding() {
Source = student,
Path = new("Name")
};
BindingOperations.SetBinding(Text2, TextBlock.TextProperty, bindingStudentText2);
依赖属性的存取原理
在DependencyObject
类中存在一个静态属性PropertyFromName
1
private static Hashtable PropertyFromName = new Hashtable();
该哈希表保存了通过Register方法注册的依赖属性
- 键:传入的CLR属性名和
ownerType
的hashcode异或运算得到 - 值:DependencyProperty对象
依赖对象中调用GetValue方法获取依赖属性的值,实际上是获取EffectiveValueEntry
实例中的值
1
2
3
4
5
6
public object GetValue(DependencyProperty dp) {
// ...
// 获取EffectiveValueEntry实例,再获取Value属性
return GetValueEntry(LookupEntry(dp.GlobalIndex), dp, null, RequestFlags.FullyResolved).Value;
}
附加属性
附加属性是指一个对象处于某个环境下,被附加的属性,本质依然是依赖属性
附加属性的声明
1
2
3
4
5
6
7
8
9
10
11
12
public class Student : DependencyObject {
public static readonly DependencyProperty NameProperty =
DependencyProperty.RegisterAttached("Name", typeof(string), typeof(Student), new UIPropertyMetadata(""));
public static string GetName(DependencyObject obj) {
return (string)obj.GetValue(NameProperty);
}
public static void SetName(DependencyObject obj, string value) {
obj.SetValue(NameProperty, value);
}
}
与一般依赖属性的不同
- 使用
RegisterAttached
方法进行注册 - 通过方法而不是CLR属性包装依赖属性
使用附加属性
1
2
3
4
5
6
7
// Human作为被附加类,继承DependencyObject
public class Human : DependencyObject {
}
var human = new Human();
Student.SetName(human, "hello"); // 将NameProperty附加到Human
string name = Student.GetName(human);
自定义组件
通过UserControl
组件可以自定义组件,使用依赖属性为组件添加自定义属性
C#类实现
- 自定义依赖属性和CLR包装属性
- 在构造器中设置Root组件的DataContext为当前类this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System.Windows;
using System.Windows.Controls;
namespace WpfDemo;
public partial class UserControl1 : UserControl {
public static readonly DependencyProperty AgeProPerty =
DependencyProperty.Register(nameof(Age), typeof(int), typeof(UserControl1), new UIPropertyMetadata());
public int Age {
get => (int)GetValue(AgeProPerty);
set => SetValue(AgeProPerty, value);
}
public UserControl1() {
InitializeComponent();
Root.DataContext = this;
}
}
xaml基本实现
- 使用
UserControl
组件作为根组件 - 定义布局组件的id为Root
d:DataContext
用于在预览调试时定义DataContext- 通过Binding绑定到自定义的CLR包装属性
1
2
3
4
5
6
7
8
9
10
11
12
<UserControl x:Class="WpfDemo.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfDemo"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="Root" d:DataContext="{d:DesignInstance local:UserControl1}">
<TextBlock Text="{Binding Age}"/>
</Grid>
</UserControl>
外部使用:传入字面量或Binding
1
<local:UserControl1 Age="30"/>
本文由作者按照 CC BY 4.0 进行授权