XAML和其依赖属性

一、XAML

在WPF之前设计图形用户界面往往使用一种语言。但在WPF当中,由于 引入了XAML语言。因此在界面设计方面,一般使用XAML语言,而在业务逻辑上使用C#或者VB这样的后台代码。XAML语言和后台代码可以配合得丝丝入扣,又可以将界面设计和业务逻辑分离。XAML是一种声明式的语言。XAML文件有两个重要组成部分:一是有完整开始和结束标签的要素,如Window、DockPanel和Button等,称为“元素”(Element);二是依附于元素的要素,如Width、Height和Background等,称为“属性”(Attribute)。在XAML中也需要引入命名空间,如Window元素中的xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"就是声明的命名空间,该命名空间貌似一个网站,但它仅仅只是一个命名空间的标示而已。XAML文件规定必须至少指定一个XML的命名空间。为了使该命名空间作用于整个文件,通常会将其放在根元素中。在WPF中,只有四种元素可以作为根元素:Window(代表一个窗口)、Page(类似一个网站的页面)、Application(代表一个应用程序)、ResourceDictionary(代表一个逻辑资源的集合)。

如果XAML文件要使用其他.NET对象或者应用程序或者其它程序集中的自定义对象,也要声明命名空间。如下代码,假如我们希望把Button的Content属性设置为String类型(尽管这样画蛇添足),由于String类型属于程序集mscorlib.dll,且其命名空间为System,所以在声明后才能使用。

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:s="clr-namespace:System;assembly=mscorlib"
                Width="300" Height="200">
            <Button.Content>
                <s:String>
                    Hello XAML
                </s:String>
            </Button.Content>
        </Button>
有时我们又需要在XAML文件中使用自定义类,比如,有下面一个Book类:
namespace WpfApplication1
{
    public class Book
    {
        public Book() { }

        public string Name { get; set; }

        public double Price { get; set; }

    }
}
那么有如下两种情况:

1.如果要引用的Book类是本地应用程序的自定义类,那么需要在XAML文件中作如下声明:

xmlns:local="clr-namespace:WpfApplication1"
然后在使用这个类的时候,使用如下方法:
<local:Book Name="葵花宝典" Price="0.1"/>
2.假如要引用的Book类是外部程序集(customlib)的自定义类,那么我们在声明的时候需要设置assembly。如下所示:
xmlns:customlib="clr-namespace:customlib:assembly=customlib"

二、XAML的标记扩展

在XAML中,有以下几种情况让XAML难以处理。

1.将一个属性赋值为null。

2.将一个属性赋值给一个静态变量,如将按钮的Background属性赋值为一个预定义画刷。

以上几种情况在C#代码中不难完成,但在XAML文件中就需要使用标记扩展了。标记扩展是通过XAML的显式的、一致的语法调用实现的,也可以通过自定义来实现XAML的主义扩展。在XAML中,只要属性值被一对花括号{}括起,XAML解析器就会认为这是一个标记扩展,而不是一个普通的字符串。这两种情况都可以用标记扩展的方法来解决,将一个按钮的Background属性赋值为空,表示这个按钮的背景颜色为透明色。使用{x:Null}的方式表示空值,代码如下:

<Button Name="btn" Content="MyButton" Click="btn_Click" Background="{x:Null}">
使用{x:Static}的方式,可以引用一个静态变量,代码如下:
<Button Name="btn" Content="MyButton" Click="btn_Click">
    <Button.Background>
        <x:Static Member="SystemColors.ActiveCaptionBrush"/>
    </Button.Background>
</Button>
如果我们想显示的字符串里面碰巧有一对花括号({}),那么XAML解析器会固执地认为这是一个标记扩展,由于又无法找到那样的标记扩展,所以编译无法通过。解决这个问题的方法是在该字符串的前面添加一个空的花括号,代码如下:
<TextBlock Text="{}{HelloXAML}"/>


三、依赖属性

依赖属性与属性有些不同,它是WPF引入的一个新的属性类型。如下代码所示为Button中的一个IsDefault属性,它是一个依赖属性。

public class Button : ButtonBase
    {
        //依赖属性
        public static readonly DependencyProperty IsDefaultProperty;
        static Button()
        {
            //注册属性
            Button.IsDefaultProperty = DependencyProperty.Register("IsDefault", typeof(bool),
                typeof(Button), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsDefaultChanged)));
        }
        //.Net属性包装器
        public bool IsDefault
        {
            get { return (bool)GetValue(Button.IsDefaultProperty); }
            set { SetValue(Button.IsDefaultProperty,value); }
        }
        
        private static void OnIsDefaultChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { }
    }
从代码中可以看出,类型为DependencyProperty的IsDefaultProperty是依赖属性,不过用户使用的是暴露后的IsDefault普通的.Net属性,从中可以看出依赖属性是WPF在原来的属性基础上提供给用户功能更加丰富的一种类型的属性。依赖属性是一种类型为DependencyProperty的属性,其依赖性标识(Dependency property identifier)则是依赖属性的实例。与依赖属性相关的术语如下:

1.DependencyObject:WPF中的一种类型,继承该类后才可以注册和拥有依赖属性。

2.WPF属性系统:WPF通过提供一系列的服务扩展了普通的.Net属性,这些服务总称为“WPF属性系统”。

3..Net属性包装器:指属性的get和set的实现。.Net的属性包装器可以把依赖属性包装成普通的属性暴露给用户使用,在这个实现均调用DependencyObject的GetValue和SetValue方法。

依赖属性不像简单的面向对象语言里的属性,它更像一个计算过程,根据所输入的值经过一些计算最终得到另外一个值。整个计算过程依赖其他属性与内存和外在的多种因素,“依赖”正是由此而来。

WPF 中相当多的元素的属性都是依赖属性,以Button为例,宽度(Width)、背景色(Background)和字体大小(FontSize)等都是依赖属性。那么WPF为什么要引入依赖属性呢?在MSDN里面的解释是,WPF的主要设计思想之一是侧重属性胜于方法和事件,即如果属性能解决问题,则坚决不使用方法和事件。在过去不用方法和事件单单用属性是很难想象的。因为属性的功能太单一,仅仅就是提供一个类型的值。因此WPF需要提供一个新的属性类型即依赖属性和与之配套的服务,让它能够做方法和事件所能做的事情。具体来说,依赖属性与以前的属性相比,提供了对资源引用、样式、动画、数据绑定、属性值继承、元数据重载及WPF设计器的集成支持功能的支持,这些功能都是WPF的重要特性。

如下图演示了依赖属性对一些功能的支持。图中“金色按钮”的背景属性一个画刷资源;“绿色按钮”通过样式使背景变成绿色;“动画”按钮通过动画使按钮的背景由红变绿,再由绿变红反复循环;“我被绑定成红色”按钮是通过一个.Net对象的属性绑定变成红色。最下面一行是属性继承支持。从左至右依次有三个按钮,单击“设置窗口字体:16”按钮,则所有标签和按钮的字体大小都会变成16;单击“设置按钮字体:8”按钮,则该按钮字体大小变成8;单击最后一个“重置字体:12”按钮,所胡按钮字体大小恢复为初始状态大小。

主要实现代码如下:

<Window x:Class="DependencyButton.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DependencyButton"
        Title="MainWindow" Height="269" Width="572">
    <Grid Name="grid1">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <!--资源支持-->
        <!--也设置这个按钮的Style属性,结果颜色没什么变化, 仍然是金黄色-->
        <Label HorizontalAlignment="Center" VerticalAlignment="Center">资源支持</Label>
        <Button Grid.Row="0" Grid.Column="1" Name="btnResource" Margin="5"
                Background="{DynamicResource ResourceKey=MyBrush}"                 
                Style="{StaticResource ResourceKey=GreenButtonStyle}">金色按钮</Button>
        
        <!--样式支持-->
        <Label Grid.Row="0" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center">样式样式</Label>
        <Button Grid.Row="0" Grid.Column="3" Name="btnStyle" Margin="5"
                Style="{StaticResource ResourceKey=GreenButtonStyle}">绿色按钮</Button>
        
        <!--动画支持-->
        <Label Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center">动画支持</Label>
        <Button Grid.Row="1" Grid.Column="1" Name="btnAnimation" Margin="5">
            <Button.Background>
                <SolidColorBrush x:Name="AnimationBrush"/>
            </Button.Background>
            <Button.Triggers>
                <EventTrigger RoutedEvent="Button.Loaded">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetName="AnimationBrush" 
                                            Storyboard.TargetProperty="(SolidColorBrush.Color)"
                                            From="Red" To="Green" Duration="0:0:5" 
                                            AutoReverse="True" RepeatBehavior="Forever"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Button.Triggers>
            动画按钮</Button>
        
        <!--数据绑定支持-->
        <Label Grid.Row="1" Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Center">数据绑定支持</Label>
        <Button Grid.Row="1" Grid.Column="3" Name="btnBinding" Margin="5"
                Background="{Binding Source={StaticResource ResourceKey=myDataSource},Path=ColorName}">我被绑定成红色</Button>
        <!--属性继承支持-->
        <Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center">属性继承支持</Label>
        <Button Grid.Row="2" Grid.Column="1" Name="btnFontSizeWindow" Margin="5"
                Click="btnFontSizeWindow_Click">设置窗口字体:16</Button>
        <Button Grid.Row="2" Grid.Column="2" Name="btnFontSizeButton" Margin="5"
                Click="btnFontSizeButton_Click">设置按钮字体:8</Button>
        <Button Grid.Row="2" Grid.Column="3" Name="btnResetFontSize" Margin="5"
                Click="btnResetFontSize_Click">重置字体:12</Button>
    </Grid>
</Window>
在App.xaml文件中添加的资源如下:
<Application x:Class="DependencyButton.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:DependencyButton"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <local:BindingData x:Key="myDataSource"/>   <!--数据绑定对象-->
        <SolidColorBrush x:Key="MyBrush" Color="Gold"/>    <!--资源-->
        <Style x:Key="GreenButtonStyle">            <!--样式-->
            <Setter Property="Control.Background" Value="Green"/>
        </Style>
    </Application.Resources>
</Application>
数据绑定对象的的代码是:
public class BindingData
    {
        private string name = "Red";
        public string ColorName
        {
            get { return name; }
            set { name = value; }
        }

        public BindingData()
        {
            ColorName = "Red";
        }

    }

在引用资源支持(金色)的按钮上,也同样也引用了样式支持(绿色),但最终结果却是显示为金色。这就是控件样式设置优先级的问题。在依赖属性的优先级中,本地直接设置的值具有最高优先级,甚至能够屏蔽触发器设置属性的行为。但如果有动画在运行,则可以改变这个依赖属性的值。之后依次是模板的父类、样式触发器、模板触发器、样式设置、主题样式、属性值继承、元数据的默认值。而对于数据绑定的支持,我们也需要在Application.Resources里面对资源进行引用,然后再把这个引用绑定到控件上。当然我们需要先声明之后再进行引用。依赖属性对WPF设计器的集成支持主要体现在自定义控件,并且为其添加一个依赖属性这个方面。


四、附加属性

在依赖属性里面,还有个神秘的东西,那就是附加属性(AttachedProperty)。在WPF里,最典型的附加属性就是各种布局里面的属性,比如Grid里面的的Row、Column或者是DockPanel里的Dock属性等,这些附加属性让每个元素去设置其属性值,在由Grid或DockPanel按照属性值把元素安排在合适的位置。如果没有附加属性要处理的布局问题,以DockPanel为例,则我们要为每个元素添加一个Dock属性,然后再设置其值,这很繁琐。

附加属性实质上是一个依赖属性,与普通的依赖属性相比有以下不同:

1.注册不再是通过Register方法注册,而是通过RegisterAttached方法注册;

2.没有普通的.Net属性包装器,而是通过Get和Set属性名来实现属性包装;

3.没有普通的.Net属性。

如下代码是一个附加属性IsBubbleSource的示例:

public static readonly DependencyProperty IsBubbleSourceProperty =
            DependencyProperty.RegisterAttached("IsBubbleSource",
                typeof(Boolean),
                typeof(AquariumObject),
                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
        public static void SetIsBubbleSource(UIElement element, Boolean value)
        {
            element.SetValue(IsBubbleSourceProperty, value);
        }
        public static Boolean GetIsBubbleSource(UIElement element)
        {
            return (Boolean)element.GetValue(IsBubbleSourceProperty);
        }




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值