WPF 依赖属性前言

​ 在.net中,我们可以属性来获取或设置字段的值,不需要在编写额外的get和set方法,但这有一个前提,那就是需要在对象中拥有一个字段,才能在此字段的基础上获取或设置字段的值,比如:

// 字段
private int _age = 23; // 属性
public int Age
{
get { return _age; }
set { _age = value; }
}

​ 但在wpf中,微软引入了一种依赖属性的方法来获取或者设置控件的值,之所以叫做依赖,是因为这个值能够从其他数据源中获取值(依赖在别人身上)而在控件对象上不保存这个值,所以依赖属性能有效地较少内存开销。

依赖属性先简单说到这里,有时我们往往只是记住了结论而忽略了推导过程,下面的我对推到过程的一些思考:

**如果让我们自己来实现依赖属性,我们该怎么实现呢? **

首先能确定的是,我们不能定义控件拥有的所有字段,如果定义了,那么控件对象被创建时,所有字段都会被初始化,都会有一个初始值,内存就会被浪费,这样不行,所以我就想到了用字典Dictionary<int,object>的方式来存储需要的值。用int来存储一个索引值,保证控件中的每个属性索引值都不一样;用object来存储属性的值。

由于每种控件都需要此字典来存储值,所以控件要有一个基类,这里我取名MyDependencyObject,它的结构大致应该是这样:

	/// <summary>
/// 依赖对象
/// </summary>
public class MyDependencyObject
{
private Dictionary<int, object> _keyValue = new Dictionary<int, object>(); public object GetValue(int index)
{
return _keyValue[index];
} public void SetValue(int index, object value)
{
_keyValue[index] = value;
}
}

然后我们应该确定的是,控件的每个属性都应该有个索引值啊,这样才能在_keyValue字典中找到对应的值。然后又想到这个索引值应该是和对象无关的,是和类相关的,所以应该是一种静态的方式来得到这个索引值,所以实现的方式大概是这样:

	/// <summary>
/// 依赖对象
/// </summary>
public class MyDependencyObject
{
private Dictionary<int, object> _keyValue = new Dictionary<int, object>(); public object GetValue(int index)
{
return _keyValue[index];
} public void SetValue(int index, object value)
{
_keyValue[index] = value;
} // 某种算法根据属性名获取一个索引值
protected static int GetIndex(string propertyName)
{
// ...
} } /// <summary>
/// 按钮控件
/// </summary>
public class MyButton : MyDependencyObject
{ /// <summary>
/// 按钮内容属性索引
/// </summary>
public int ContentProperty = MyDependencyObject.GetIndex("Content"); /// <summary>
/// 按钮内容属性
/// </summary>
public string Content
{
get
{
return (string)base.GetValue(ContentProperty);
}
set
{
base.SetValue(ContentProperty, value);
}
}
}

在基类MyDependencyObject中定义一个静态方法GetIndex,子类根据属性的名字来获取唯一的索引,然后在根据索引值来获取和设置值。

虽然上面的方式已经可以达到节省内存的目的,但是对于属性更多的控制,比如属性的默认值、值变化回调、是否是只读的等等这些都达不到要求,所以属性应该被定义成更复杂的类型以完成更多的功能,而不是是用一个int的索引就完成了,所以我把这个更复杂的类型定义为MyDependencyProperty,它的结构如下:

	/// <summary>
/// 依赖属性
/// </summary>
public class MyDependencyProperty
{
/// <summary>
/// 索引和属性数据字典
/// </summary>
public static Dictionary<int, MyDependencyProperty> _indexProperty = new Dictionary<int, MyDependencyProperty>(); /// <summary>
/// 注册依赖属性
/// </summary>
/// <param name="propertyName">属性名</param>
/// <param name="ownType">拥有者类型</param>
/// <param name="propertyData">属性数据</param>
/// <returns></returns>
public static MyDependencyProperty Register(string propertyName, Type ownType, MyPropertyMetadata propertyData)
{
MyDependencyProperty property = new MyDependencyProperty(propertyName, ownType, propertyData);
int index = GetIndex(propertyName, ownType);
_indexProperty[index] = property;
return property;
} /// <summary>
/// 构造函数
/// </summary>
public MyDependencyProperty(string propertyName, Type ownType, MyPropertyMetadata data)
{
Name = propertyName;
OwnType = ownType;
DefaultMetaData = data;
} /// <summary>
///属性名
/// </summary>
public string Name { get; private set; } /// <summary>
/// 拥有者类型
/// </summary>
public Type OwnType { get; private set; } /// <summary>
/// 属性数据
/// </summary>
public MyPropertyMetadata DefaultMetaData { get; private set; } /// <summary>
/// 使用某种算法按照属性名获取一个索引值
/// </summary>
/// <param name="propertyName"></param>
/// <param name="ownType"></param>
/// <returns></returns>
private static int GetIndex(string propertyName, Type ownType)
{
// ...
return 0;
}
} /// <summary>
/// 属性数据
/// </summary>
public class MyPropertyMetadata
{
public MyPropertyMetadata(object defaultValue)
{
DefaultValue = defaultValue;
} /// <summary>
/// 默认值
/// </summary>
public object DefaultValue { get; set; } // 其他的值 ...
}

MyDependencyProperty中包含了静态字段Dictionary<int, MyDependencyProperty> _indexProperty,它保存了索引-依赖属性的集合,整个应用程序只包含一个集合。通过静态方法GetIndex(string propertyName, Type ownType)计算索引值,计算索引值需要属性名和拥有者类型,这样可以计算出唯一的索引。

像下面这样使用上面的依赖属性:

	/// <summary>
/// 依赖对象
/// </summary>
public class MyDependencyObject
{
private Dictionary<int, object> _keyValue = new Dictionary<int, object>(); public object GetValue(MyDependencyProperty dp)
{
int index = MyDependencyProperty.GetIndex(dp.Name, dp.OwnType);
// 如果字典的Key中不包含索引 那么返回属性的默认值
if(!_keyValue.ContainsKey(index))
{
return dp.DefaultMetaData.DefaultValue;
}
else
{
return _keyValue[index];
}
} public void SetValue(MyDependencyProperty dp, object value)
{
int index = MyDependencyProperty.GetIndex(dp.Name, dp.OwnType);
_keyValue[index] = value;
} } /// <summary>
/// 按钮控件
/// </summary>
public class MyButton : MyDependencyObject
{ /// <summary>
/// 按钮内容依赖属性
/// </summary>
public MyDependencyProperty ContentProperty
= MyDependencyProperty.Register("Content", typeof(MyButton), new MyPropertyMetadata("Hello world")); /// <summary>
/// 按钮内容属性
/// </summary>
public string Content
{
get
{
return (string)base.GetValue(ContentProperty);
}
set
{
base.SetValue(ContentProperty, value);
}
}
}

简单的实现方式想到的也就这么多了,感觉算是有了依赖属性的雏形啦,WPF中的依赖属性下一篇在说吧。

WPF 依赖属性前言的更多相关文章

  1. WPF依赖属性的正确学习方法

    前言 我在学习WPF的早期,对依赖属性理解一直都非常的不到位,其恶果就是,我每次在写依赖属性的时候,需要翻过去的代码来复制黏贴. 相信很多朋友有着和我相同的经历,所以这篇文章希望能帮助到那些刚刚开始学 ...

  2. WPF依赖属性详解

    WPF依赖属性详解 WPF 依赖属性 英文译为 Dependency Properties,是WPF引入的一种新类型的属性,在WPF中有着极为广泛的应用,在WPF中对于WPF Dependency P ...

  3. WPF自学入门(五)WPF依赖属性

    在.NET中有事件也有属性,WPF中加入了路由事件,也加入了依赖属性.最近在写项目时还不知道WPF依赖属性是干什么用的,在使用依赖项属性的时候我都以为是在用.NET中的属性,但是确实上不是的,通过阅读 ...

  4. WPF依赖属性值源(BaseValueSource)

    原文:WPF依赖属性值源(BaseValueSource)   WPF依赖属性提供一个机制,可以获取依赖属性提供值的来源 其以BaseValueSource枚举表示 1.Default public ...

  5. WPF依赖属性(续)(3)依赖属性存储

    原文:WPF依赖属性(续)(3)依赖属性存储          在之前的两篇,很多朋友参与了讨论,也说明各位对WPF/SL计数的热情,对DP系统各抒已见,当然也出现了一些分歧. 以下简称DP为依赖属性 ...

  6. WPF依赖属性(续)(1)

    原文:WPF依赖属性(续)(1)                 之前有写过几篇文章,详细地介绍了依赖属性的基本使用方法,如果你不想了解其内部实现机制的话,那么通过那两篇文章的介绍,足以应付平时的应用 ...

  7. WPF依赖属性(续)(2)依赖属性与附加属性的区别

    原文:WPF依赖属性(续)(2)依赖属性与附加属性的区别        接上篇,感谢各位的评论,都是认为依赖属性的设计并不是为了节省内存,从大的方面而讲是如此.样式,数据绑定,动画样样都离不开它.这篇 ...

  8. 监听WPF依赖属性

    原文:监听WPF依赖属性 当我们使用依赖属性的时候,有时需要监听它的变化,这在写自定义控件的时候十分有用, 下面介绍一种简单的方法.   如下使用DependencyPropertyDescripto ...

  9. WPF 依赖属性

    依赖属性,简单的说,在WPF控件应用过程中,界面上直接可以引用的属性 如:<Button Content="aaa"></Button> Content称为 ...

随机推荐

  1. MQTT研究之EMQ:【EMQX使用中的一些问题记录(1)】

    issue 1. EMQX的共享订阅 EMQX是一个非常强大的物联网通信消息总线,基于EMQX开展应用开发,要注意很多配置细节问题,这里要说到的就是共享订阅以及和cleanSession之间的关系问题 ...

  2. flutter upgrade之后出现Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from

    错误信息 Initializing gradle... Resolving dependencies... Running Gradle task 'assembleDebug'... /Users/ ...

  3. RabbitMQ的应用总结

    RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有很多公开标准 ...

  4. JavaScript 图片与Base64数据互相转换脚本

    JavaScript 图片与Base64数据互相转换脚本 注: 转换过程中注意跨域问题.测试页是否支持相关标签创建.dom结构. 方法一:非Html 5使用FileReader 使用XMLHttpRe ...

  5. matlab学习笔记12_3串联结构体,按属性创建含有元胞数组的结构体,filenames,isfield,isstruct,orderfields

    一起来学matlab-matlab学习笔记12 12_3 结构体 串联结构体,按属性创建含有元胞数组的结构体,filenames,isfield,isstruct,orderfields 觉得有用的话 ...

  6. EasyDSS高性能RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器与EasyDSS流媒体解决方案的不同

    背景分析 众所周知,立足于视频软件的开发,我们的产品很多.经常有客户问到我们产品的差别以及某个产品在某个系统架构中的具体定位,因此我想通过一系列的博客说明一下,不同产品之间的区别,以及在具体架构中自身 ...

  7. 【bat批处理】批量执行某个文件夹下的所有sql文件bat批处理

    遍历文件夹下所有的sql文件,然后命令行执行 for /r "D:\yonyou\UBFV60\U9.VOB.Product.Other" %%a in (*.sql) do ( ...

  8. net core 环境部署的坑

    1.supervisor “no such file” error. 检查指令是否正确,路径.dotnet环境是否正常 2.Couldn‘t find a valid ICU package inst ...

  9. Java中的常量池(字符串常量池、class常量池和运行时常量池)

    转载. https://blog.csdn.net/zm13007310400/article/details/77534349 简介: 这几天在看Java虚拟机方面的知识时,看到了有几种不同常量池的 ...

  10. ES6常用的新特性

    1.Let&const <!DOCTYPE html> <html lang="en"> <head> <meta charset ...