Always keep a|

石起起

园龄:1年10个月粉丝:1关注:0

2025-02-10 10:26阅读: 4评论: 0推荐: 0

第4章-依赖属性

metadata:元数据
affects:影响
arrange: 安排、排列
measure: 度量、测量
render: 渲染、呈现
inherits: 继承、传播
prohibited: 禁止
journal: 日志
coerce: 强制
attached: 附加

❓ 模板选择器
❓ 针对于mvvm.toolkit的反编译学习工具,也可以用来学习WPF底层代码

依赖属性的作用及优点

  • 使用效率更高的保存机制
  • 支持附加功能(附加属性)
  • 支持更改通知
  • 支持属性继承(在元素树中向下传播默认属性值的能力)
  • 支持动画、数据绑定以及样式
  • 依赖属性的使用方式与普通属性是一样的

定义依赖项属性

📢 只能为依赖对象(继承自 DependencyObject 的类)添加依赖项属性。大部分结构都继承自 DependencyObject

👉 第一步:定义表示属性的对象

public class FrameworkElement: UIElement, ...
{
	// ⭐
	public static readonly DependencyProperty MarginProperty;
}

🔔 说明:

  • MarginProperty:依赖属性的名称以 Property 结尾;
  • 使用 static 修饰:表示属性始终保持可用与共享;
  • 使用 readonly 修饰:表示属性只能在类的静态构造函数中对其进行设置;

👉 第二步:注册依赖项属性

❓❓ 必须在与其关联的类的静态构造函数中进行

public class Class1 : DependencyObject
{
	public static readonly DependencyProperty MyMarginProperty;

	static Class1()
	{
		FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure);

		MyMarginProperty = DependencyProperty.Register("MyMargin",
			typeof(Thickness), typeof(Class1), metadata,
			new ValidateValueCallback(Class1.IsMarginValid));
	}

	private static bool IsMarginValid(object value)
	{
		throw new NotImplementedException();
	}
}

💡 个人理解:这里写在静态构造里的形式,可省略静态构造,直接写在类的最外层。
参考文章:WPF依赖属性详解 - 痕迹g - 博客园 (cnblogs.com)

FrameworkPropertyMetadata 对象指示希望依赖属性使用什么服务(数据绑定、动画及日志)

DependencyProperty.Register 方法的参数说明:

  • 属性名
  • 属性使用的数据类型
  • 拥有该属性的类
  • 一个具有附加属性设置的 FrameworkPropertyMetadata 对象 (可选)
  • 一个用于验证属性的回调函数 (可选)

FrameworkPropertyMetadata 类的属性

📢 这些属性的默念默认值都是 false

  • AffectsArrange、AffectsMeasure、AffectsParentArrange 和 AffectsParentMeasure: 如果为 true,依赖项属性会影响在布局操作的测量过程和排列过程如何放置相邻的元素或父元素。
  • AffectsRender: 如果为 true,依赖项属性会对元素的绘制方式造成一定的影响,要求重新绘制元素。
  • BindsTwoWayByDefault: 如果为 true,默认情况下,依赖项属性将使用双向数据绑定而不是单向数据绑定。不过,当创建数据绑定时,可以明确指定所需的绑定行为。
  • Inherits: 如果为 true,就通过元素树传播该依赖项属性值,并且可以被嵌套的元素继承。如 Font 属性。
  • IsAnimationProhibited: 如果为 true,就不能将依赖项属性用于动画。
  • IsNotDataBindable: 如果为 ture,就不能使用绑定表达式设置依赖属性项。
  • Journal: 如果为 true,在基于页面的应用程序中,依赖项属性将被保存到日志中。
  • SubPropertiesDoNotAffectRender: 如果为 true,并且对象的某个子属性(属性的属性)发生了变化,WPF 将不会重新渲染该对象。
  • DefaultUpdateSourceTrigger: 当该属性用于绑定表达式时,该属性用于为Binding.UpdateSouceTrigger 属性设置默认值。
  • DefaultValue: 该属性用于为依赖项属性设置默认值。
  • CoerceValueCallback: 该属性提供了一个回调函数,用于在验证依赖项属性之前尝试“纠正”属性值。
  • PropertyChangedCallback: 该属性值提供了一个回调函数,当依赖项属性发生变化时调用该回调函数。

❓ FrameworkPropertyMetadata 和 PropertyMetadata 的异同
WPF 属性元数据PropertyMetadata,UIPropertyMetadata,FrameworkPropertyMetadata作用-CSDN博客
✔️ FrameworkPropertyMetadata 继承自 UIPropertyMetadata, UIPropertyMetadata 继承自 PropertyMetadata

👉 第三步:添加属性包装器

这就是依赖属性使用的时候与普通属性一样的原因!

public Thickness Margin
{
	get {return (Thickness)GetValue(MarginProperty);}
	set {SetValue(MarginProperty, value);}
}

📢 GetValue() 和 SetValue() 方法是 DependencyObject 基类中定义的。

💡 在属性封装器不是验证数据或事件的正确位置。因此应当只包含对 SetValue() 和 GetValue() 方法的调用。

设置依赖项属性值的顺序:简单描述,即依赖项属性的值可能来自数据绑定、样式或动画,也可能来自直接设置的值。如果想要让该属性像从来没有设置过值那样的话,需要使用 myElement.ClearValue(FrameworkElement.MarginProperty)

个人总结:

采用wpf自动生成的代码就行

public int MyProperty
{
	get { return (int)GetValue(MyPropertyProperty); }
	set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
	DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

WPF 使用依赖项属性的方式

两个关键 :

  • 更改通知
  • 动态识别

更改通知:
当属性值发生变化时,会触发 OnPropertyChangedCallback() 的方法。
💡 我的理解是:需要调用 PropertyChangedCallback 回调函数。

依赖项属性动态识别顺序:

  • 默认值。
  • 继承而来的值。
  • 来自主题样式的值。
  • 来自项目样式的值。
  • 本地值。

动态识别后最终确认的值:

  • 确定基本值。
  • 如果属性是使用表达设置的,就对表达式进行求值(数据绑定和资源)。
  • 如果属性是动画目标,就应用动画。
  • 运行 CoerceValueCallback 回调函数来修正属性值。

共享的依赖项属性

尽管一些类具有不同的继承层次,但它们会共享同一依赖项属性。原因是这个可共享的依赖项属性在静态构造中初始化时调用了

DependencyProperty.AddOwer();

❓ 附加的依赖属性

📢 定义附加属性需要使用 RegisterAttached() 方法,而不是使用 Register() 方法。
📢 当创建附加属性时,不必定义 .NET 属性封装器
📢 对于附加属性值的获取与设置需要调用两个静态方法:SetPropertyName() 和 GetPropertyName(). 方法中也使用到了 SetValue() 和 GetValue(). 👈 其实可以直接使用这两个函数来设置附加属性值。

public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
  "IsBubbleSource",
  typeof(Boolean),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetIsBubbleSource(UIElement element, Boolean value)
{
  element.SetValue(IsBubbleSourceProperty, value);
}
public static Boolean GetIsBubbleSource(UIElement element)
{
  return (Boolean)element.GetValue(IsBubbleSourceProperty);
}

💡 通过 element.SetValue(IsBubbleSourceProperty, value); 可以看出,附加属性其实就是依赖属性的扩展,它本身就是一种依赖属性,是附加到依赖属性上的一种属性。

属性验证

WPF 为依赖项属性提供了两种方法来阻止非法值:

  • ValidateValueCallback: 该回调函数可接受或拒绝新值。
  • CoerceValueCallback: 该回调函数可将新值修改为更能被接受的值。

在 DependencyProperty.Register() 中创建可选的 ValidateValueCallback 验证回调函数:

MyMarginProperty = DependencyProperty.Register("MyMargin",
	typeof(Thickness), typeof(Class1), metadata,
	new ValidateValueCallback(Class1.IsMarginValid));

...

private static bool IsMarginValid(object value)
{
	Thickness thickness = (Thickness)value;
	return thickness.IsValid(true, false, true, false);
}

验证函数返回true或false,只能校验当前传递来的值。

通过 FrameworkPropertyMetada 对象使用 CorcreValueCallback 回调函数:

📢 返回值就是校验后强制返回并被应用的值。

方式一:

FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(new Thickness(),FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(Test1), new CoerceValueCallback(Test2));

...

private static void Test1(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ }

private static object Test2(DependencyObject d, object baseValue)
{ return baseValue; }

方式二:

FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
metadata.CoerceValueCallback = new CoerceValueCallback(Test2);

...

private static object Test2(DependencyObject d, object baseValue)
{ return baseValue; }

三个回调:

  • ValidateValueCallback:对值进行验证
  • CoerceValueCallback:强制对属性与值进行校验,可修改值,一般发生在上一个回调之前
  • OnPropertyChangedCallback: 当属性值发生变化时,一般在前两个回调完成后触发

💡 属性快捷键(代码片段)

  • prop
  • propdp
  • propa 附加属性
  • propr 自定义
  • propfull

🔥 总结

  • 学会如何创建并使用依赖属性
  • 学会如何创建并使用附加属性
  • 学会如何使用三个回调函数:强制回调、验证回调、更改通知回调
  • 使用这些知识点的时候如果不清楚结构可以F12查看定义

✅ 实际使用一下附加属性,写个例子
参考文章:WPF整理-为控件添加自定义附加属性 - DebugLZQ - 博客园 (cnblogs.com)

  • 写一个类专门用来放该附加属性
  • 在xmal中引用项目集,可使用local
  • 在控件中引用该附加属性
  • 在附加属性上添加值变化回调函数,对依赖对象(控件)进行操作

💡 我的新理解:附加属性就是对依赖属性的扩展,因为附加属性本身就是一种依赖属性。

❓❓ wpf 编程宝典有的地方感觉讲得也不是很清楚,之后可能需要结合 wpf专业编程指南 或者 WPF 等其他书籍补充学习 这样会拉升学习周期 是不是可以按照概念一起阅读 以一个为主 其它的书为参考资料呢

Stack栈集合(后进先出)

Stack<>类 (System.Collections.Generic) | Microsoft Learn
表示相同指定类型的实例可变大小的后进先出 (LIFO) 集合。

  • Push: 从顶部插入元素
  • Pop: 从顶部删除元素
  • Peek: 返回一个元素,该元素位于顶部,但不将其从集合中删除

本文作者:石起起

本文链接:https://www.cnblogs.com/myshiqiqi/p/18707296

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   石起起  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起