依赖属性和附加属性

本文深入探讨了WPF中的依赖属性,包括DependencyObject类、DependencyProperty注册、属性系统服务、PropertyMetadata和附加属性。依赖属性支持数据绑定、样式、动画等功能,提供属性更改通知和值强制。文中通过代码示例展示了如何创建和使用依赖属性,以及如何实现属性验证和回调。此外,还讨论了附加属性的概念及其在不同场景中的应用。

依赖属性dependency property

为什么需要依赖属性?是为了节约资源(节省内存开销)。

 

DependencyObject类

Object->DispatcherObject->DependencyObject

表示参与依赖属性系统的对象。所有依赖属性都派生自次类。

DependencyObject类在其多个派生类上启用 Windows Presentation Foundation (WPF) 属性系统服务。

属性系统的主要功能是计算属性值,并提供有关已更改值的系统通知。 参与属性系统的另一个关键类是DependencyProperty。 DependencyProperty允许将依赖项属性注册到属性系统中,并提供有关每个依赖项属性的标识和信息,而DependencyObject作为基类使对象可以使用依赖项属性。

DependencyObject 服务和特征包括:

  • 依赖属性承载支持。 通过调用 Register 方法,并将该方法的返回值存储为类中的公共静态字段,来注册依赖属性。
  • 附加属性托管支持。 可以通过调用方法来注册附加属性 RegisterAttached ,并将该方法的返回值存储为类中的公共静态只读字段。 (还有其他成员要求;请注意,这表示附加属性的 WPF 特定实现。 有关详细信息,请参阅 附加属性概述。然后可以在派生自的任何类上设置附加属性 DependencyObject ) 。
  • 获取、设置和清除中存在的任何依赖项属性值的实用工具方法 DependencyObject 。
  • 元数据、强制值支持、属性更改通知和替代依赖属性或附加属性的回调。 此外, DependencyObject 类还有助于依赖项属性的每个所有者的属性元数据。
  • 派生自、或的类的公共 ContentElement 基类 Freezable Visual 。 (UIElement ,另一个基元素类具有包含的类层次结构 Visual 。 )
DependencyObject属性
名称备注权限

DependencyObjectType

获取 DependencyObjectType 包装此实例的 CLR 类型的。get;

IsSealed

获取一个值,该值指示此实例当前是否为密封的(只读)。get;
DependencyObject方法
名称备注权限

ClearValue

清除属性的本地值。public

CoerceValue

对指定依赖属性的值进行强制。 通过对调用方 DependencyObject 上存在的依赖属性的属性元数据中所指定的任何 CoerceValueCallback 函数进行调用来完成此操作。public

Equals

确定提供的 DependencyObject 是否等效于当前 DependencyObjectpublic

GetHashCode

获取此 DependencyObject 的哈希代码。public

GetLocalValueEnumerator

创建一个专用的枚举数,用于确定哪些依赖项属性在此 DependencyObject 上具有以本地方式设置的值。public

GetValue

对 DependencyObject 的此实例返回依赖属性的当前有效值。public

InvalidateProperty

重新评估指定依赖属性的有效值。public

OnPropertyChanged

每当更新此 DependencyObject 的任何依赖属性的有效值时调用。 更改的特定依赖属性将在事件数据中报告。protected

ReadLocalValue

如果存在,则返回依赖属性的本地值。public

SetCurrentValue

设置依赖属性的值而不更改其值源。public

SetValue

设置依赖属性的本地值。public

ShouldSerializeProperty

返回一个值,该值指示序列化进程是否应序列化所提供的依赖属性的值。protected

 

DependencyProperty类

Object->DependencyProperty

表示可通过诸如样式、数据绑定、动画和继承等方法设置的属性。

  • 可以设置样式的属性。有关更多信息,请参见样式和模板

  • 可以通过数据绑定设置该属性。有关数据绑定依赖项属性的更多信息,请参见如何:绑定两个控件的属性

  • 可以使用动态资源引用来设置该属性。有关更多信息,请参见XAML资源

  • 该属性可以自动从元素树中的父元素继承其值。有关更多信息,请参见属性值继承

  • 该属性可以设置动画。有关更多信息,请参见动画概述

  • 该属性可以报告该属性的先前值已​​更改的时间,并且该属性值可以被强制。有关更多信息,请参见依赖项属性回调和验证

  • 该属性向WPF报告信息,例如,更改属性值是否需要布局系统重新组合元素的外观。

  • 该属性在WPF Designer for Visual Studio中获得支持。例如,可以在“属性”窗口中编辑该属性

  • 附加属性是使任何对象能够将信息报告给定义该附加属性的类型的属性。在WPF中,任何从DependencyObject继承的类型都可以使用附加属性,而不管该类型是否从定义该属性的类型继承。附加属性是XAML语言的功能。要在XAML中设置附加属性,请使用ownerTypepropertyName语法。附加属性的一个示例是DockPanel.Dock属性。如果要创建可在所有DependencyObject类型上使用的属性,则应创建一个附加属性。要了解有关附加属性的更多信息,包括如何创建它们,请参见附加属性概述

 

DependencyProperty.字段
名称备注权限

UnsetValue

指定一个由 WPF 属性系统使用的静态值,而不是 null 指示该属性存在,但不具有属性系统设置的值。public
DependencyProperty属性
名称备注权限

DefaultMetadata

获取依赖项对象的默认元数据。get;

GlobalIndex

获取唯一标识依赖项对象的内部生成值。get;

Name

获取依赖属性的名称。get;

OwnerType

获取使用属性系统注册依赖属性或者将自己作为属性所有者添加的对象的类型。get;

PropertyType

获取依赖项对象用于其值的类型。get;

ReadOnly

获取一个值,该值指示由此 DependencyProperty 实例标识的依赖项对象是否为只读依赖项对象。get;

ValidateValueCallback

获取依赖项对象的值验证回调。get;

 

DependencyProperty方法
名称备注权限

AddOwner

将另一种类型添加为已注册到一种类型的依赖属性的所有者。public

GetHashCode

返回此 DependencyProperty 的哈希代码。public

GetMetadata

返回与此依赖属性关联的的元数据(只要它对于特定的类型存在)。 它可以是在其中首次注册依赖属性的类型、随后将其添加到的类型,或在其中通过继承获取依赖属性而已专门重写元数据的类型。public

IsValidType

确定指定的值对于该依赖项对象的类型是否可接受(与原依赖项对象注册中提供的属性类型相对照)。public

IsValidValue

确定所提供的值通过基本类型检查后是否被属性类型接受,以及它是否有可能在该类型的值的允许范围以内。public

OverrideMetadata

当此依赖属性位于指定类型的实例上时为其提供替换元数据(而不是在最初注册依赖属性时提供的元数据)。public

Register

注册依赖属性。public

RegisterAttached

在属性系统上注册附加属性。public

RegisterAttachedReadOnly

注册只读附加属性。public

RegisterReadOnly

将依赖属性注册为只读依赖属性。public

ToString

返回依赖属性的字符串表示形式。public

 

 

PropertyMetadata类

Object->PropertyMetadata

定义依赖属性在应用于特定类型(包括该属性向其注册的条件)时行为的某些方面。

可以在调用Register方法(或附加属性或只读依赖项属性的变体)时在依赖项属性注册期间定义属性元数据,并在调用OverrideMetadata方法时在原始所有者注册后定义和使用属性元数据。AddOwner还采用属性元数据。

此类是一个具体的基类,可以在每个这些调用中使用。但是,使用诸如FrameworkPropertyMetadata之类的派生类之一指定元数据是非常普遍的。这些派生类支持作为布尔属性值携带的更详细的元数据,这些元数据对于检测或启用仅在WPF框架级别实现的某些属性系统和布局行为很有用。

此类的几个属性可读写到对象模型,但只能在实例在属性系统操作(例如RegisterOverrideMetadata)中使用之前写入。这些属性中的每一个也可以由构造函数设置,但是可以公开,以便Merge方法实现可以对其进行设置。

 

PropertyMetadata 属性
名称备注权限

CoerceValueCallback

获取或设置对此元数据中所指定 CoerceValueCallback 实现的引用。get; set;

DefaultValue

获取或设置依赖属性的默认值。get; set;

IsSealed

获取一个值,该值确定是否已通过某种方式将元数据应用于属性,从而导致该元数据实例变为不可变状态。get;

PropertyChangedCallback

获取或设置对此元数据中所指定 PropertyChangedCallback 实现的引用。get; set;
PropertyMetadata方法
名称备注权限

Merge

将此元数据与基元数据合并。protected

OnApply

当此元数据已经应用到一个属性时(这表明正在密封元数据)调用。protected

依赖属性

创建依赖属性

  • 创建的类派生自DependencyObject 
  • 成员变量同时被public static readonly修饰
  • 用DependencyProperty.Register方法注册依赖属性

 

设置、获取依赖属性

快速创建

输入propdp,二次按下Tab,如下,进行相应修改

    public int MyProperty
    {
        get { return (int)GetValue(MyPropertyProperty); }
        set { SetValue(MyPropertyProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

PropertyMetadata

CoerceValueCallback:依赖属性值被强制改变时,此委托会被调用。此委托可关联一个相应函数。

DefaultValue:依赖属性未被显式赋值时,若读取之则获取此默认值,不设此值会抛出异常。

IsSealed:控制PropertyMetadata的属性值是否可以被修改,默认值是true。

PropertyChangedCallback:依赖属性的值被改变之后,此委托会被调用,此委托可以关联一个相应函数。

依赖属性的DefaultMetadata只能通过Register方法的第四个参数进行赋值,一旦赋值不能改变(只读)。如果想要新的PropertyMetadata替换,需要使用DependencyProperty.OverrideMetadata方法。

 

DependencyProperty.Register方法

public static System.Windows.DependencyPropertyKey RegisterReadOnly (string name, Type propertyType, Type ownerType, System.Windows.PropertyMetadata typeMetadata);

public static System.Windows.DependencyPropertyKey RegisterReadOnly (string name, Type propertyType, Type ownerType, System.Windows.PropertyMetadata typeMetadata, System.Windows.ValidateValueCallback validateValueCallback);

public static System.Windows.DependencyProperty Register (string name, Type propertyType, Type ownerType);

public static readonly DependencyProperty IsDirtyProperty = DependencyProperty.Register(
  "IsDirty",
  typeof(Boolean),
  typeof(AquariumObject3)
);

public static System.Windows.DependencyProperty Register (string name, Type propertyType, Type ownerType, System.Windows.PropertyMetadata typeMetadata, System.Windows.ValidateValueCallback validateValueCallback);

public static System.Windows.DependencyProperty RegisterAttached (string name, Type propertyType, Type ownerType);

public static System.Windows.DependencyProperty RegisterAttached (string name, Type propertyType, Type ownerType, System.Windows.PropertyMetadata defaultMetadata);

public static System.Windows.DependencyProperty RegisterAttached (string name, Type propertyType, Type ownerType, System.Windows.PropertyMetadata defaultMetadata, System.Windows.ValidateValueCallback validateValueCallback);

public static System.Windows.DependencyPropertyKey RegisterAttachedReadOnly (string name, Type propertyType, Type ownerType, System.Windows.PropertyMetadata defaultMetadata);

public static System.Windows.DependencyPropertyKey RegisterAttachedReadOnly (string name, Type propertyType, Type ownerType, System.Windows.PropertyMetadata defaultMetadata, System.Windows.ValidateValueCallback validateValueCallback);
name:要注册的依赖属性的名称。
propertyType:属性的类型。
ownerType:正在注册依赖属性的所有者类型。
typeMetadata:依赖属性的属性元数据。
validateValueCallback:对回调的引用,除了典型的类型验证之外,该引用还应执行依赖属性值的任何自定义验证。

FrameworkPropertyMetadata

Object->PropertyMetadata->UIPropertyMetadata->FrameworkPropertyMetadata

使用FrameworkPropertyMetadata对象配置创建的依赖属性的附加功能。

为依赖属性报告或应用元数据(从而专门添加特定于框架的属性系统特征)。

此类从PropertyMetadata派生(通过UIPropertyMetadata)。对于大多数WPF框架级应用程序开发目的,FrameworkPropertyMetadata是用于依赖项属性元数据的类型,而不是基本元数据类型PropertyMetadataUIPropertyMetadata。对于现有的依赖项属性和大多数自定义依赖项属性方案都是如此。

此类声明的补充PropertyMetadata基类的成员包括各种布尔属性,这些布尔属性指定或报告WPF框架级别的属性系统行为,例如属性继承,数据绑定和布局。

用于创建FrameworkPropertyMetadata实例的多个构造函数签名带有FrameworkPropertyMetadataOptions参数。所述FrameworkPropertyMetadataOptions枚举仅用于在构造来指定初始行为,并且之后不以其它方式暴露FrameworkPropertyMetadata构造。从构造的实例中,您可以通过各种属性来获取或设置相应的信息,这些属性共享构造函数调用中使用的枚举值的名称。

FrameworkPropertyMetadata属性
名称备注权限

AffectsArrange

获取或设置一个值,该值表示依赖属性是否会影响布局引擎操作期间的排列过程。get; set;

AffectsMeasure

获取或设置一个值,该值表示依赖属性是否会影响布局引擎操作期间的测量过程。get; set;

AffectsParentArrange

获取或设置一个值,该值表示依赖属性是否会影响布局引擎操作期间父元素布局的排列处理过程。get; set;

AffectsParentMeasure

获取或设置一个值,该值表示依赖属性是否会影响布局引擎操作期间其父元素布局的测量过程。get; set;

AffectsRender

获取或设置一个值,该值指示依赖属性是否会以某种方式(这种方式不会具体影响排列或测量,但将请求重绘)对常规布局造成潜在影响。get; set;

BindsTwoWayByDefault

获取或设置一个值,该值指示默认情况下属性是否双向绑定。get; set;

DefaultUpdateSourceTrigger

获取或设置在应用具有该元数据的属性的绑定时要使用的 UpdateSourceTrigger 的默认值,这些绑定的 UpdateSourceTrigger 设置为 Defaultget; set;

Inherits

获取或设置一个值,该值指示依赖属性的值是否可继承。get; set;

IsDataBindingAllowed

获取一个值,该值指示依赖属性是否支持数据绑定。get;

IsNotDataBindable

获取或设置一个值,该值指示依赖属性是否支持数据绑定。get; set;

Journal

获取或设置一个值,该值表示该属性是否包含应用程序可以或应该作为日记功能实现的一部分而存储的日记信息。get; set;

OverridesInheritanceBehavior

获取或设置一个值,该值指示属性值继承计算是否应跨越元素逻辑树中的某些内容边界。get; set;

SubPropertiesDoNotAffectRender

获取或设置一个值,该值指示依赖属性的子属性是否影响包含对象的呈现。get; set;
FrameworkPropertyMetadata方法
名称备注权限

Merge

允许合并源元数据和基本元数据。protected

OnApply

当此元数据已经应用到一个属性时(这表明正在密封元数据)调用。protected

WPF对依赖属性值的读取优先级

优先级
1)WPF属性系统强制值
2)由动画过程控制的值
3)本地变量值(存储在Effective ValueEntry数组中)
4)由上级元素的Template设置值
5)由隐藏样式(Implicit Style)设置的值
6)由样式触发器(Style Trigger)设置的值
7)由模板触发器(Template Trigger)设置的值
8)由样式(Style Setter)设置的值
9)由默认样式(Default Style)设置的值,默认模式其实就是由主题(Theme)指定的模式。
10)由上级元素继承而来的值
11)默认值,源于依赖属性的元数据(metadata)

WPF对依赖属性值的设置

  1. 首先验证依赖属性的值是否可写,如果不能就排除异常。
  2. 检查值是不是DependencyProperty.UnsetValue,如果是,说明调用者的意图是清空现有值。此时程序会调用C了爱人ValueCommom方法来清空现有值。
  3. 检查EffectiveValueEntry数组中是否已经存在响应依赖属性的位置,如果有就把旧值改写为新值;如果没有就新建EffectiveValueEntry对象并存储新值。
  4. 调用UpdataEffectiveValue对新值做一些响应处理。

WPF使用依赖属性的方式(更改通知和动态值识别)

当属性值变化时,如果希望进行响应:

  • 创建绑定
  • 触发器
  • OnPropertyChangedCallBack()方法、PropertyChangedCallBack回调函数

共享依赖属性

TextBlock.FontFamily和Control.FontFamily属性指向一个静态的依赖属性。

TextElement类的静态构造函数注册该属性,而TextBlock类和Control类的静态构造函数只能通过调用DependencyProperty.AddOwner()方法重用该属性:

TextBlock.FontFamilyProperty=TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));

 

范例

XAML范例

<Window x:Class="DependencyPropertyDemo.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:DependencyPropertyDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="400">
    <Window.Resources>
        <local:MyDependencyObject x:Key="myData"/>
    </Window.Resources>
    <StackPanel>
        <TextBlock  Margin="5" Text="{Binding Source={StaticResource myData}, Path=MyProperty}"/>
        <TextBox  Margin="5"  Text="{Binding Source={StaticResource myData},Path=MyProperty,UpdateSourceTrigger=PropertyChanged}"/>      
    </StackPanel>
</Window>
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 DependencyPropertyDemo
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();         
        }
    }

    public class MyDependencyObject: DependencyObject
    {
        public string MyProperty
        {
            get { return (string)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }
        public static readonly DependencyProperty MyPropertyProperty =DependencyProperty.Register("MyProperty", typeof(string), typeof(MyDependencyObject));
    }
}

C#范例

<Window x:Class="DependencyPropertyDemo.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:DependencyPropertyDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="400">
    <StackPanel>
        <TextBlock  Margin="5" />
        <TextBox  Margin="5"  />      
    </StackPanel>
</Window>
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 DependencyPropertyDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            MyDependencyObject myDependencyObject = new MyDependencyObject();
            myDependencyObject.MyProperty = "MyProperty";

            Binding binding1 = new Binding();
            binding1.Path = new PropertyPath("MyProperty");
            Binding binding2 = new Binding();
            binding2.Path = new PropertyPath("Text");
            binding2.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            foreach (var item in (this.Content as StackPanel).Children)
            {
                if (item is TextBlock)
                {
                    binding1.Source = myDependencyObject;
                    BindingOperations.SetBinding((item as TextBlock), TextBlock.TextProperty, binding1);
                }
                if (item is TextBox)
                {
                    binding2.Source = (item as TextBox);
                    BindingOperations.SetBinding(myDependencyObject, MyDependencyObject.MyPropertyProperty, binding2);
                }
            }
        }
    }

    public class MyDependencyObject: DependencyObject
    {
        public string MyProperty
        {
            get { return (string)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }
        public static readonly DependencyProperty MyPropertyProperty =DependencyProperty.Register("MyProperty", typeof(string), typeof(MyDependencyObject));
    }
}

 


附加属性AttachedProperty

Object->AttachedProperty

用于将附加信息关联或附加到对象的实例。

把对象放入特定环境后对象才具有的属性(被环境赋予的属性)就称为附加属性。

Windows Workflow Designer使用它来跟踪在编辑会话期间有趣的数据片段,否则这些数据是瞬态的,这些数据(在大多数情况下)将在编辑会话结束时被丢弃。AttachedProperty非常有用,因为View中的项目可以绑定到值并对更改做出反应。例如,这就是显示所选项目模板的方式(它从附加属性中取消选择)。

 

附加属性的声明、注册和使用

输入propa,两次按下Tab后,修改以下代码

    public static int GetMyProperty(DependencyObject obj)
    {
        return (int)obj.GetValue(MyPropertyProperty);
    }

    public static void SetMyProperty(DependencyObject obj, int value)
    {
        obj.SetValue(MyPropertyProperty, value);
    }

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.RegisterAttached("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));
  • 创建的类派生自DependencyObject 
  • 成员变量同时被public static readonly修饰
  • 实现GetMyProperty、SetMyProperty
  • 用DependencyProperty.RegisterAttached方法注册附加属性

附加属性也可以使用Binding依赖在其他对象的数据上。

        <Canvas>
            <TextBlock Canvas.Left="{Binding ElementName=slider1,Path=Value}"/>
        </Canvas>
TextBlock.SetBinding(Canvas.LeftProperty,new Binding("Value"){Source=slider1});

 

范例:PasswordBox的绑定

来源:

https://www.bilibili.com/video/BV1Jy4y1C7hU?p=12

<PasswordBox common:PasswordHelper.Attach="True"
             common:PasswordHelper.Password="{BindingLoginModel.Password,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
public class PasswordHelper
    {
        //防止附加属性值与控件属性值循环触发赋值< Password_PasswordChanged > < OnPropertyChanged >
        private static bool _isUpdating = false;
 
        //附加属性值变化后给推送给控件
        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.RegisterAttached("Password", typeof(string), typeof(PasswordHelper), new FrameworkPropertyMetadata(default(string), new
                 PropertyChangedCallback(OnPropertyChanged)));
        public static string GetPassword(DependencyObject d)
        {
            return d.GetValue(PasswordProperty).ToString();
        }
        public static void SetPassword(DependencyObject d, object value)
        {
            d.SetValue(PasswordProperty, value);
        }
        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PasswordBox password = d as PasswordBox;
            password.PasswordChanged -= Password_PasswordChanged;
            if (_isUpdating)
                password.Password = e.NewValue?.ToString();
            password.PasswordChanged += Password_PasswordChanged;
        }
 
 
 
        public static readonly DependencyProperty AttachProperty =
            DependencyProperty.RegisterAttached("Attach", typeof(bool), typeof(PasswordHelper), new FrameworkPropertyMetadata(default(bool), new
                 PropertyChangedCallback(OnAttached)));
        public static bool GetAttach(DependencyObject d)
        {
            return (bool)d.GetValue(AttachProperty);
        }
        public static void SetAttach(DependencyObject d, object value)
        {
            d.SetValue(AttachProperty, value);
        }
        //控件的值变化后给推送给附加属性
        private static void OnAttached(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PasswordBox password = d as PasswordBox;
            password.PasswordChanged += Password_PasswordChanged;
        }
        //控件的值变化后给推送给附加属性
        private static void Password_PasswordChanged(object sender, RoutedEventArgs e)
        {
            PasswordBox passwordBox = sender as PasswordBox;
            _isUpdating = true;
            SetPassword(passwordBox, passwordBox.Password);
            _isUpdating = false;
        }
    }

属性验证

WPF阻止非法值的方法

表示用作验证依赖属性有效值的回调的方法。

public delegate bool ValidateValueCallback(object value);

 

为只要重新计算依赖项属性值或专门请求强制转换时就调用的方法提供一个模板。

public delegate object CoerceValueCallback(DependencyObject d, object baseValue);

设置依赖属性的过程:

  1. CoerceValueCallback修改提供的值,或者返回DependencyProperty.UnsetValue(完全拒绝修改)
  2. ValidateValueCallback,该方法返回true以接受一个值作为合法值,或者返回false拒绝新值
  3. PropertyChangedCallback,引发更改事件。

PropertyChangedCallback 委托

表示在依赖属性的有效属性值更改时调用的回调。

public delegate void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e);

 

范例

 

<Window x:Class="DependencyPropertyDemo.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:DependencyPropertyDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="400">
    <Window.Resources>
        <local:MyDependencyObject x:Key="myData"/>
    </Window.Resources>
    <StackPanel>
        <TextBlock  Margin="5" Text="{Binding Source={StaticResource myData}, Path=MyProperty}" Focusable="True"/>
        <TextBox  Margin="5"  Text="{Binding Source={StaticResource myData},Path=MyProperty,UpdateSourceTrigger=LostFocus}"/>
    </StackPanel>
</Window>
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 DependencyPropertyDemo
{
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class MyDependencyObject : DependencyObject
    {
        static int i = 0;
        public string MyProperty
        {
            get { return (string)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }
        public static readonly DependencyProperty MyPropertyProperty = 
               DependencyProperty.Register("MyProperty", typeof(string), typeof(MyDependencyObject),
                   new FrameworkPropertyMetadata("1", FrameworkPropertyMetadataOptions.None,new PropertyChangedCallback(OnPropertyChanged), new CoerceValueCallback(OnoerceValue)),
                   new ValidateValueCallback(OnValidateValue));

        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
           
            MessageBox.Show("OnPropertyChanged:"+i++.ToString());
        }


        private static bool OnValidateValue(object value)
        {
            if (value.ToString().Length<=6)   return true; 
            return false;
        }

        private static object OnoerceValue(DependencyObject d, object baseValue)
        {           
            return baseValue.ToString();
        }
    }
}


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值