WPF教程(七)--依赖属性(3)--附加属性

news2024/9/20 22:33:45

一、 只读依赖属性


  以前在对于非WPF的功能来说,对于类的属性的封装中,经常会对那些希望暴露给外界只读操作的字段封装成只读属性,同样在WPF中也提供了只读属性的概念,如一些 WPF控件的依赖属性是只读的,它们经常用于报告控件的状态和信息,像IsMouseOver等属性, 那么在这个时候对它赋值就没有意义了。 或许你也会有这样的疑问:为什么不使用一般的.Net属性提供出来呢?一般的属性也可以绑定到元素上呀?这个是由于有些地方必须要用到只读依赖属性,比如 Trigger等,同时也因为内部可能有多个提供者修改其值,所以用.Net属性就不能胜任了。
  那么一个只读依赖属性怎么创建呢?其实创建一个只读的依赖属性和创建一个一般的依赖属性大同小异。不同的地方就是DependencyProperty.Register变成了DependencyProperty.RegisterReadOnly。和前面的普通依赖属性一样,它将返回一个 DependencyPropertyKey。而且只提供一个GetValue给外部,这样便可以像一般属性一样使用了,只是不能在外部设置它的值罢了。
示例如下:

public partial class WindowReadOnly : Window
 {
     public WindowReadOnly ()
     {
         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
     public int Counter
     {
         get { return (int)GetValue(counterKey.DependencyProperty); }
     }

     //用RegisterReadOnly来代替Register来注册一个只读依赖属性
     private static readonly DependencyPropertyKey counterKey =
         DependencyProperty.RegisterReadOnly("Counter",
                                             typeof(int),
                                             typeof(WindowReadOnly),
                                             new PropertyMetadata(0));
 }

XAML代码部分:

<Window x:Name="winReadOnly" x:Class="WpfApp1.WindowReadOnly"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="WindowDepend" Height="300" Width="300">
    <Grid>
        <Viewbox>
            <TextBlock Text="{Binding ElementName=winReadOnly, Path=Counter}" />
        </Viewbox>
    </Grid>
</Window>

这里写图片描述

 二、附加属性


    另外一种特殊的依赖属性——附加属性。附加属性是一种特殊的依赖属性。这是WPF的特性之一,通俗的理解起来就是,别人有的属性,由于你跟他产生了关系所以你也有了这个属于他的属性。
附加属性是说一个属性本来不属于某个对象,但由于某种需求而被后来附加上,也就是把对象放入一个特定环境后对象才具有的属性就称为附加属性,附加属性的作用就是将属性与数据类型解耦,让数据类型的设计更加灵活,

示例:一个TextBox控件被放在不同的布局容器中时就会有不同的布局属性,这些属性就是由布局容器为TextBox附加上的,附加属性的本质就是依赖属性二者仅仅在注册和包装器上有一点区别。
附加属性是依赖属性的一种特殊形式,它可以让用户在一个元素中设置其他元素的属性。一般来说,附加属性是用于一个父元素定位其他元素布局 的。就像Grid和DockPanel元素就包含附加属性。Grid使用附加属性来指定包含子元素的特定行和列,而DockPanel使用附加属性是来指定子元素应该停靠在面板中的何处位置。
附加属性就是自己没有这个属性,在某些上下文中需要就被附加上去。比如StackPanel的Grid.Row属性,如果我们定义StackPanel类时定义一个Row属性是没有意义的,因为我们并不知道一定会放在Grid里,这样就造成了浪费。
例如,下面转场控件的定义使用了Grid的Row属性来将自身定位到特定的行中。

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="101*"/>
            <RowDefinition Height="80"/>
            <RowDefinition Height="80"/>
        </Grid.RowDefinitions>
        <--!此处stackPanel控件增加了一个Grid.Row="0"附加属性-->
        <StackPanel Grid.Row="0" >

   尽管对于一个普通的WPF开发人员来说,理解依赖和附加属性并不一定是必须的,但是掌握好WPF系统的整个运行机制对于提升WPF应用技术是非常重要的。
使用附加属性,可以避开可能会防止一个关系中的不同对象在运行时相互传递信息的编码约定。一定可以针对常见的基类设置属性,以便每个对象只需获取和设置该属性即可。但是,你可能希望在很多情况下这样做,这会使你的基类最终充斥着大量可共享的属性。它甚至可能会引入以下情况:在数百个后代中,只有两个后代尝试使用一个属性。这样的类设计很糟糕。为了解决此问题,我们使用附加属性概念来允许对象为不是由它自己的类结构定义的属性赋值。在创建对象树中的各个相关对象之后,在运行时从子对象读取此值。
  最好的例子就是布局面板。每一个布局面板都需要自己特有的方式来组织它的子元素。如Canvas画板需要Top和left属性来布局,DockPanel需要Dock属性来布局
下面代码中的Button 就是用了Canvas的Canvas.Top和Canvas.Left=”20” 来进行布局定位,那么这两个就是传说中的附加属性。

<Canvas>
    <--!Canvas.Top以及Canvas.Left属性就是canvas父控件给子控件带来的附加属性-->
    <Button Canvas.Top="20" Canvas.Left="20" Content="Knights Warrior!"/>
</Canvas>

  定义附加属性的方法与定义依赖属性的方法一致,前面我们是使用DependencyProperty.Register来注册一个依赖属性,只是在注册属性时使用的是RegisterAttach()方法。这个RegisterAttached的参数和 Register是完全一致的,那么Attached(附加)这个概念又从何而来呢?
  其实我们使用依赖属性,一直在Attached(附加)。我们注册(构造)一个依赖属性,然后在DependencyObject中通过 GetValue和SetValue来操作这个依赖属性,也就是把这个依赖属性通过这样的方法关联到了这个DependencyObject上,只不过是通过封装CLR属性来达到的。那么RegisterAttached又是怎样的呢?
下面我们来看一个最简单的应用:首先我们注册(构造)一个附加属性

    public class TurnoverManager : DependencyObject
    {
        //通过静态方法的形式暴露读的操作
        public static double GetAngle(DependencyObject obj)
        {
            return (double)obj.GetValue(AngleProperty);
        }
        //通过静态方法的形式暴露写的操作
        public static void SetAngle(DependencyObject obj, double value)
        {
            obj.SetValue(AngleProperty, value);
        }
        //通过使用RegisterAttached来注册一个附加属性
        public static readonly DependencyProperty AngleProperty =
            DependencyProperty.RegisterAttached("Angle", typeof(double), typeof(TurnoverManager), new PropertyMetadata(0.0, OnAngleChanged));
        //根据附加属性中的值,当值改变的时候,旋转相应的角度。
        //创建属性时的回调函数
        private static void OnAngleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var element = obj as UIElement;
            if (element != null)
            {
                element.RenderTransformOrigin = new Point(0.5, 0.5);
                element.RenderTransform = new RotateTransform((double)e.NewValue);
            }
        }     
    }

然后,我们在程序中使用这个我们自己定义的附加属性:

<Grid>

    <Grid.RowDefinitions>
        <RowDefinition Height="313*"/>
        <RowDefinition Height="57*"/>
    </Grid.RowDefinitions>
    <Canvas Grid.Row="0">
        <Ellipse Name="ellipseRed" Fill="Red" Width="100" Height="60" Canvas.Left="56"
                 Canvas.Top="98" local:TurnoverManager.Angle="{Binding         
                                 ElementName=sliderAngle, Path=Value}"/>
        <Rectangle Name="ellipseBlue" Fill="Blue" Width="80" Height="80" 
                                 Canvas.Left="285"
                                 Canvas.Top="171" local:TurnoverManager.Angle="45" />
        <Button  Name="btnWelcome" Content="欢迎光临" Canvas.Left="265" Canvas.Top="48" 
                                 FontSize="20" local:TurnoverManager.Angle="60"/>
    </Canvas>
    <WrapPanel Grid.Row="1">
        <Label Content="角度大小" />
        <Slider x:Name="sliderAngle" Minimum="0" Maximum="240" Width="300" />
    </WrapPanel>
</Grid>

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

这里写图片描述

 效果图展示:

这里写图片描述

三,关于WPF对于依赖属性的基本操作:

我们通过下面的这幅图,简单介绍一下WPF属性系统对依赖属性操作的基本步骤:

这里写图片描述

         借用一个常见的图例,介绍一下WPF属性系统对依赖属性操作的基本步骤:
第一步,确定Base Value,对同一个属性的赋值可能发生在很多地方。比如控件的背景(Background),可能在Style或者控件的构造函数中都对它进行了赋值,这个Base Value就要确定这些值中优先级最高的值,把它作为Base Value。
第二步,估值。如果依赖属性值是计算表达式(Expression),比如说一个绑定,WPF属性系统就会计算表达式,把结果转化成一个实际值。
第三步,动画。动画是一种优先级很高的特殊行为。如果当前属性正在作动画,那么因动画而产生的值会优于前面获得的值,这个也就是WPF中常说的动画优先
第四步,强制。如果我们在FrameworkPropertyMetadata中传入了 CoerceValueCallback委托,WPF属性系统会回调我们传入的的delagate,进行属性值的验证,验证属性值是否在我们允许的范围之内。例如强制设置该值必须大于于0小于10等等。在属性赋值过程中,Coerce拥有最高的优先级,这个优先级要大于动画的优先级别。
第五步,验证。验证是指我们注册依赖属性如果提供了ValidateValueCallback委托,那么最后WPF会调用我们传入的delegate,来验证数据的有效性。当数据无效时会抛出异常来通知。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/448174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jsp+springboot基于ssm的人才招聘求职网站推荐模拟考试系统java idea

登录需要随机数字验证码 管理员&#xff1a; 招聘管理 求职信息管理 &#xff0c; 招聘信息管理 人员管理 管理注册的用户进行审核操作 公告管理 发布公告新闻 &#xff08;公告我想不到啥意思&#xff0c;可修改或增加别的&#xff09; 个人信息 修改个人信息 招聘者&am…

【沐风老师】3dMax填充地形网格插件使用方法详解

3dMax填充地形网格插件使用方法 3dMax填充地形网格插件&#xff0c;它从等高线或现有网格创建和优化地形曲面。基于四边形的输出允许更容易的后期重构&#xff0c;例如使用推/拉绘制。常规的面大小可以减少渲染问题&#xff08;GI、置换&#xff09;&#xff0c;并且可以在其上…

如何实现Windows RDP 远程桌面异地跨网连接

Windows RDP远程桌面的应用非常广泛。远程桌面协议(RDP)是一个多通道(multi-channel)的协议&#xff0c;让使用者(所在计算机称为用户端或本地计算机)连上提供微软终端机服务的计算机(称为服务端或远程计算机)。大部分的Windows版本都有用户端所需软件&#xff0c;有些其他操作…

【LeetCode】剑指 Offer 65. 不用加减乘除做加法 p310 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/ 1. 题目介绍&#xff08;65. 不用加减乘除做加法&#xff09; 写一个函数&#xff0c;求两个整数之和&#xff0c;要求在函数体内不得使用 “”、“-”、“*”、“/” 四则运算…

C语言入门篇——输入输出篇

目录 1、printf()函数 1.1、printf()函数中的标记 1.2、输出最小宽度&#xff08;width&#xff09; 1.3、精度&#xff08;.precision&#xff09; 2、scanf()函数 2.1、scanf(“输入控制符”, 输入参数) 2.2、scanf(“输入控制符非输入控制符”, 输入参数); 2.3、字符…

docker简单教程(二)启动服务,实现简单的网站

docker简单教程&#xff08;二&#xff09;启动服务 文章目录 docker简单教程&#xff08;二&#xff09;启动服务1&#xff1a;创建index.html2&#xff1a;创建Dockerfile3&#xff1a;构建4&#xff1a;启动5&#xff1a;访问6&#xff1a;下一篇将介绍一些常用的操作 这篇我…

Ae 脚本:TypeMonkey 动态文本动画

使用 TypeMonkey 脚本可以快速生成带有摄像机动画的动态文本 Kinetic text效果&#xff0c;简单快捷&#xff0c;无需繁琐的关键帧控制。 经典网络案例&#xff1a;倒鸭子 ◆ ◆ ◆ 使用方法 一般操作流程 新建合成&#xff0c;并确定好合成的持续时间。或者&#xff0c;设置…

句子改写神器-文案自动改写的免费软件

AI改写软件&#xff1a;让你的写作更加轻松高效 现代社会中&#xff0c;写作已经成为了我们各行各业必不可少的一部分。无论是文章、报道、论文还是其他各类文本&#xff0c;都需要花费大量的时间和精力来撰写。而且&#xff0c;在写作过程当中&#xff0c;我们还需要进行反复…

【Java】『蓝桥杯』10道编程题及答案(二)

系列文章 【Java】『蓝桥杯』10道编程题及答案&#xff08;一&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/130223115 【Java】『蓝桥杯』10道编程题及答案&#xff08;二&#xff09; 本文链接&#xff1a;https://blog.csdn.net/y…

Python 代码打造小 AI ,罗列博文笔记总索引列表,自动生成“我的博文笔记总索引”博文 HTML5 源码文本

Python 代码打造小 AI &#xff0c;获取笔记信息&#xff0c;自动阅读量降序编排索引列表&#xff0c;生成 HTML5 源码文本。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&…

依赖自动装配

自动配置 前面花了大量的时间把Spring的注入去学习了下&#xff0c;总结起来就一个字麻烦。 问:麻烦在哪? 答:配置文件的编写配置上。 问:有更简单方式么? 答:有&#xff0c;自动配置 什么是自动配置以及如何实现自动配置&#xff0c;就是接下来要学习的内容&#xf…

English Learning - L2-16 英音地道语音语调 语调 2023.04.20 周四

English Learning - L2-16 英音地道语音语调 语调 2023.04.20 周四 语调降调升调降升升降 语调如何正确的表情达意用降调的句型用升调的句型用降升调的句型升降调 & 平调 回顾词重音句重音弱读语音语调四步法 存档音频 语调 降调 重音音节降 升调 一般表示不确定&#xf…

进制转换—包含整数和小数部分转换(二进制、八进制、十进制、十六进制)手写版,超详细

目录 1.进制转换必备知识&#xff1a; 1.1 二进制逢2进1 8进制逢8进1 10进制逢10进1 16进制逢16进1 1.2为了区分二、八、十、十六进制&#xff0c;我们通常在数字后面加字母进行区分 2. 二进制与八进制、十六进制相互转换 2.1 二进制转八进制 2.2 八…

移动电视双天线分集接收技术解决方案

移动电视双天线分集接收技术 随着DVB-T在手机电视、车载电视、楼宇电视、地铁电视等户外广播领域内的发展&#xff0c;在这些接收范围内&#xff0c;多径衰落、多普勒频移等小范围衰落是不可避免的问题&#xff0c;解决这些衰落和干扰成为倍受关注的问题。为了解决衰落&#x…

ROS学习第二十三节——TF坐标变换实操

1.综述 需求描述: 程序启动之初: 产生两只乌龟&#xff0c;中间的乌龟(A) 和 左下乌龟(B), B 会自动运行至A的位置&#xff0c;并且键盘控制时&#xff0c;只是控制 A 的运动&#xff0c;但是 B 可以跟随 A 运行 结果演示: 实现分析: 乌龟跟随实现的核心&#xff0c;是乌龟…

在外Windows远程连接MongoDB数据库【无公网IP】

文章目录 前言1. 安装数据库2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射2.3 测试随机公网地址远程连接 3. 配置固定TCP端口地址3.1 保留一个固定的公网TCP端口地址3.2 配置固定公网TCP端口地址3.3 测试固定地址公网远程访问 转载自远程内网穿透的文章&#xff1a;公网远…

【Pytorch学习笔记】12.修改预训练模型权重参数的方法(用于对单通道灰度图使用预训练模型)

文章目录 1.导出模型参数&#xff0c;修改参数2.修改模型结构&#xff0c;导回参数 我们在训练单通道图像&#xff0c;即灰度图&#xff08;如医学影像数据&#xff09;时&#xff0c;常会使用预训练模型进行训练。 但是一般的预训练模型是以ImageNet数据集预训练的&#xff0c…

OpenAI Embedding:快速实现聊天机器人(四)

theme: orange 本文正在参加「金石计划」 接上文OpenAI Embedding&#xff1a;快速实现聊天机器人(三)如何使用Python实现embedding相似度搜索&#xff0c;这篇文章继续讲如何将搜索到的相似文本进行提炼&#xff0c;并最终得出问题的答案。 提炼文本 通过调用azure openai服务…

数据库基础篇 《10.创建和管理表》

1. 基础知识 1.1 一条数据存储的过程 1.2 标识符命名规则 1.3 MySQL中的数据类型 其中&#xff0c;常用的几类类型介绍如下&#xff1a; 2. 创建和管理数据库 2.1 创建数据库 方式1&#xff1a;创建数据库 CREATE DATABASE 数据库名; 方式2&#xff1a;创建数据库并指…

Netty工作模型——网络IO模型的演进,从BIO到NIO到Reactor模型与Netty工作模型

文章目录 一、IO模型1、BIO&#xff08;同步阻塞&#xff09;2、NIO&#xff08;同步非阻塞&#xff09;3、AIO&#xff08;异步非阻塞&#xff09; 二、Reactor模型1、单Reactor单线程2、单Reactor多线程3、主从Reactor多线程 三、Netty工作模型1、Netty工作模型2、Netty入门案…