依赖属性
对比C#属性
首先传统CLR的属性是什么,它们由自动生成的 getter 和 setter 方法管理。比方:
- public class Person
- {
- public string Name { get; set; }
- }
复制代码 优点:简单、直接,实用于不需要高级功能的场景。
缺点:它的值是存储在对象的字段中,无法进行数据绑定、样式、动画等功能
WPF依赖属性(Dependency Properties)
那么在WPF中 属性的绑定赋值有很多复杂利用 比如数据绑定 样式绑定等高级功能,所以要有适配WPF技术中的 高级属性利用 也就是 依赖属性
- 定义与核心机制
依赖属性是 WPF 中基于 DependencyObject 类实现的一种特殊属性系统。它通过全局哈希表管理属性值,支持动态来源(如绑定、动画、样式等),并答应属性值继承和优先级控制。比方,Button 的 Background 属性可通过多种方式赋值(如直接设置、绑定命据或应用样式)
- 核心特点
内存优化高效存储:依赖属性通过哈希表存储值,相同控件的相同属性仅生存一份默认值,减少内存消耗。
动态绑定支持:支持与数据源绑定,实现 UI 与业务逻辑的实时同步。
值继承与优先级:子控件可继承父控件的属性值(如 FontSize),且差别来源的值按优先级生效(如当地设置 > 样式 > 继承值)。
变动关照:通过 PropertyChangedCallback 回调响应属性值厘革,常用于触发界面更新
优先级盘算与值决议
既然依赖属性 应对高级利用 多种动态绑定支持 表现值在控件上终极出现一个值 那肯定有优先级
优先级从高到低依次为:
- 动画值(如正在执行的动画效果)。
- 当地值(通过 SetValue 或XAML直接设置的值)。
- 绑定值(数据绑定的结果)。
- 样式与模板(通过 Style 或 ControlTemplate 设置的值)。
- 继承值(从父元素继承的值,如 FontSize)。
- 默认值(通过 PropertyMetadata 定义的默认值)
回调与验证机制
依赖属性支持通过回调函数实现动态控制和验证:
- 属性变动回调(PropertyChangedCallback):在值厘革时触发,用于更新UI或执行逻辑(如重绘控件)。
- 强制值回调(CoerceValueCallback):强制调整值(比方限制数值范围),优先级高于动画。
- 验证回调(ValidateValueCallback):验证值的正当性,若无效则抛出非常
WPF 自带的依赖属性
WPF 中绝大多数控件的属性都是依赖属性,它们通过 DependencyProperty 实现,支持动态绑定、动画、样式等特性。以下是常见的内置依赖属性分类及示例:
- 布局与外观
Width/Height:控件的尺寸(如 Button 的宽高)
Background/Foreground:背景致和前景致(如 TextBox 的背景致)
FontSize/FontFamily:字体样式(支持继承父容器的字体设置)
- 举动与交互
IsEnabled:控件是否可用(如禁用按钮)
Visibility:控件的可见性(支持 Collapsed、Hidden 等状态)
Command:绑定下令(如 Button 的点击事故绑定 MVVM 下令)
- 数据绑定与模板
ItemsSource:列表控件的数据源(如 ListBox 的绑定集合)
Content:内容属性(如 Label 的表现文本)
特点:这些属性通过全局哈希表存储,仅生存非默认值,减少内存占用
自定义依赖属性
这个会联合 自定义控件文章 来讲解
这里就先给个 示例
重要步骤就是 定义控件 注册依赖属性DependencyProperty 包装clr 定义样式 和 布局利用
- public class NumericBox : Control
- {
- // 注册依赖属性(含验证)
- public static readonly DependencyProperty ValueProperty =
- DependencyProperty.Register(
- "Value",
- typeof(double),
- typeof(NumericBox),
- new FrameworkPropertyMetadata(0.0,
- FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
- null,
- CoerceValue
- ),
- ValidateValue
- );
- // CLR 包装器
- public double Value
- {
- get => (double)GetValue(ValueProperty);
- set => SetValue(ValueProperty, value);
- }
- // 强制值回调(限制范围)
- private static object CoerceValue(DependencyObject d, object value)
- {
- return Math.Max(0, (double)value); // 确保值 ≥ 0
- }
- // 验证回调(拒绝非法值)
- private static bool ValidateValue(object value)
- {
- return value is double && (double)value <= 100; // 最大值 100
- }
- }
- <!-- Window.Resources中自定义样式 -->
- <Style TargetType="{x:Type local:NumericBox}">
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type local:NumericBox}">
- <!-- 模板内容(如文本框、增减按钮等) -->
- <TextBlock Text="{Binding Path=Value, RelativeSource={RelativeSource AncestorType={x:Type local:NumericBox}}}"/>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- </Style>
-
- <!-- 使用的布局地方local 使用 -->
- <local:NumericBox Width="120" Margin="5,0" Value="{Binding dataItem.Id} />
复制代码 附加属性
本质与定义
- 附加属性(Attached Properties)是一种特殊类型的依赖属性,答应将属性“附加”到其他非宿主类的对象上。比方,Grid.Row 属性由 Grid 类定义,但可附加到任何子控件上,用于指定其在网格中的行位置。
- 实现方式:通过 DependencyProperty.RegisterAttached 静态方法注册,并定义 Get 和 Set 静态方法作为访问器。
- 核心特点:
跨控件扩展:无需修改目标控件源码即可添加新属性(如为 TextBox 添加水印提示)。
全局性:可在任何继承自 DependencyObject 的对象上利用。
与依赖属性的区别
宿主差别:依赖属性定义在宿主类内部(如自定义控件的属性),而附加属性定义在外部类并附加到其他控件。
注册方法:依赖属性利用 Register,附加属性利用 RegisterAttached。
访问方式:附加属性通过静态 Get/Set 方法访问,依赖属性通过 CLR 包装器直接访问
附加属性的范例应用场景
- 布局控制
网格布局:Grid.Row 和 Grid.Column 属性由 Grid 类定义,附加到子元素以指定位置。
画布定位:Canvas.Left 和 Canvas.Top 控制子元素在画布中的坐标。
- 举动扩展
验证逻辑:为 TextBox 添加输入验证或水印提示。
动态样式:通过附加属性触发控件样式的动态厘革(如高亮选中项)。
- 服务类场景
跨组件通信:定义服务类,通过附加属性通报全局状态(如用户权限或主题颜色)
自定义附加属性
也会联合自定义控件文章来讲解
这里就先给个 示例
重要步骤就是 定义控件 注册依赖属性DependencyProperty 包装clr 定义样式 和 布局利用
- public static class GridHelper
- {
- public static readonly DependencyProperty ShowBorderProperty =
- DependencyProperty.RegisterAttached("ShowBorder", typeof(bool), typeof(GridHelper),
- new PropertyMetadata(false, OnShowBorderChanged));
- public static bool GetShowBorder(DependencyObject obj) => (bool)obj.GetValue(ShowBorderProperty);
- public static void SetShowBorder(DependencyObject obj, bool value) => obj.SetValue(ShowBorderProperty, value);
- private static void OnShowBorderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is Grid grid && (bool)e.NewValue)
- {
- // 动态为每个子元素添加边框
- foreach (var child in grid.Children.OfType<FrameworkElement>())
- {
- var border = new Border { BorderBrush = Brushes.Gray, BorderThickness = new Thickness(1) };
- Grid.SetRow(border, Grid.GetRow(child));
- Grid.SetColumn(border, Grid.GetColumn(child));
- grid.Children.Add(border);
- }
- }
- }
- }
- <Grid local:GridHelper.ShowBorder="True">
- <Grid.RowDefinitions>...</Grid.RowDefinitions>
- <Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>
- <Button Grid.Row="0" Grid.Column="0" Content="按钮" />
- </Grid>
复制代码 注意事项
- 定名规范
附加属性名称以 Property 结尾(如 ShowBorderProperty)。
Get/Set 方法需与属性名严格对应(如 GetShowBorder)。
- 性能优化
避免在回调函数中频繁操作 UI 元素,优先利用数据绑定。
合理设置默认值以减少初始化开销。
- 调试技巧
定名空间检查:确保 XAML 中精确引入附加属性所在的定名空间。
模板绑定:在自定义控件模板中利用 TemplateBinding 关联附加属性
属性对比
小结
- WPF依赖属性的访问机制通过全局存储、优先级盘算和动态回调实现了高效灵活的特性。其核心优势在于支持数据绑定、动画、样式继承等复杂场景,同时通过优化存储和盘算逻辑包管了性能
- 附加属性是 WPF 实现灵活 UI 扩展的核心机制,其核心价值在于解耦功能与控件,支持跨组件通信、动态布局等复杂场景。通过合理设计,开辟者可以明显提升代码复用性和可维护性
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |