天天看点

初步了解WPF依赖属性

一 依赖属性

    在WPF库实现中,依赖属性使用普通的C#属性进行了包装,使得我们可以通过和以前一样的方式来使用依赖属性。但必须明确,在WPF中我们大多数都在使用依赖属性,而不是使用属性。依赖属性重要性在于,在WPF核心特性,如动画、数据绑定以及样式中都需要使用到依赖属性。WPF中的依赖属性主要有以下三个优点:

    依赖属性加入了属性变化通知、限制、验证等功能。这样可以使我们更方便地实现应用,同时大大减少了代码量。许多之前需要写很多代码才能实现的功能,在WPF中可以轻松实现。

    节约内存:在WinForm中,每个UI控件的属性都赋予了初始值,这样每个相同的控件在内存中都会保存一份初始值。而WPF依赖属性很好地解决了这个问题,它内部实现使用哈希表存储机制,对多个相同控件的相同属性的值都只保存一份。

    支持多种提供对象:可以通过多种方式来设置依赖属性的值。可以配合表达式、样式和绑定来对依赖属性设置值。

二 如何自己定义一个依赖属性

    在C#属性定义的基础上;

    让依赖属性的所在类型继承自DependencyObject类;

    使用public static 声明一个DependencyProperty的变量,该变量就是真正的依赖属性;

    在类型的静态构造函数中通过Register方法完成依赖属性的元数据注册;

    提供一个依赖属性的包装属性,通过这个属性来完成对依赖属性的读写操作;

    依赖属性是通过调用DependencyObject的GetValue和SetValue来对依赖属性进行读写的。它使用哈希表来进行存储的,对应的Key就是属性的HashCode值,而值(Value)则是注册的DependencyPropery;而C#中的属性是类私有字段的封装,可以通过对该字段进行操作来对属性进行读写。总结为:属性是字段的包装,WPF中使用属性对依赖属性进行包装。

三 依赖属性优先级

    按从高到低排序:

1 属性系统强制转换。强制转换和动画在本 SDK 中都作用于称为“基值”的值。便于动画不受别的设置影响。

2 活动动画或具有 Hold 行为的动画。为了获得任何实用效果,属性的动画必须优先于基(未动画)值,即使该值是在本地设置的情况下也将如此。

3 本地值。本地值可以通过“包装”属性 (Property) 的便利性进行设置,这也相当于在 XAML 中设置 Attribute 或 Property 元素,或者使用特定实例的属性调用 SetValue API。如果您使用绑定或资源来设置本地值,则每个值都按照直接设置值的优先级顺序来应用。

4 TemplatedParent 模板属性。如果元素是作为模板(ControlTemplate 或 DataTemplate)的一部分创建的,则具有 TemplatedParent。在模板中,按以下优先级顺序应用:

    1 来自 TemplatedParent 模板的触发器。

    2 TemplatedParent 模板中的属性 (Property) 集。(通常通过 XAML 属性 (Attribute) 进行设置。)

5 隐式样式。仅应用于Style属性。Style属性是由任何样式资源通过与其类型匹配的键来填充的。该样式资源必须存在于页面或应用程序中;查找隐式样式资源不会进入到主题中。

6 样式触发器。来自页面或应用程序上的样式中的触发器。(这些样式可以是显式或隐式样式,但不是来自优先级较低的默认样式。)

7 模板触发器。来自样式中的模板或者直接应用的模板的任何触发器。

8 样式 Setter。来自页面或应用程序的样式中的 Setter 的值。

9 默认(主题)样式。在默认样式中,按以下优先级顺序应用:

    1 主题样式中的活动触发器。

    2 主题样式中的 Setter。

10 继承。有几个依赖项属性从父元素向子元素继承值,因此不需要在应用程序中的每个元素上专门设置这些属性。

11 来自依赖项属性元数据的默认值。任何给定的依赖项属性都具有一个默认值,它由该特定属性的属性系统注册来确定。而且,继承依赖项属性的派生类具有按照类型重写该元数据(包括默认值)的选项。因为继承是在默认值之前检查的,所以对于继承的属性,父元素的默认值优先于子元素。因此,如果任何地方都没有设置可继承的属性,将使用在根元素或父元素中指定的默认值,而不是子元素的默认值。

四 一个简单实例

初步了解WPF依赖属性
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid>  
    <Button x:Name="myButton" Background="Green" Width="400" Height="300">
            <Button.Style>
                <Style TargetType="{x:Type Button}">
                    <Setter Property="Background" Value="Yellow"/>
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="Red" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
            Click Me 
        </Button>
  </Grid>
</Page>
           

    代码在三个地方设置了按钮的颜色;按钮最终为绿色;