第二章 开始认识XAML (2)

依赖项属性系统

依赖项属性是Silverlight中非常重要的部分。它为多个离散的源,如动画和数据绑定提供了获得访问对象属性的方式。Silverlight包含了一些类来直接关联构造用户界面。您可以通过图2-3来查看位于层次结构顶层的类。请注意,位于层次结构顶部的是DependencyObject。尽管它只有很少的公开方法,但该顶层对象提供了许多基础功能来支持依赖项属性系统。让我们仔细研究一下依赖项属性是什么,我们重点关注DependencyObject对象的哪些方面内容。

image

图2-3.顶层部分对象层次结构关联到可视化元素

依赖项属性

依赖项属性是一种支持.NET属性的特殊属性类型。依赖项属性的重点实际上就是依赖于多个源的值(这也就是为什么称作依赖项属性的原因),因此,标准的.NET属性系统是办不到的。依赖项属性的值可能来源于数据绑定、动画、特定于XAML中的资源模板、样式或者本地值。图2-4显示了这些源的优先级。

image

图2-4.资源依赖项属性值的优先级

动画具有最高的优先级。属性值的改变取决于动画对象值的改变。本地值是那些通过设置元素的attribute或property的值。本地值也可以通过数据绑定或者静态资源来进行设置,实际上这些就是本地值-因此,他们位于中间优先级。位于最底层级的是数据模板或者控件模板,如果本地值没有重写他们才会生效。紧接着是在页面或应用程序中被定义的样式,如果没有设置任何值,那么依赖项属性将取默认值。


注意 属性的基础值和默认值是不同的。属性的基础值取决于如上所示图表中应用的来源类型并且会在到达动画对象前终止。属性的默认值表示在没有明确给定值时提供的值(例如布局容器的构造器就有可能建立在默认的大小属性上)。

 

现在我们来研究一下依赖项属性的实际例子,其实大家已经接触过一些依赖项属性。FrameworkElement类定义的Width属性,这个属性是最先被定义的一个依赖项属性并且由.NET属性进行包装。在传统的获取或设置属性值时依赖项属性提供了所有需要的功能。让我们看看依赖项属性是如何定义的:

public static readonly DependencyProperty WidthProperty;

按照惯例,依赖项属性总是以单词Property结尾,在Silverlight中始终如此。请注意,这里我们定义该依赖项属性为public,这也是惯例的一部分,没有什么理由不把他设置为公开的属性。依赖项属性就应该像.NET属性包装器一样将属性定义为公开的属性。.NET属性系统提供了一种快捷方式,隐藏底层的依赖项属性的实现,依赖项属性通过GetValue和SetValue进行了封装。

public double Width
{
   get {
      return (double) this.GetValue(WidthProperty);
   }
   set {
      base.SetValue(WidthProperty, value);
   }
}

只是简单的定义依赖项属性是不够的-还必须使用DependencyProperty将它注册到依赖项属性系统中去。注册静态方法。Register方法需要提供以下参数:

public static DependencyProperty Register(
    string name,
    Type propertyType,
    Type ownerType,
    PropertyMetadata typeMetadata

您现在不必提供所有的参数,让我们现在在MainPage.xaml.cs文件中创建一个名为TextSize的依赖项属性吧。在类中添加以下代码:

public static readonly DependencyProperty TextSizeProperty =
    DependencyProperty.Register("TextSize",
                                typeof(double),
                                typeof(MainPage),
                                new PropertyMetadata(new
                                PropertyChangedCallback(onTextSizeChanged)));
     public double TextSize
     {
         get { return ((double)this.GetValue(TextSizeProperty)); }
         set { this.SetValue(TextSizeProperty, value); }
     }

依赖项属性的名称()不需要属性依赖于它-此惯例仅保持在类中实际的字段名。现在您拥有了一个新的依赖项属性来用于数据绑定或者任何用于修改依赖项属性值的其他数据来源。

依赖项属性还有另外一种用途:属性变更通知。此能力可以用于捕获属性值的改变。这个功能非常适用于某些场景,如进度条,进度条有最大值和最小值,值会越来越大或越来越小。Register方法的最后一个参数

指定了一个属性变更通知句柄,这里的处理程序用来限制TextSizeProperty属性值不能大于36:

private static void onTextSizeChanged(DependencyObject source,
                                              DependencyPropertyChangedEventArgs e)
{
      if (((double)source.GetValue(e.Property)) > 36)
      {
           source.SetValue(e.Property, 36.0);
       }
}


注意:属性值变更的回调方法是进行数据验证和数据约束的最佳方法。它也是最佳的用于保持修改依赖项属性逻辑的方法,因此,当一个值改变时,它将影响其他DependencyObject包含属性的依赖项属性值

DependencyObject实例的第一个参数-这个参数用于获取或设置属性值。如果您试图将TextSize属性的值设置为大于36并且显示出值,那么您将发现值不能大于36.

附加属性(Attached Properties)

附加属性是依赖项属性的一种特殊类型。附加属性提供了给对象分配属性值然而对象实际上并没有该属性-附加属性值主要用于元素层次结构中的父对象。其实您已经见过许多的附加属性。让我们再来看看登录界面中标题文本的XAML代码:

<TextBlock HorizontalAlignment="Center"
                   Text="Please enter your information"
                   Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2"/>

Grid类定义了许多附加属性,包括Column、Row和ColumnSpan这些用于TextBlock对象的属性。如果您在MSDN中查找一下TextBlock对象,您根本就找不到关于Grid.Row或者Grid.Column的属性。这是因为Column、Row和ColumnSpan在Grid类中被定义为附加属性。Grid类总共定义了四个附加属性:Column、Row、ColumnSpan和RowSpan。点式语法主要指定了那些提供了依赖项属性的类。通过使用这种语法,可以给对象附加任意属性而对象本身并没有这些属性。Grid布局控件的附加属性为子元素指定了他们应该定位到Grid中什么位置。您可以在MSDN中查找"Attached Properties"。如果您打算试图使用任意的依赖项属性作为附加属性,分析器将会抛出一个异常。注册一个附加属性的方式跟注册普通的依赖项属性的方式差不多,不过要使用RegisterAttached代替Register。

依赖项属性是Silverlight中的重要内容并且会经常使用,也将贯穿整本书的其余部分。

可视化元素的根元素:DependencyObject

任何继承自DependencyObject的类,直接地或间接地获得了依赖项属性的相关功能。您已经了解过GetValue和SetValue方法了,这两个方法可能是DependencyObject最重要的两个方法。此跟对象也提供了获取属性值的能力(它的基本值),在没有动画发生的情况下。

类型转换

介绍XAML的类型转换是为了更容易地解决设置结构复杂的属性值。类型转化器简化了对象的字符串呈现,但是可以接受复杂的处理,如在一些对象中的值包装。如果不是与Silverlight(或者WPF、XAML)紧密联系,当分析XAML时类型转换便很难使用。让我们来看看登录界面中的Canvas布局控件的XAML定义:

<Canvas Background="White">

Background属性就是一个从字符串到实际类型的类型转换例子。如果您通过C#来创建Canvas,则应是以下的样子:

Canvas canvas = new Canvas { Background = new
      SolidColorBrush(Color.FromArgb(255, 255, 255, 255))};

如果您想一想,您会认为Background属性被Color类型所支持;尽管如此,它实际上是由Brush对象所支持。使用Brush对象为Background提供了简单地方式显示纯色、梯度色彩和其他样式的背景,这样可以创建出更加灵活多样的背景效果。我们将在第七章详细讨论画刷的内容。指定Canvas控件的背景为XAML

属性是一种最快速的设置背景样式的方式并且是大家所知的property attribute语法。XAML也支持property element语法,使用Brush来显式地设置Background属性。

<Canvas>
    <Canvas.Background>
        <SolidColorBrush Color="White"/>
    </Canvas.Background>
</Canvas>

当属性作为元素呈现是,必须依照对象名.属性的语法进行书写,如Canvas.Background。

处于许多原因,Content也提供了通过属性或在元素公开的标签里面,每个步骤都展现在用户名、密码文本标签里面。用户名标签使用Content属性文件:

<TextBlock Text="Username:" VerticalAlignment="Top"
               HorizontalAlignment="Right"
               Grid.Column="0" Grid.Row="1"/>

 

密码标签指定为TextBlock元素的子元素:

<TextBlock HorizontalAlignment="Right" VerticalAlignment="Top"
            Grid.Column="0" Grid.Row="2">
            Password:
         </TextBlock>

内容属性语法,非常像property attribute语法,始终非常有用的速写法,两种方式在标记或者后台代码中都能够良好的工作。需要注意的是内容属性可以指定什么元素严格地取决于您使用什么控件,例如,TextBox的内容属性不能指定为Button控件。

posted @ 2009-11-03 23:37  王一平  阅读(406)  评论(4编辑  收藏  举报