八. 只读依赖属性

  我们以前在对简单属性的封装中,经常会对那些希望暴露给外界只读操作的字段封装成只读属性,同样在WPF中也提供了只读属性的概念,如一些 WPF控件的依赖属性是只读的,它们经常用于报告控件的状态和信息,像IsMouseOver等属性, 那么在这个时候对它赋值就没有意义了。 或许你也会有这样的疑问:为什么不使用一般的.Net属性提供出来呢?一般的属性也可以绑定到元素上呀?这个是由于有些地方必须要用到只读依赖属性,比如 Trigger等,同时也因为内部可能有多个提供者修改其值,所以用.Net属性就不能完成天之大任了。
那么一个只读依赖属性怎么创建呢?其实创建一个只读的依赖属性和创建一个一般的依赖属性大同小异(研究源码你会发现,其内部都是调用的同 一个Register方法)。仅仅是用DependencyProperty.RegisterReadonly替换了 DependencyProperty.DependencyProperty而已。和前面的普通依赖属性一样,它将返回一个 DependencyPropertyKey。该键值在类的内部暴露一个赋值的入口,同时只提供一个GetValue给外部,这样便可以像一般属性一样使 用了,只是不能在外部设置它的值罢了。

下面我们就用一个简单的例子来概括一下:

public partial class Window1 : Window{public Window1(){InitializeComponent();//内部用SetValue的方式来设置值DispatcherTimer timer =new DispatcherTimer(TimeSpan.FromSeconds(1),DispatcherPriority.Normal,(object sender, EventArgs e)=>{int newValue = Counter == int.MaxValue ? 0 : Counter + 1;SetValue(counterKey, newValue);},Dispatcher);}//属性包装器,只提供GetValue,这里你也可以设置一个private的SetValue进行限制public int Counter{get { return (int)GetValue(counterKey.DependencyProperty); }}//用RegisterReadOnly来代替Register来注册一个只读的依赖属性private static readonly DependencyPropertyKey counterKey =DependencyProperty.RegisterReadOnly("Counter",typeof(int),typeof(Window1),new PropertyMetadata(0));}

XAML中代码:

<Window x:Name="winThis" x:Class="WpfApplication1.Window1"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Read-Only Dependency Property" Height="300" Width="300">    <Grid>        <Viewbox>            <TextBlock Text="{Binding ElementName=winThis, Path=Counter}" />        </Viewbox>    </Grid></Window>

效果如下图所示:

九. 附加属性

  前面我们讲了依赖属性。现在我们再继续探讨另外一种特殊的Dependency属性——附加属性。附加属性是一种特殊的依赖属性。他允许给一个对象添加一个值,而该对象可能对此值一无所知。

  最好的例子就是布局面板。每一个布局面板都需要自己特有的方式来组织它的子元素。如Canvas需要Top和left来布 局,DockPanel需要Dock来布局。当然你也可以写自己的布局面板(在上一篇文章中我们对布局进行了比较细致的探讨,如果有不清楚的朋友也可以再 回顾一下)。

下面代码中的Button 就是用了Canvas的Canvas.Top和Canvas.Left="20" 来进行布局定位,那么这两个就是传说中的附加属性。

<Canvas>    <Button Canvas.Top="20" Canvas.Left="20" Content="Knights Warrior!"/></Canvas>

  在最前面的小节中,我们是使用DependencyProperty.Register来注册一个依赖属性,同时依赖属性本身也对外提供了 DependencyProperty.RegisterAttached方法来注册附加属性。这个RegisterAttached的参数和 Register是完全一致的,那么Attached(附加)这个概念又从何而来呢?

  其实我们使用依赖属性,一直在Attached(附加)。我们注册(构造)一个依赖属性,然后在DependencyObject中通过 GetValue和SetValue来操作这个依赖属性,也就是把这个依赖属性通过这样的方法关联到了这个DependencyObject上,只不过是 通过封装CLR属性来达到的。那么RegisterAttached又是怎样的呢?

下面我们来看一个最简单的应用:首先我们注册(构造)一个附加属性

public class AttachedPropertyChildAdder{//通过使用RegisterAttached来注册一个附加属性public static readonly DependencyProperty IsAttachedProperty =DependencyProperty.RegisterAttached("IsAttached", typeof(bool), typeof(AttachedPropertyChildAdder),new FrameworkPropertyMetadata((bool)false));//通过静态方法的形式暴露读的操作public static bool GetIsAttached(DependencyObject dpo){return (bool)dpo.GetValue(IsAttachedProperty);}//通过静态方法的形式暴露写的操作public static void SetIsAttached(DependencyObject dpo, bool value){dpo.SetValue(IsAttachedProperty, value);}
}

在XAML中就可以使用刚才注册(构造)的附加属性了:

  

  在上面的例子中,AttachedPropertyChildAdder 中 并没有对IsAttached采用CLR属性形式进行封装,而是使用了静态SetIsAttached方法和GetIsAttached方法来存取 IsAttached值,当然如果你了解它内部原理,你就会看到实际上还是调用的SetValue与GetValue来进行操作(只不过拥有者不同而 已)。这里我们不继续深入下去,详细在后面的内容会揭开谜底。

十. 清除本地值

  在很多时候,由于我们的业务逻辑和UI操作比较复杂,所以一个庞大的页面会进行很多诸如动画、3D、多模板及样式的操作,这个时候页面的值已经 都被改变了,如果我们想让它返回默认值,可以用ClearValue 来清除本地值,但是遗憾的是,很多时候由于WPF依赖属性本身的设计,它往往会不尽如人意(详细就是依赖属性的优先级以及依赖属性EffectiveValueEntry 的 影响)。ClearValue 方法为在元素上设置的依赖项属性中清除任何本地应用的值提供了一个接口。但是,调用 ClearValue 并不能保证注册属性时在元数据中指定的默认值就是新的有效值。值优先级中的所有其他参与者仍然有效。只有在本地设置的值才会从优先级序列中移除。例如,如 果您对同时也由主题样式设置的属性调用 ClearValue,主题值将作为新值而不是基于元数据的默认值进行应用。如果您希望取消过程中的所有属性值,而将值设置为注册的元数据默认值,则可以 通过查询依赖项属性的元数据来最终获得默认值,然后使用该默认值在本地设置属性并调用 SetValue来实现,这里我们得感得PropertyMetadata类为我们提供了诸如DefaultValue这样的外部可访问的属性。

上面讲了这么多,现在我们就简单用一个例子来说明上面的原理(例子很直观,相信大家能很容易看懂)

XAML中代码如下:

<Window  x:Class="WpfApplication1.DPClearValue"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Window1" Height="400" Width="400">    <StackPanel Name="root">        <StackPanel.Resources>            <Style TargetType="Button">                <Setter Property="Height" Value="20"/>                <Setter Property="Width" Value="250"/>                <Setter Property="HorizontalAlignment" Value="Left"/>            </Style>            <Style TargetType="Ellipse">                <Setter Property="Height" Value="50"/>                <Setter Property="Width" Value="50"/>                <Setter Property="Fill" Value="LightBlue"/>            </Style>            <Style TargetType="Rectangle">                <Setter Property="Height" Value="50"/>                <Setter Property="Width" Value="50"/>                <Setter Property="Fill" Value="MediumBlue"/>            </Style>            <Style x:Key="ShapeStyle" TargetType="Shape">                <Setter Property="Fill" Value="Azure"/>            </Style>        </StackPanel.Resources>        <DockPanel Name="myDockPanel">            <Ellipse Height="100"   Width="100" Style="{StaticResource ShapeStyle}"/>            <Rectangle Height="100" Width="100"  Style="{StaticResource ShapeStyle}" />        </DockPanel>        <Button Name="RedButton" Click="MakeEverythingAzure" Height="39" Width="193">改变所有的值</Button>        <Button Name="ClearButton" Click="RestoreDefaultProperties" Height="34" Width="192"> 清除本地值</Button>    </StackPanel></Window>

后台代码:

public partial class DPClearValue{//清除本地值,还原到默认值void RestoreDefaultProperties(object sender, RoutedEventArgs e){UIElementCollection uic = myDockPanel.Children;foreach (Shape uie in uic){LocalValueEnumerator locallySetProperties = uie.GetLocalValueEnumerator();while (locallySetProperties.MoveNext()){DependencyProperty propertyToClear = (DependencyProperty)locallySetProperties.Current.Property;if (!propertyToClear.ReadOnly) { uie.ClearValue(propertyToClear); }}}}//修改本地值void MakeEverythingAzure(object sender, RoutedEventArgs e){UIElementCollection uic = myDockPanel.Children;foreach (Shape uie in uic) { uie.Fill = new SolidColorBrush(Colors.Azure); }}}

当按下”改变所有的值“按钮的时候,就会把之前的值都进行修改了,这个时候按下”清除本地值“就会使原来的所有默认值生效。

转载于:https://blog.51cto.com/knightswarrior/383935

WPF基础到企业应用系列7——深入剖析依赖属性(三)相关推荐

  1. WPF基础到企业应用系列8——依赖属性之“风云再起”

    一. 摘要 首先圣殿骑士很高兴"WPF 基础到企业应用系列" 能得到大家的关注.支持和认可.看到很多朋友留言希望加快速度的问题,我会尽力的,对你们的热情关注也表示由衷的感谢.这段时 ...

  2. WPF 基础到企业应用系列5——WPF千年轮回 续前缘

    一,摘要 首先很高兴这个系列能得到大家的关注和支持,前端时间身体状况不适,所以暂停了更新,对此表示非常抱歉,以后会逐渐加快进度,不过由于这是一个很长的系列,我也想把它写好,所以以后也会慢慢来,在这个系 ...

  3. 一起谈.NET技术,WPF 基础到企业应用系列5——WPF千年轮回2

    一,摘要 首先很高兴这个系列能得到大家的关注和支持,前端时间身体状况不适,所以暂停了更新,对此表示非常抱歉,以后会逐渐加快进度,不过由于这是一个很长的系列,我也想把它写好,所以以后也会慢慢来,在这个系 ...

  4. WPF 基础到企业应用系列2——WPF前世今生

    1.开篇前言       很多时候了解一项新技术的历史和趋势往往比这项技术的本身价值还要重要.WPF作为一项新技术(已经三年多了,或者应该叫老技术了),我们都有必要了解它的来龙去脉,尤其是公司的CTO ...

  5. WPF 基础到企业应用系列1——开篇故意

    參考资料 提到參考资料,大家第一感觉就是MSDN,当然我也不例外.这个站点基本上是学习微软技术的首选站点,除了这个站点以外,我还參考了非常多其它的社区和站点,基本上都在.NET 技术社区之我见(英文篇 ...

  6. WPF快速入门系列(2)——深入解析依赖属性

    一.引言 感觉最近都颓废了,好久没有学习写博文了,出于负罪感,今天强烈逼迫自己开始更新WPF系列.尽管最近看到一篇WPF技术是否老矣的文章,但是还是不能阻止我系统学习WPF.今天继续分享WPF中一个最 ...

  7. C#-WPF基础入门和进阶系列课程1 运算符封装和案例

    创建控制台项目,主函数中定义创建如下: 任何复杂的业务,都是在此基础上面去增加扩展出来的! 根基基础扎实了,变化的业务输出也才会是水到渠成的事情: 否则您仍然会寸步难行: #region 运算符封装基 ...

  8. 【零基础玩转BLDC系列】无刷直流电机无位置传感器三段式启动法详细介绍及代码分享

    无刷直流电动机基本转动原理等内容请参考<基于霍尔传感器的无刷直流电机控制原理>.<基于反电动势过零检测法的无刷直流电机控制原理>与<以GD32F30x为例定时器相关功能详 ...

  9. WPF基础学习笔记(一)Dependency Object 和 Dependency Property

    .依赖属性是WPF个人觉得对精彩和最有特色的部分.所以特地先拿出来. 首先要实现Dependency Property 则必须要继承Dependency Object.如果看下WPF的基础控件其实都间 ...

最新文章

  1. Spring AOP与IOC
  2. 【Cocosd2d实例教程五】Cocos2d添加虚拟摇杆控制器
  3. 滴滴与能链杀红眼的加油市场,究竟有多大?
  4. 入行IT,为什么建议你学Java?
  5. java安全编码指南之:声明和初始化
  6. 为什么有些女孩在发现渣男的真面目以后,还喜欢他们?
  7. mysql 超时_为MySQL设置查询超时
  8. 介绍某现金贷平台的决策规则
  9. 5、Fiddler如何捕获HTTPS会话
  10. linux压缩文件命令_Linux基础篇(二)--Linux常用命令
  11. Sqlyog的安装与使用
  12. 计算机代数与数论pdf,基础数论算法 - maTHμ - 计算机代数系统.pdf
  13. 计算机机试题Excel,2009年职称计算机考试_Excel机试题-1
  14. 《UnityAPI.Animator动画器》(Yanlz+Unity+SteamVR+云技术+5G+AI+VR云游戏+Animator+avatar+CrossFade+Key+立钻哥哥++OK++)
  15. 利用antd进行轻量级表单开发,获取验证码
  16. 搞懂朴素贝叶斯分类算法
  17. Zend与PHP之间到底是什么关系
  18. gb28181协议流媒体实现为rtp荷载ps流,将h264流打包成ps流。
  19. MySQL必知必会二:MySQL简介
  20. 怎么使用Navicat连接数据库?

热门文章

  1. 百度社会化分享组件使用问题
  2. 安卓微软雅黑字体ttf_618巨献丨精致的悦黑5字重小字体
  3. vsftp账号_Linux入门-CentOS7安装vsftp
  4. zabbix查看mysql同步_Zabbix 检测Mysql数据库的主从同步
  5. 策略模式在jdk Arrays 中使用
  6. mysql -- MAC下安装配置mysql
  7. 盘点程序员最喜欢的15个网站
  8. 编程应该用 Mac ,还是 PC ?
  9. 2017已过半,这半年,你累吗?
  10. 查询CPU占用高的SQL语句的解决方案