Silverlight的依赖属性与附加属性

13、Silverlight中的依赖属性、附加属性与路由事件详解 本文详细解析了Silverlight中的三大核心概念:依赖属性、附加属性和路由事件。依赖属性通过动态值解析机制,实现高效的属性管理;附加属性允许在不同类之间共享属性;路由事件通过事件冒泡机制,简化了复杂界面的事件处理逻辑。文章还通过示例展示了它们的实际应用场景,包括自定义布局面板和事件处理机制,帮助开发者更好地掌握Silverlight的高级特性,提升应用开发效率和灵活性。 阅读详情

好久没写Silverlight了,依赖属性(Dependency Property)和附加属性(Attached Property)这两个算是很基础的知识都不是很记得了。写一写,当做一下笔记吧。

CLR属性 与 依赖属性

CLR属性我们非常熟悉了,在DotNet编程中随处可见。最简单最常见的属性访问器就是直接操纵类的私有成员,如下:

public class Per son
{
    private String _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

C#3.0对这种常见的写法提供了“自动属性”这一特性,方便了偶等这些懒惰的码农。

public class Person { public string Name { get; set; } }

这两种写法是等价的,都是需要设立一个实例级的私有变量作为属性访问器的持久存储。这对于我们非UI应用来说没什么。因为第一,我们一般不会创建太多类实例;第二,一个类的属性通常不会很多,加几个私有变量不会增加系统负担。但是这两个理由对于UI应用程序来说恰恰不成立。

在很多UI应用中,我们经常会创建很多类实例,成千上万个实例在UI系统中是很普遍的事情。同时,UI类通常会包含大量的属性供设计人员使用,例如背景颜色,前景颜色,字体,边距等等,这些属性在绝大多数情况下会保持默认值,如果为每个实例都建立这么多的私有变量来存储UI属性的值,势必会造成极大的浪费,对系统负担的开销也是不小。

鉴于以上提到的问题,设计一个高效的属性存储系统对于UI应用程序的开发是非常重要的。因此Silverlight引入了“依赖属性(DependencyProperty)”。

采用键值对替代成员变量作为属性内部存储

传统CLR属性,一个属性对应一个私有变量,UI元素的属性那么多,创建过多的私有变量不是一件简单的事情,况且大多数属性只会用到默认值。因此Silverlight使用键值对的形式来存放那些用户显式设置的属性(称为Local Value本地值),没有设置的属性就不存。那属性的默认值存放在哪?既然各个实例的默认值都一样(不然也不叫默认值了),那么直接存放到静态成员变量上就行了。这也就大大提高了存储的效率。

注册依赖属性

既然依赖属性采用键值对这样的哈希结构进行存储,那么要获取不同属性的值,我们就必须使用不同的哈希键,否则就会读取到其他属性的值了。因此,当我们在向Silverlight属性系统注册依赖属性的时候,Silverlight会返回一个唯一的属性标识对象,类型为DependencyProperty。我们以后就通过这个唯一标识对象去访问依赖属性的值。

由于这个唯一标识符是所有类实例都公用并且不会被修改的,因此我们通常将其保存到一个static readonly的成员变量中。

DependencyProperty类提供了两个方法,一个是Register方法,用于注册依赖属性;另外一个是RegisterAttached,用于注册附加属性,这个后面再讲。

public static DependencyProperty Register(
    string name,
    Type propertyType,
    Type ownerType,
    PropertyMetadata typeMetadata
)

Register方法的签名由几部分组成,Name参数指明了依赖属性使用的名称,这个名字很重要,在定义控件Style和Template的时候,Setter的Property属性填入的值就是注册依赖属性时使用的名称;propertyType指明了依赖属性实际的类型,ownerType指明了是哪个类注册了此依赖属性,最后typeMetadata存放了一些依赖属性的元信息,包括依赖属性使用的默认值,还有属性值发生变更时的通知函数。

属性的存取

和CLR属性不同,依赖属性不是直接对私有变量的操纵,而是通过GetValue和SetValue的方法来操作属性值的。

 

下面的代码演示了为Ball控件设置一个Center的依赖属性,并且在程序中读取和修改此属性的过程:

public class Ball : Control { public static readonly DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof(Point), typeof(Ball), null); } public class BallApp { public void RollBall(Ball ball) { Point curCenter = (Point)ball.GetValue(Ball.CenterProperty); curCenter.X++;

// 注意对值类型对象操作完毕之后一定要调用SetValue修改才能生效 ball.SetValue(Ball.CenterProperty, curCenter); } }

由于上述对依赖属性的操作经常需要涉及到类型的转换,比较麻烦,而传统CLR属性用起来和直接操纵普通变量一样方便,因此通常在设计依赖属性的时候,都会使用CLR属性将其包装起来,我们称之为增强型的CLR属性。

public class Ball : Control
{
    public static readonly DependencyProperty CenterProperty =
        DependencyProperty.Register("Center", typeof(Point), typeof(Ball), null);
    
    public Point Center
    {
        get { return (Point)GetValue(CenterProperty); }
        set { SetValue(CenterProperty, value); }
    }
}

按照约定,依赖属性的名称通常是相应CLR属性名称后面加上个“Property”字符串。

事实上,使用CLR包装依赖属性并不只是为了方便(http://msdn.microsoft.com/en-us/library/cc221408%28VS.95%29.aspx#back_dependency_properties),很多依赖于CLR属性作为基础的工具或者子系统并不能直接访问依赖属性,而只能通过CLR属性去间接访问依赖属性。例如上面的例子中,假设我们并没有设置一个Center的CLR属性,那么以下的Xaml将会编译失败,因为Xaml解析器无法知道Ball类有一个Center的依赖属性(在Style中设置Center属性值就可以编译成功,因为Style是动态查找属性的)。

<Ball Center="2" />

依赖属性的寻值逻辑和值变更通知

上面提到的只是依赖属性相比CLR属性在存储效率的不同,实际上,依赖属性还有其他实用的特性。

寻值逻辑

CLR属性在获取值的时候是直接读取成员变量值返回的,而依赖属性在使用的时候是通过GetValue函数的调用来获取属性的值。实际上,GetValue内部做的事情可不止是简单的读取字典里头存放的值。他还有寻值逻辑。如下图所示:

 

当你调用GetValue去读取一个依赖属性的值的时候,Silverlight的属性系统会首先从动画系统中查找当前是否有作用在此依赖属性上的动画,如果有,则返回此动画值。从这里也可以看出,依赖属性是Silverlight实现动画机制的基础。注意,如果动画已经停止了,并且没有设置FillBehavior=HoldEnd的话,那么Silverlight就不会返回此动画值。

如果读不到动画值,那么Silverlight就会尝试读取本地值。本地值有几种类型,一种是用户通过代码或者Xaml直接设定的值。一种是通过资源绑定得到的值,最后一种是通过数据绑定得到的值。这些都被视为本地值。

<StackPanel x:Name="LayoutRoot">
    <StackPanel.Resources>
        <System:String x:Key="TextBlockResource">资源数据绑定文本
  System:String>
    
   StackPanel.Resources>
    <TextBlock Text="{Binding Source={StaticResource TextBlockResource}}" />
    <TextBlock x:Name="DataBindingElement" Text="{Binding ElementName}" />

    StackPanel>

如果还是读取不到,那么就继续尝试读取控件模板和样式中设置的值。

如果所有这些值都读取失败,那么Silverlight属性系统就会返回该依赖属性的默认值。当我们注册依赖属性的时候,可以传入一个PropertyMetaData对象,这个对象包含了此依赖属性的默认值和值变更通知回调函数。如果注册的时候没有传入默认值,则对于引用类型的依赖属性,返回null,对于字符串,返回String.Empty,对于值类型,则返回一个以默认值初始化的实例。

这里需要对集合类型特别注意,由于通过PropertyMetaData传入的默认值是所有类实例共享的,因此,一定要在类构造函数中显式传入集合的实例。

public class GameRoom : Control
{
    public List
 
   Balls
    {
        get { 
  return (List
  
   )GetValue(BallsProperty); }
        set { SetValue(BallsProperty, 
   value); }
    }

    
   public 
   static 
   readonly DependencyProperty BallsProperty =
        DependencyProperty.Register(
   "Balls", 
   typeof(List
   
    ), 
    typeof(GameRoom), 
    null);

    
    public GameRoom()
    {
        Balls = 
    new List
    
     ();
    }
}
    
   
  
 

可能正是因为Silverlight的依赖属性在获取值的时候需要从多个地方去读取值,而不是像CLR属性一样,直接从成员变量中读取值,所以才被称之为“依赖”属性吧。

值变更通知

属性值的变更通知我们并不陌生。我们在DotNet中实现的时候,一般是让类实现INotifyPropertyChanged接口。在UI系统中,值变更通知是经常需要用到的。数据源一旦变更,所有相应的UI元素的值都要相应的做出调整。Silverlight的依赖属性对此有内置的支持。只要你在绑定时使用依赖属性,那么当依赖属性值发生变更的时候,所有绑定的地方的值都会同步更新。而且,依赖属性也提供了一个值变更通知函数(在注册依赖属性时通过PropertyMetaData传入),你可以自定义一个函数来控制值变更时需要执行的操作。

 

public class Ball : Control { public static readonly DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof(Point), typeof(Ball), new PropertyMetadata(OnCenterChanged)); public Point Center { get { return (Point)GetValue(CenterProperty); } set { SetValue(CenterProperty, value); } } private static void OnCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Ball ball = d as Ball; // 获取新的球心 Point newCenter = (Point)e.NewValue; // ... } }

Silverlight的附加属性(Attached Property)——全局的依赖属性

刚才提到的依赖属性和CLR属性一样都是服务于某一个类的。只不过将属性改造得存储上更有效率,使用上更加强大。在Silverlight中还有一种特殊的依赖属性,这种依赖属性并不只是服务于某个特定的类,而是服务于全局,这就是附加属性。从名字上也可以看出来,附加属性是在某个类里面注册,然后可以被其他类所使用。

什么情况下需要使用附加属性呢?举Canvas类的ZIndex属性作为例子。

容器类在叠加子控件的时候,需要考虑哪个控件放置在最上面,那个放在下面。

 

那么容器类怎么知道子控件的叠放顺序呢?最不动脑子的设计就是为所有的控件都添加一个ZIndex的属性,属性的值代表叠放的顺序。但这样的后果就是,如果我这个控件不参与布局,那多这个属性就会显得很浪费。所以比较理想的设计是,需要用到这个属性的时候就有这个属性,不需要的时候就没有这个属性的负担。附加属性的出现就是为了解决这样的问题。一旦控件需要某个属性的时候,我们可以把这个属性附加到这个控件类上。

注册附加属性和依赖属性差不多,只不过函数名为RegisterAttached。这个就不多说了。

什么时候应该用到依赖属性

既然依赖属性那么高效,而且那么强大,那么我们是不是应该保持使用依赖属性的习惯呢?事实上,任何好处都是有代价的。Silverlight的依赖属性在访问效率上并不如直接访问成员变量那么高效。因此,对于那些比较简单而访问频率又非常高的属性,建议还是使用传统的CLR属性去实现。

 

暂时想到这么多了,以后有新的认识再补充补充。

——Kevin Yang

【故障处理】- ORA-01722 在执行升级脚本@?/rdbms/admin/catupgrd.sql时报错ora-01722。 阅读详情

相关推荐

IC 时钟设计核心问题与高质量系统设计指南(附源码)

本文深入探讨了IC时钟设计的核心问题与高质量系统设计方法。时钟系统作为IC的"心脏",其性能直接影响时序稳定性、功耗效率和可靠性。文章从五个维度展开分析:时钟源选择与性能匹配、时钟分配网络设计、抖动抑制、多时钟域管理和功耗优化。针对高质量时钟系统设计,提出了时钟源优化、时钟树结构优化、干扰抑制与仿真验证等具体方法。同时,系统阐述了时钟与复位的协同设计关系,强调异步复位同步释放等技术的重要性。文章还总结了高质量IC系统设计的综合要点,包括电源/信号完整性、时序功耗平衡、可靠性设计及全流程验

Open 1060

附加属性和依赖属性

无论什么时候,只要依赖属性的值发生改变,wpf就会自动根据属性的元数据触发一系列的动作,这些动作可以重新呈现UI元素,也可以更新当前的布局,刷新数据绑定等等,这种变更的通知最有趣的特点之一就是属性触发器,他可以在属性值改变的时候,执行一系列自定义的动作,而不需要更改任何其他的代码来实现。依赖属性和CRL属性类似,提供一个实例级私有字段的访问封装,通过GetValue和SetValue访问器实现属性的读写操作。附加属性是由支持附加属性的父元素产生作用,支持附加属性的元素会继承所在的父元素。...

hp0524chl的博客 990

支付系统保证可靠性的秘诀 ----- 订单的补偿和补单

文章目录WHAT? 前言WHY? 为什么要用多级缓存?HOW 怎么做?方案 版本1:数据库存储专线配置通过优先级进行动态上下线方案 版本1 劣势:方案 版本2 :本地缓存替代数据库方案 版本2 劣势:方案 版本3:分布式缓存做自动补偿方案 版本3 总结:Where 分布式多级缓存还能用在哪些地方?总结 WHAT? 前言 刚入职场的时候觉得支付宝等第三方支付系统好强大,要保证这么多的交易不能错,也不能重复还要保证正确的扣钱,直到后面接触订单系统以及第三方支付公司的核心交易系统后才真正窥探到了支付系统的核心

boboaisisi的专栏 3534

WPF 依赖属性和附加属性

WPF 依赖属性和附加属性

lvxingzhe3的博客 2471

C#与发那科机器人通信实战:从SDK配置到机械臂控制

本文详细介绍了如何使用C#与发那科机器人进行通信,从SDK配置到机械臂控制的完整实战流程。内容包括环境准备、通信连接建立、机械臂运动控制、异常处理与状态监控,以及高级功能实战,帮助开发者快速掌握工业机器人编程技术。

weixin_42524276的博客 457

麒麟系统无法进入单用户模式该怎么办?

🏆本文收录于《全栈Bug调优(实战版)》专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 备注:部分问题/疑难杂症搜集于互联网。

**My Coding Family** 1820

DependencyProperty.Register:wpf 向别的控件传递参数

在这里,如果你的属性更改回调方法在 OnMessagePropertyChanged 中执行了一些逻辑,那么在属性更改时,这个逻辑就会被触发。示例代码:在MainWindow.xaml中要嵌入UserControl1.xaml,并且向UserControl1.xaml传入参数,当参数改变时,触发回调函数。: 这是一个 PropertyMetadata 对象,它包含关于依赖属性的元数据,例如默认值、属性更改回调等信息。: 这是包含依赖属性的类的类型。: 这是包含依赖属性的类的类型。: 依赖属性的名称。

博客 1911

[毕设项目-MCP+ENSP]一句话搞定ENSP自动化:MCP + LLM实战(架构图、工具清单、踩坑全解)

用 MCP + LLM 把“人话”变成对华为 ENSP 的安全自动化操作,三款工具覆盖配置/查询/落盘,配套架构、实战与排坑清单。

逐梦苍穹的博客 2537

WPF 依赖属性和附加属性区别

WPF 依赖属性和附加属性区别

IT_ziliang的博客 5871

CTR-GCN 论文解读

提出了一种信道拓扑优化图卷积,该卷积可以动态有效地对信道拓扑进行建模。共享拓扑是一个参数化的邻接矩阵,作为所有通道的拓扑先验,并提供顶点之间的一般相关性。通过使用特定于通道的相关性细化共享拓扑,CTR-GC 获得了通道拓扑。

一杯水果茶!足矣~ 3088

Windows 11 安装 JDK 25:下载、安装、配置环境变量、验证

本文详细介绍了在 Windows 11 系统上安装 JDK 25 的完整流程,包括从 Oracle 官网下载压缩包、解压至自定义目录、配置 JAVA_HOME 与 Path 环境变量,并验证安装是否成功。文章推荐使用 ZIP 压缩包而非 EXE 安装程序,以实现绿色便携、多版本共存和灵活管理。最后通过一个使用 Java 25 新特性的示例程序,验证 JDK 的编译与运行环境。

寒水馨 · 轻量级技术文档 · hanshuixin.org 7659

深入了解 |WPF的DependencyProperty

今天我们将深入了解WPF的一个核心概念:DependencyProperty。

VickyQu214的博客 2599

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Silverlight 依赖属性与附加属性

依赖属性(Dependency Property) 依赖属性是存储于基类DependencyObject中的一个键值配对字典中的。它并没有存储在所属的类中,所以不会每次都去重新创建一个属性默认值占用内存。 #region 设置一个X坐标属性 public double X { get { return (doub...

anbimo3361的博客 174

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Silverlight的依赖属性与附加属性(一)

学习Silverlight已经一段时间,当中也弄个几个小东西,也是用过mvvm进行过开发,但是平时都是拿来主义,有问题就百度或者是谷歌,觉得需要系统的学习。 下面是我在学习中看过的各位大神的博客,转载过来算是用于资料的积累。Silverlight的依赖属性与附加属性Silverlight AttachedProperty,CLR属性,DependancyProperty,...

weixin_30856965的博客 124

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

上一篇: 莫名其妙的Silverlight资源文件引用问题
下一篇: 盘点国内外不同特色的Web流量分析工具
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值