.NET|--WPF|--笔记合集|--依赖项属性|--3.属性包装器
前言
属性包装器的主要作用是将依赖属性的访问方式转换为标准的 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"的继承关系了,如图所示.
// 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;
}
}
其实绕了一圈, 依赖项属性都写了三四篇笔记了, 最后只是给属性赋值操作, 感觉是不是有点虎头蛇尾, 哈哈... 其实重点都在注册那里, 所以最后用起来就感觉很方便了. 这种设计使得开发者在享受强大功能的同时,不需要被复杂的实现细节所困扰。 这也是为什么你会觉得使用依赖属性很简单的原因。 它体现了良好的设计理念,即在提供强大功能的同时,尽量简化开发者的使用体验。 并且现在只是使用, 依赖项属性, 主要还是在以后数据绑定, 动画等其他功能上体现出来强大, 那时候可以再深入研究. 下面一个例子演示下依赖项属性在数据绑定上的简单应用↓
依赖项属性在数据绑定上的简单应用(该代码在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;
}
}
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 方法进行操作。
__EOF__

本文作者:国产凌凌漆
本文链接:https://www.cnblogs.com/love-zf/p/18389972.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/love-zf/p/18389972.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库