Wpf依赖属性附加属性

一、依赖属性(Dependency Property)与普通属性(CLR Property)的区别

WPF中的依赖属性(Dependency Property)与普通属性(CLR Property)相比,存在显著的不同点和额外功能:

  1. 存储机制与效率

    • 普通属性:存储在对象实例本身的内存空间中,每个实例都会独立存储属性值。
    • 依赖属性:存储在一个全局的属性系统中,WPF采用一种高效的存储结构(如稀疏存储机制),这可以减少大量相同属性值在不同实例间的重复存储,提高内存利用率。
  2. 属性系统集成

    • 普通属性:仅是简单的getter/setter对,不与WPF的属性系统结合。
    • 依赖属性:完全集成到WPF的属性系统中能够参与样式设置、数据绑定、动画、模板化、属性更改通知等高级功能
  3. 值继承与样式重写

    • 普通属性:通常不支持层次化的值继承,也不能轻易被样式所覆盖。
    • 依赖属性:支持值的继承,即父元素的某些依赖属性值可以被子元素继承;并且它们容易在样式表中被重写,便于实现样式和主题切换。
  4. 变更通知

    • 普通属性:若要支持属性变更通知(例如INotifyPropertyChanged接口),需要手动编写事件触发代码。
    • 依赖属性:内置了变更通知机制,一旦依赖属性的值发生变化,WPF会自动触发相关事件,从而更新UI和其他关联的对象。
  5. 附加属性

    • 普通属性:通常只存在于类的实例上。
    • 依赖属性:可以作为附加属性使用,这意味着其他对象可以为不属于自己的元素设置属性值。
  6. 验证和回调

    • 普通属性:若要添加验证逻辑,需要在setter中编写代码。
    • 依赖属性:可通过注册依赖属性时提供的PropertyMetadata参数指定验证回调和强制回调委托,以便在属性值变更时执行验证逻辑和额外操作。

因此,在WPF开发中,依赖属性主要用于那些需要与WPF框架深度集成并利用其特性(如数据绑定、样式和动画)的场景,而普通属性则更多地用于常规业务逻辑和简单状态维护。

依赖属性出现的目的是用来实现WPF中的样式、自动绑定及实现动画等特性

依赖属性就是可以自己没有值,并能够通过Binding从数据源获取值(依赖在别人身上)的属性。拥有依赖属性的对象被称为“依赖对象”

主要应用在以下地方:

双向绑定,依赖项属性可以不用写额的代码,也不用实现INotifyPropertyChanged接口,它本身就俱备双向绑定的特性,比如,我把员工对象的姓名绑定到摇文本框,一旦绑定,只要文本框中的值发生改变,依赖项属性员工姓名也会跟着变化,反之亦然;

触发器。这个东西在WPF中很重要,比如,一个按钮背景是红色,我想让它在鼠标停留在它上面是背景变成绿色,而鼠标一旦移开,按钮恢复红色。如果在传统的Windows编程中,你一定会想办法弄一些事件,或者委托来处理,还要写一堆代码。告诉你,有了依赖项属性,你将一行代码都不用写,所有的处理均由WPF属性系统自动处理。而触发器只是临时改变属性的值,当触完成时,属性值自动被“还原”。

依赖属性与传统的CLR属性相比优点:
节约内存:在WinForm等项目开发中,你会发现UI控件的属性通常都是赋予的初始值,为每一个属性存储一个字段将是对内存的巨大浪费。WPF依赖属性 允许对象在被创建的时候并不包含用于存储数据的空间(即字段所占用的空间)、只保留在需要用到数据的时候能够获得默认值。借用其它对象的数据或者实 时分配空间的能力----这种对象称为依赖对象而他这种实时获取数据的能力则依靠依赖属性来实现。在WPF开发中,必须使用依赖对象作为依赖属性的宿主, 使二者结合起来,才能形成完整的Binding目标被数据所驱动

与CLR属性相比编程方面依赖属性最大的作用就是不用继承实现INotifyPropertyChanged接口和定义数据源就可以实现数据变化绑定

依赖属性的使用

第一步: 创建一个依赖对象类继承自 DependencyObject基类。

在WPF中,几乎所有的UI元素都继承自DependencyObject,这个类封装了对依赖属性的存储及访问等操作依赖属性使用静态类型与依赖属性的内部存储机制相关。WPF处理依赖属性不再像普通.NET属性那样将属性值存储到一个私有变量中,而是使用一个字典型的变量来存放用户显示设置的值。

第二步:在依赖对象类中,创建一个public static readonly的依赖属性,通过注册实例化

第三步:创建一个CLR属性包装器,内部通过这个属性来实现具体的读写操作

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

第四步:创建一个返回值类型为BindingExpressionBase 的方法,方法内部主要利用BindingOperations.SetBinding实现绑定

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace WpfApp1
{
    //Student为依赖对象
    public  class Student:DependencyObject
    {
       //CLR属性  包装器
        public string Name
        {
            get { return (string)GetValue(Student. NameProperty); }
            set { SetValue(Student.NameProperty, value); }
        }
        //依赖属性
        public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student));
        //定义一个SetBinding方法
        public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding)
        {
            return BindingOperations.SetBinding(this, dp, binding);
        }
        //与CLR属性相比编程方面依赖属性最大的作用就是不用继承实现INotifyPropertyChanged接口和定义数据源就可以实现数据变化绑定

    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        Student objStudent = new Student();
        public MainWindow()
        {
            InitializeComponent();
            //依赖属性通过依赖对象的SetBinding方法绑定txt1的Text属性
            objStudent.SetBinding(Student.NameProperty, new Binding("Text") { Source = txt1 });
            //txt2控件将依赖对象的NameClr属性和txt2绑定
            txt2.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = objStudent });
        }

    }
}
<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <TextBox x:Name="txt1"></TextBox>
            <TextBox x:Name="txt2"/>
          
        </StackPanel>
    </Grid>
</Window>

二、附加属性

在WPF(Windows Presentation Foundation)中,附加属性(Attached Property)是一种特殊的依赖属性(Dependency Property),它扩展了标准.NET属性系统,使得属性不仅可以直接关联到类实例,还可以“附加”到任何派生自DependencyObject的类实例上,即便那个类并没有在它的CLR(Common Language Runtime)类中定义这个属性。

附加属性的主要特征和用途包括:

  1. 跨层级通信

    • 子控件可以通过附加属性来告知父容器或其他外部逻辑有关自身的状态或布局要求。例如,DockPanel.Dock属性就是一个经典的例子,子元素通过设置这个附加属性告诉DockPanel如何排列自己。
  2. 扩展性

    • 开发者可以在不修改现有控件源代码的情况下,为其增加新的行为或功能。
  3. 全局可访问

    • 附加属性可以在XAML标记中全局使用,无论元素原本是否支持该属性。
  4. 依赖属性特性

    • 由于附加属性本质上是依赖属性,因此它们继承了依赖属性的所有特性,比如支持数据绑定、样式设置、动画以及提供元数据以定义默认值、验证规则等。

在XAML中声明和使用附加属性的方式通常是这样的:

<!-- 定义部分 -->
<Window xmlns:myNamespace="clr-namespace:MyApp.Properties">
    <!-- 在这里定义了一个名为"MyProp"的附加属性 -->
    <myNamespace:MyClass.MyProp>
        <!-- 设置附加属性的值 -->
    </myNamespace:MyClass.MyProp>

<!-- 使用部分 -->
<Button myNamespace:MyClass.MyProp="SomeValue"/>

在C#中定义附加属性,则需要创建一个依赖属性并使用RegisterAttached方法注册:

public static class MyClass
{
    public static readonly DependencyProperty MyPropProperty =
        DependencyProperty.RegisterAttached("MyProp", typeof(string), typeof(MyClass),
            new PropertyMetadata(default(string)));

    // 提供读写访问器
    public static void SetMyProp(DependencyObject element, string value)
    {
        element.SetValue(MyPropProperty, value);
    }

    public static string GetMyProp(DependencyObject element)
    {
        return (string)element.GetValue(MyPropProperty);
    }
}

总之,附加属性是WPF框架中一种强大的机制,它极大地增强了组件之间的交互能力和灵活性。

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp1
{
     public class School:DependencyObject
    {
        //附加属性使用GetValue和SetValue两个方法获取和设置属性值,
        //依赖属性则是利用CLR属性的GetValue和SetValue方法,附加属性分开为两个方法
        public static int GetGrade(DependencyObject obj)
        {
            return (int)obj.GetValue(GradePropety);
        }

        public static void SetGrade(DependencyObject obj, int value)
        {
            obj.SetValue(GradePropety, value);
        }
        //附加属性GradePropety
        public static readonly DependencyProperty GradePropety =
            DependencyProperty.RegisterAttached("MyProperty", typeof(int), typeof(School));


    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp1
{
    public class Human:DependencyObject
    {

    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Human objHuman = new Human();
            School.SetGrade(objHuman,6);
            int grade = School.GetGrade(objHuman);
            MessageBox.Show(grade.ToString());
        }
    }
}
<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <Button Height="40" Background="Gray" Click="Button_Click"/>
        </StackPanel>
    </Grid>
</Window>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值