您好,
会员登录 快速注册
退出 ( 条未读消息)
关于本站 意见反馈 首页

公告:小宅博客网可以开发票了,需要发票的,去群里找群主哈!!
全部文章分类
  • 人工智能 >

  • 编程语言 >

  • WPF系列 >

  • ASP.NET系列 >

  • Linux >

  • 数据库 >

  • 嵌入式 >

  • WEB技术 >

  • PLC系列 >

  • 微服务与框架 >

  • 小宅DIY >

  • 学习资料 >

OpenCv基础 ANN车牌识别 yolov5车牌识别 指针式仪表识别 ROS系列 YOLO Halcon Detectron2 昇腾AI ChatGPT在线体验 英伟达JETSON ChatGLM ChatTTS FunASR 地平线 ByteTrack 魔搭社区 LangChain
C C# C++ Python Java Go
WPF
ASP.NET小功能 GPS定位系统-MVC GPS定位系统-VUE ASP.NET WebRTC
Linux Linux内核 Shell MakeFile
MySql SqlServer Oracle
STM8 STM32 51单片机
VUE入门 HTML JavaScript CSS layui镜像网站 ElementUi中文官网 element-plus 图标
三菱 欧姆龙 西门子 施耐德 松下 台达
IOTSharp IOTGateway ABP FRAMEWORK Docker
亚克力音响 编程仙途:智驭万法
面试题与技巧 Python入门技能树 微软C#教程
首页 编程之美 工具下载 全国就业 流量地图 文心一言
WPF
内容介绍 1、新建WPF工程 2、Application介绍 3、Dispatcher介绍 4、Window 介绍 5、主要布局属性介绍 6、Grid网格布局 7、UniformGrid布局 8、DockPanel与ViewBox布局 9、Border与ViewBox布局 10、依赖属性(一) 11、依赖属性(二) 12、依赖属性(三) 13、依赖属性(四) 14、WPF中的数据绑定(一) 15、WPF中的数据绑定(二) 16、WPF中的数据绑定(三) 17、WPF中的数据绑定(四) 18、ListView示例(一) 19、ListView示例(二) 20、DataGrid示例(一) 21、DataGrid示例(二) 22、DataGrid示例(三) 23、引用FontAwesome矢量图 24、ListBox日志效果 25、Polygon绘制多边形 26、Ellipse绘制实心圆 27、数据模板DataTemplate 基于WPF的exe远程升级程序
11、依赖属性(二)
13、依赖属性(四)
激萌の小宅 博客园 WPF

文章作者:激萌の小宅

促销:¥0

价格:¥0

配送方式: 购买后立即生效(如购买异常,请联系站长)
付款之后一定要等待自动跳转结束,否则购买可能会失败
  • 0 天

    有效期

  • 0

    总销量

  • 2

    累计评价

依赖属性(三) - (十二)


四、只读依赖属性

        在以前在对于非WPF的功能来说,对于类的属性的封装中,经常会对那些希望暴露给外界只读操作的字段封装成只读属性,同样在WPF中也提供了只读属性的概念,如一些 WPF控件的依赖属性是只读的,它们经常用于报告控件的状态和信息,像IsMouseOver等属性, 那么在这个时候对它赋值就没有意义了。 或许你也会有这样的疑问:为什么不使用一般的.Net属性提供出来呢?一般的属性也可以绑定到元素上呀?这个是由于有些地方必须要用到只读依赖属性,比如 Trigger等,同时也因为内部可能有多个提供者修改其值,所以用.Net属性就不能完成天之大任了。 

       那么一个只读依赖属性怎么创建呢?其实创建一个只读的依赖属性和创建一个一般的依赖属性大同小异。不同的地方就是DependencyProperty.Register变成了DependencyProperty.RegisterReadOnly。和前面的普通依赖属性一样,它将返回一个 DependencyPropertyKey。而且只提供一个GetValue给外部,这样便可以像一般属性一样使用了,只是不能在外部设置它的值罢了。

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

<Window x:Name="mainWindow" x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="300">
    <Grid>
        <Viewbox>
            <TextBlock Text="{Binding ElementName=mainWindow, Path=Counter }" />
        </Viewbox>
    </Grid>
</Window>


XAML代码:

using System;
using System.Windows;
using System.Windows.Threading;

namespace WpfApp
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            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(MainWindow), new PropertyMetadata(0));
    }
}


效果如下图所示: 


五、附加属性

       现在我们再继续探讨另外一种特殊的依赖属性——附加属性。附加属性是一种特殊的依赖属性。这是WPF的特性之一,通俗的理解起来就是,别人有的属性,由于你跟他产生了关系所以你也有了这个属于他的属性。

       附加属性是说一个属性本来不属于某个对象,但由于某种需求而被后来附加上,也就是把对象放入一个特定环境后对象才具有的属性就称为附加属性,附加属性的作 用就是将属性与数据类型解耦,让数据类型的设计更加灵活,举例,一个TextBox被放在不同的布局容器中时就会有不同的布局属性,这些属性就是由布局容 器为TextBox附加上的,附加属性的本质就是依赖属性,二者仅仅在注册和包装器上有一点区别。   

       附加属性是依赖属性的一种特殊形式,它可以让用户在一个元素中设置其他元素的属性。一般来说,附加属性是用于一个父元素定位其他元素布局 的。就像Grid和DockPanel元素就包含附加属性。Grid使用附加属性来指定包含子元素的特定行和列,而DockPanel使用附加属性是来指 定子元素应该停靠在面板中的何处位置。 

       附加属性就是自己没有这个属性,在某些上下文中需要就被附加上去。比如StackPanel的Grid.Row属性,如果我们定义StackPanel类时定义一个Row属性是没有意义的,因为我们并不知道一定会放在Grid里,这样就造成了浪费。

例如,下面转场控件的定义使用了Grid的Row属性来将自身定位到特定的行中。

<Grid.RowDefinitions>
    <RowDefinition Height="101*"/>
    <RowDefinition Height="80"/>
    <RowDefinition Height="80"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" >


       尽管对于一个普通的WPF开发人员来说,理解依赖和附加属性并不一定是必须的,但是掌握好WPF系统的整个运行机制对于提升WPF应用技术是非常重要的。 

       使用附加属性,可以避开可能会防止一个关系中的不同对象在运行时相互传递信息的编码约定。一定可以针对常见的基类设置属性,以便每个对象只需获取和 设置该属性即可。但是,你可能希望在很多情况下这样做,这会使你的基类最终充斥着大量可共享的属性。它甚至可能会引入以下情况:在数百个后代中,只有两个 后代尝试使用一个属性。这样的类设计很糟糕。为了解决此问题,我们使用附加属性概念来允许对象为不是由它自己的类结构定义的属性赋值。在创建对象树中的各 个相关对象之后,在运行时从子对象读取此值。 

       最好的例子就是布局面板。每一个布局面板都需要自己特有的方式来组织它的子元素。如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来注册一个依赖属性,只是在注册属性时使用的是RegisterAttach()方法。这个RegisterAttached的参数和 Register是完全一致的,那么Attached(附加)这个概念又从何而来呢?

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

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

using System.Windows;
using System.Windows.Media;

namespace WpfApp.Services
{
    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);
            }
        }
    }
}


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

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp.Services"
        mc:Ignorable="d"
        Title="MainWindow" Height="400" Width="500">
    <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>
</Window>


通过调整角度值,显示不同的效果如下

11、依赖属性(二)
13、依赖属性(四)

友情链接: CSDN激萌の小宅 95知识库 自考题库 罗分明个人网络博客 精益编程leanboot

小宅博客  www.bilibili996.com All Rights Reserved. 备案号: 闽ICP备2024034575号

网站经营许可证  福建省福州市 Copyright©2021-2025 版权所有

小宅博客
首页 智能家居 地图定位
公告:小宅博客网可以开发票了,需要发票的,去群里找群主哈!!

文章作者:激萌の小宅

促销:¥0

价格:¥0

配送方式: 购买后立即生效(如购买异常,请联系站长)
付款之后一定要等待自动跳转结束,否则购买可能会失败
  • 0 天

    有效期

  • 0

    总销量

  • 2

    累计评价

依赖属性(三) - (十二)


四、只读依赖属性

        在以前在对于非WPF的功能来说,对于类的属性的封装中,经常会对那些希望暴露给外界只读操作的字段封装成只读属性,同样在WPF中也提供了只读属性的概念,如一些 WPF控件的依赖属性是只读的,它们经常用于报告控件的状态和信息,像IsMouseOver等属性, 那么在这个时候对它赋值就没有意义了。 或许你也会有这样的疑问:为什么不使用一般的.Net属性提供出来呢?一般的属性也可以绑定到元素上呀?这个是由于有些地方必须要用到只读依赖属性,比如 Trigger等,同时也因为内部可能有多个提供者修改其值,所以用.Net属性就不能完成天之大任了。 

       那么一个只读依赖属性怎么创建呢?其实创建一个只读的依赖属性和创建一个一般的依赖属性大同小异。不同的地方就是DependencyProperty.Register变成了DependencyProperty.RegisterReadOnly。和前面的普通依赖属性一样,它将返回一个 DependencyPropertyKey。而且只提供一个GetValue给外部,这样便可以像一般属性一样使用了,只是不能在外部设置它的值罢了。

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

<Window x:Name="mainWindow" x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="300">
    <Grid>
        <Viewbox>
            <TextBlock Text="{Binding ElementName=mainWindow, Path=Counter }" />
        </Viewbox>
    </Grid>
</Window>


XAML代码:

using System;
using System.Windows;
using System.Windows.Threading;

namespace WpfApp
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            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(MainWindow), new PropertyMetadata(0));
    }
}


效果如下图所示: 


五、附加属性

       现在我们再继续探讨另外一种特殊的依赖属性——附加属性。附加属性是一种特殊的依赖属性。这是WPF的特性之一,通俗的理解起来就是,别人有的属性,由于你跟他产生了关系所以你也有了这个属于他的属性。

       附加属性是说一个属性本来不属于某个对象,但由于某种需求而被后来附加上,也就是把对象放入一个特定环境后对象才具有的属性就称为附加属性,附加属性的作 用就是将属性与数据类型解耦,让数据类型的设计更加灵活,举例,一个TextBox被放在不同的布局容器中时就会有不同的布局属性,这些属性就是由布局容 器为TextBox附加上的,附加属性的本质就是依赖属性,二者仅仅在注册和包装器上有一点区别。   

       附加属性是依赖属性的一种特殊形式,它可以让用户在一个元素中设置其他元素的属性。一般来说,附加属性是用于一个父元素定位其他元素布局 的。就像Grid和DockPanel元素就包含附加属性。Grid使用附加属性来指定包含子元素的特定行和列,而DockPanel使用附加属性是来指 定子元素应该停靠在面板中的何处位置。 

       附加属性就是自己没有这个属性,在某些上下文中需要就被附加上去。比如StackPanel的Grid.Row属性,如果我们定义StackPanel类时定义一个Row属性是没有意义的,因为我们并不知道一定会放在Grid里,这样就造成了浪费。

例如,下面转场控件的定义使用了Grid的Row属性来将自身定位到特定的行中。

<Grid.RowDefinitions>
    <RowDefinition Height="101*"/>
    <RowDefinition Height="80"/>
    <RowDefinition Height="80"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" >


       尽管对于一个普通的WPF开发人员来说,理解依赖和附加属性并不一定是必须的,但是掌握好WPF系统的整个运行机制对于提升WPF应用技术是非常重要的。 

       使用附加属性,可以避开可能会防止一个关系中的不同对象在运行时相互传递信息的编码约定。一定可以针对常见的基类设置属性,以便每个对象只需获取和 设置该属性即可。但是,你可能希望在很多情况下这样做,这会使你的基类最终充斥着大量可共享的属性。它甚至可能会引入以下情况:在数百个后代中,只有两个 后代尝试使用一个属性。这样的类设计很糟糕。为了解决此问题,我们使用附加属性概念来允许对象为不是由它自己的类结构定义的属性赋值。在创建对象树中的各 个相关对象之后,在运行时从子对象读取此值。 

       最好的例子就是布局面板。每一个布局面板都需要自己特有的方式来组织它的子元素。如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来注册一个依赖属性,只是在注册属性时使用的是RegisterAttach()方法。这个RegisterAttached的参数和 Register是完全一致的,那么Attached(附加)这个概念又从何而来呢?

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

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

using System.Windows;
using System.Windows.Media;

namespace WpfApp.Services
{
    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);
            }
        }
    }
}


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

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp.Services"
        mc:Ignorable="d"
        Title="MainWindow" Height="400" Width="500">
    <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>
</Window>


通过调整角度值,显示不同的效果如下