首页 > 其他分享 >.NET|--WPF|--笔记合集|--依赖项属性|--3.属性包装器

.NET|--WPF|--笔记合集|--依赖项属性|--3.属性包装器

时间:2024-08-31 10:47:09浏览次数:11  
标签:TextBlock -- System Controls Windows NET TextWrapping 属性

前言


属性包装器的主要作用是将依赖属性的访问方式转换为标准的 CLR 属性访问方式,
从而使代码更加简洁、直观,并提供一致性和更好的开发体验。
通过属性包装器,开发者可以利用依赖属性的高级功能,同时保持代码的可读性和易用性。

"属性包装器"在TextBlock源码中使用

public class TextBlock : FrameworkElement // 其它继承的基类省略.
{
	// 第1步:定义依赖项属性
	public static readonly DependencyProperty TextWrappingProperty;
	
	// 第2步:注册依赖项属性( 本篇笔记要讲的 )
	static TextBlock()
	{
		TextWrappingProperty = 
		DependencyProperty.Register
		(
			"TextWrapping", 
			typeof(TextWrapping), //一个枚举类"System.Windows.TextWrapping"
			typeof(TextBlock), 
			new FrameworkPropertyMetadata(TextWrapping.NoWrap, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender), 
			IsValidTextWrap
		);
	}

	// 注册依赖项属性的参数"ValidateValueCallback",用于验证属性值的回调函数.
	private static bool IsValidTextWrap(object o)
	{
		TextWrapping textWrapping = (TextWrapping)o;
		if (textWrapping != TextWrapping.Wrap && textWrapping != TextWrapping.NoWrap)
		{
			return textWrapping == TextWrapping.WrapWithOverflow;
		}
		return true;
	}

	// 第3步:属性包装器( 本笔记重点 )
	public TextWrapping TextWrapping
	{
		get
		{
			return (TextWrapping)GetValue(TextWrappingProperty);
		}
		set
		{
			SetValue(TextWrappingProperty, value);
		}
	}
}

GetValue 和 SetValue


属性包装器, 就几行代码, 看着很简单, 重点还是在2个方法上:GetValue和SetValue.

GetValue 和 SetValue 方法是 DependencyObject 类的方法,用于获取和设置依赖属性的值。

为什么"TextBlock"中可以直接使用"DependencyObject"的方法, 就可以看下"TextBlock"的继承关系了,如图所示.

3f2c89f41031df2cb7a53bf087e51e2d.png

// DependencyObject 的GetValue和SetValue的源码
public class DependencyObject : DispatcherObject
{
    public void SetValue(DependencyProperty dp, object value)
    {
        VerifyAccess();
        PropertyMetadata metadata = SetupPropertyChange(dp);
        SetValueCommon(dp, value, metadata, coerceWithDeferredReference: false, coerceWithCurrentValue: false, OperationType.Unknown, isInternal: false);
    }

    public object GetValue(DependencyProperty dp)
    {
        VerifyAccess();
        if (dp == null)
        {
            throw new ArgumentNullException("dp");
        }
        return GetValueEntry(LookupEntry(dp.GlobalIndex), dp, null, RequestFlags.FullyResolved).Value;
    }
}

给依赖项赋值( 使用LINQPad运行下面的代码)

/*
注意事项 : 
不能直接启动(F5), 
多次直接启动(F5)会报错 : "不能在同一 AppDomain 中创建多个 System.Windows.Application 实例。"

当报错的时候,有2种解决方案 : 

1.使用"菜单栏--Query--Kill Process and Execute"

2.更改设置 : 
Edit -- Preference -- Advanced -- Execution
Always use Fresh Process per Execution


这2种方法, "kill Process and Execute"和"Always use Fresh Process per Execution",
其实意思是一样~

*/

/*

*/


#region 测试方法

internal void Test()
{
	//System.Windows.Controls.Image image = new System.Windows.Controls.Image();
	//System.Windows.DependencyObject dependencyObject;
	//System.Windows.DependencyProperty dependencyProperty;
	//System.Windows.DependencyProperty dp = new System.Windows.DependencyProperty();
}

#endregion


// 程序入口
void Main()
{

	System.Windows.Application app = new System.Windows.Application();
	var mainWindow = new MainWindows
	{
		Title = "Main Window",
		Width = 400,
		Height = 250,
		WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen
	};
	app.Run(mainWindow);
}

// 主窗口
public class MainWindows : System.Windows.Window
{
	public MainWindows()
	{
		Init();
	}

	public void Init()
	{
		// 创建一个Grid
		System.Windows.Controls.Grid grid = new System.Windows.Controls.Grid();

		// 创建一个TextBlock
		System.Windows.Controls.TextBlock textBlock = new System.Windows.Controls.TextBlock();

		// *****赋值**********************************************************************************
		textBlock.Text = "使用属性包装器,为依赖项属性Text赋值.\r\n --zh89233";

		// 将TextBlock添加到Grid
		grid.Children.Add(textBlock);
		// 将Grid设置为Window的内容
		this.Content = grid;
	}
}


5b9f6e24b9cf6519da5c972551a1153d.png


其实绕了一圈, 依赖项属性都写了三四篇笔记了, 
最后只是给属性赋值操作, 感觉是不是有点虎头蛇尾, 哈哈...
其实重点都在注册那里, 所以最后用起来就感觉很方便了.

这种设计使得开发者在享受强大功能的同时,不需要被复杂的实现细节所困扰。
这也是为什么你会觉得使用依赖属性很简单的原因。
它体现了良好的设计理念,即在提供强大功能的同时,尽量简化开发者的使用体验。

并且现在只是使用, 依赖项属性, 主要还是在以后数据绑定, 动画等其他功能上体现出来强大, 
那时候可以再深入研究.

下面一个例子演示下依赖项属性在数据绑定上的简单应用↓

依赖项属性在数据绑定上的简单应用(该代码在LINQPad上运行)

// 程序入口
void Main()
{

	System.Windows.Application app = new System.Windows.Application();
	var mainWindow = new MainWindows
	{
		Title = "Main Window",
		Width = 400,
		Height = 250,
		WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen
	};
	app.Run(mainWindow);
}

// 主窗口
public class MainWindows : System.Windows.Window
{
	public MainWindows()
	{
		Init();
	}

	public void Init()
	{
		// 创建一个Grid
		System.Windows.Controls.Grid grid = new System.Windows.Controls.Grid();

		// 创建一个TextBlock
		System.Windows.Controls.TextBlock textBlock = new System.Windows.Controls.TextBlock();

		// 创建一个TextBox
		System.Windows.Controls.TextBox textBox = new System.Windows.Controls.TextBox();

		// 创建一个Button( 此按钮只是为了演示, 更改了TextBlock的Text值, TextBox的Text值也会修改 )
		System.Windows.Controls.Button button = new System.Windows.Controls.Button { Content = "更改TextBlock.Text值" };

		// 设置TextBox的初始文本
		textBox.Text = "使用属性包装器,为依赖项属性Text赋值.\r\n --zh89233";

		#region *****************核心代码, 需要注意*****************

		// 创建Binding对象并绑定TextBlock的Text属性到TextBox的Text属性
		System.Windows.Data.Binding binding = new System.Windows.Data.Binding("Text")
		{
			Source = textBox,
			Mode = System.Windows.Data.BindingMode.TwoWay
		};
		textBlock.SetBinding(System.Windows.Controls.TextBlock.TextProperty, binding);

		// 为Button的Click事件添加处理程序
		button.Click += (sender, e) =>
		{
			// 更新TextBlock的Text属性
			textBlock.Text = "点击,更改textBlock.Text值为:" + DateTime.Now.ToString();
		};

		#endregion

		// 设置Grid行定义
		grid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition());
		grid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition());
		grid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition());

		// 将TextBlock、TextBox和Button添加到Grid
		grid.Children.Add(textBlock);
		System.Windows.Controls.Grid.SetRow(textBlock, 0);

		grid.Children.Add(textBox);
		System.Windows.Controls.Grid.SetRow(textBox, 1);

		grid.Children.Add(button);
		System.Windows.Controls.Grid.SetRow(button, 2);

		// 将Grid设置为Window的内容
		this.Content = grid;
	}
}

5a6679b6b7807a116294bea7cce4fc36.gif

DependencyObject.ClearValue()


属性包装器中, 看到了DependencyObject的GetValue()和SetValue()方法,
其实还有一个比较常用的方法::ClearValue()

插曲--Visual Studio查看定义是经过简化的,以"TextBlock"的"TextWrapping"为例

在Visual Studio中查看定义, 会发现, 只有属性包装器, 就可能会误认为和CLR属性是一样的,
其实并不是, 这是Visual Studio中简化的...
// Visual Studio中查看定义(快捷键F12)
public class TextBlock : FrameworkElement, IServiceProvider, IContentHost, IAddChild, IAddChildInternal
{
    public TextWrapping TextWrapping { get; set; }
}

但是反编译的话,就可以看到完整的代码

// ILSpy中反编译出来的代码↓
public class TextBlock : FrameworkElement, IServiceProvider, IContentHost, IAddChild, IAddChildInternal
{
    public TextWrapping TextWrapping
    {
        get
        {
            return (TextWrapping)GetValue(TextWrappingProperty);
        }
        set
        {
            SetValue(TextWrappingProperty, value);
        }
    }
}
简化的表示只是告诉你这个属性存在,但它实际上是通过依赖属性机制实现的。
Visual Studio 显示简化的属性定义只是为了让代码更易读。
实际的实现仍然是依赖属性,利用 GetValue 和 SetValue 方法进行操作。	

标签:TextBlock,--,System,Controls,Windows,NET,TextWrapping,属性
From: https://www.cnblogs.com/love-zf/p/18389972

相关文章

  • [DPDK] Docker容器内安装DPDK
    Docker安装DPDK参考:容器安装DPDK。确认版本兼容性我使用的是Mellanox-5网卡。为了确认支持的DPDK版本,请先确认自己的OFED和frimware版本。查看firmware版本:$ibstatCA'mlx5_0'CAtype:MT4119Firmwareversion:16.35.3006查看OFED版本:$ofed_info-......
  • linux磁盘已满,查看哪个文件占用多
    使用df-h查看磁盘空间占用情况FilesystemSizeUsedAvailUse%Mountedonudev3.9G03.9G0%/devtmpfs799M3.1M796M1%/run/dev/vda199G99G0G100%/tmpfs3.9G03.9G0%/dev/shmtmpfs......
  • 一个免费好用的魔法
    这款虽然是免费的但一天只能免费一个小时,但你可以多造几个号就能一直免费用的时候要把火绒,360等类似的软件关了,不然有时候会得不了。因为是免费的所以有人数限制,一般在9.30之后人都特别多,只有到晚上或者中午和早上时候人少,手机端和PC端是分开的,手机端人是特别少的。官网连接:htt......
  • 网络安全公开数据集Maple-IDS,恶意流量检测数据集开放使用!
    MapleDataset枫叶数据集MapleDataset枫叶数据集由东北林业大学网络安全实验室(https://maple.nefu.edu.cn/lab/)公开发布,是用于入侵检测评估的数据集,其目的在于提升异常基础入侵检测系统(IDS)以及入侵预防系统(IPS)的性能与可靠性。在网络攻击愈发复杂的当下,拥有一个可靠且紧跟时代的......
  • vue3 导出为Excel文件
    服务端给的一个下载接口:/order/exportOrderOpenInvoice导出转化为ExcelconstexportOrder=async()=>{ letreqData={};  letexportOrderOpenInvoiceUrl="/order/exportOrderOpenInvoice";   try{   constresponse=awaitaxios.get(exportOrderOpenInvo......
  • 【网络知识系列】域名抢注
    原创祺印说信安以下文章来源于河南等级保护测评,作者铸盾安全一、概述域名仿冒尝试利用用户在地址栏中直接输入URL时引入的印刷错误(即“打字错误”)。通过利用用户错误,网络威胁行为者将毫无戒心的用户引导至与原始域名极为相似的非法域名。这种策略涉及购买和注册与现有域名......
  • vivo手机导出微信聊天记录方法
    1,先在手机上打开“开发人员选项”并将USB调试功能打开。再将手机通过数据线连接到电脑,打开电脑上已经安装好的“互传备份助手”(点击此处进入下载页面),再点击界面上的新建备份。注意:在电脑上运行互传备份助手时,会提醒手机上也要安装互传助手,按提示安装即可。 2,在互传备份助手窗......
  • 物质、质量、能量
    物质和质量宏观物质是由粒子构成,粒子没有质量这一概念,粒子有数量,粒子之间具有相互作用。所谓的质量是人为创造的一种概念术语,是粒子之间相互作用的一种宏观表现。基本粒子是否由单一物质构成,目前不清楚。我们平时所谓的对物体称重,实际上是在测量外物对被测物体的作用力。能......
  • 自我介绍+软工5问
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13228这个作业的目标<学会使用博客和github,学会使用git的简单使用和markdowm语法,为之后的学习打下基础>......
  • Day01.md-Markdown语法讲解
    标题三级标题四级标题字体Hello,Word!Hello,Word!Hello,Word!Hello,Word!引用选择狂神说java,走向人生巅峰分割线图片超链接点击跳转到狂神博客列表ABCABC表格名字性别生日张三男1997.1.1代码......