[笔记/翻译]WPF的新特性——依赖属性(5)

来源:互联网 发布:虚幻4和unity3d区别 编辑:程序博客网 时间:2024/05/22 03:53

附加属性

 

       附加属性一种特殊的依赖属性形式,它可以被附加到任意对象上。

 

       对于之前的About Dialog示例,如果我们不想让整个Window元素及其子元素都被FontSizeFontStyle影响,而是希望改变它们仅影响位于第二个StackPanel中的OKHelp两个按钮。我们很自然地会想到把它们从Window元素中移动到StackPanel元素中,但这样做是不可以的,因为StackPanel本身并不包含任何与字体相关的属性。此时,我们可以使用定义在TextElement类中的附加属性FontSizeFontStyle来完成相同的任务:

 

<Window x:Class="Test.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="WPF揭秘" SizeToContent="WidthAndHeight"

    Background="OrangeRed">

    <StackPanel>

        <Label FontWeight="Bold" FontSize="20" Foreground="White">

            WPF揭秘(版本3.0

        </Label>

        <Label>(C)2006 SAMS 出版集团</Label>

        <Label>已安装的章节:</Label>

        <ListBox>

            <ListBoxItem>第一章</ListBoxItem>

            <ListBoxItem>第二章</ListBoxItem>

        </ListBox>

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"

                    TextElement.FontSize="30" TextElement.FontStyle="Italic">

            <Button MinWidth="75" Margin="10">Help</Button>

            <Button MinWidth="75" Margin="10">OK</Button>

        </StackPanel>

        <StatusBar>

            <Button>您已经注册了本产品。</Button>

        </StatusBar>

    </StackPanel>

</Window>

 

       TextElement.FontSizeTextElement.FontStyle必须用在StackPanel元素上,因为它不包含这两个属性。当XAML解析器/编译器遇到这个语法时,需要TextElement(有时被称作“附加属性提供器”)提供静态的SetFontSizeSetFontStyle方法。因此,与上例中第二个StackPanel等价的C#代码为:

 

StackPanel panel = new StackPanel();

TextElement.SetFontSize(panel, 30);

TextElement.SetFontStyle(panel, FontStyles.Italic);

panel.Orientation = Orientation.Horizontal;

panel.HorizontalAlignment = HorizontalAlignment.Center;

Button helpButton = new Button();

helpButton.MinWidth = 75;

helpButton.Margin = new Thickness(10);

helpButton.Content = "Help";

Button okButton = new Button();

okButton.MinWidth = 75;

okButton.Margin = new Thickness(10);

okButton.Content = "OK";

panel.Children.Add(helpButton);

panel.Children.Add(okButton);

 

       枚举值如FontStyles.ItalicOrientation.HorizontalHorizontalAlignment.Center可以在XAML中被分别简单表示为ItalicHorizontalCenter,这要感谢.NET中的EnumConverter类型转换器,它可以将任何大小写不敏感的字符串转换为枚举值。

 

       在内部,SetFontSize方法调用了DependencyObject.SetValue方法,后者一般由依赖属性的访问器(accessor(就是之前说过的按钮的IsDefaultProperty的访问器IsDefault调用,但此时却是由当前传入的DependencyObject的实例进行调用:

 

public static void SetFontSize(DependencyObject element, double value)

{

element.SetValue(TextElement.FontSizeProperty, value);

}

 

       相似地,附加属性也定义了调用DependencyObject.GetValueGetXXX方法:

 

public static double GetFontSize(DependencyObject element)

{

return (double)element.GetValue(TextElement.FontSizeProperty);

}

 

       附加属性被广泛使用在WPF的布局系统中,相关内容放到以后介绍。

 

       Button等控件继承的FontSizeFontStyle附加属性并不是由它们自己定义的,而是定义在其基类Control中,这十分令人迷惑。实际上,它们真正被定义在看起来与控件并不相关的TextElement类当中。在TextElement类中,包含下面的注册代码:

 

TextElement.FontSizePropert = DependencyProperty.RegisterAttached(

"FontSize", typeof(double), typeof(TextElement),

new FrameworPropertyMetadata(

SystemFonts.MessageFontSize,

FrameworkPropertyMetadataOptions.Inherits |

FrameworkPropertyMetadataOptions.AffectsRender,

FrameworkPropertyMetadataOptions.AffectsMeasure),

new ValidateValueCallback(TextElement.IsValidFontSize));

 

       与前面讲到的Button.IsDefaultPropertyRegister类似,只不过RegisterAttached为附加属性优化了元数据的处理过程。

 

       控件并不注册它们,而是通过调用AddOwner来添加一个已注册了的依赖属性:

 

Control.FontSizeProperty = TextElement.FontSizeProperty.AddOwner(

typeof(Control), new FrameworkPropertyMetadata(SystemFonts.MessageFontSize,

FrameworkPropertyMetadata.Inherits));

 

因此,由控件继承所公开的FontSizeFontStyle以及其它字体相关的依赖属性都与TextElement公开的属性完全一样。所幸在多数类中,公开了附加属性的类(比如包含GetXXXSetXXX)都与定义了一般依赖属性的类相同,这使得许多混乱得以避免。

 

       Windows Form类似,WPF中的许多类也定义了一个System.Object类型的Tag属性,用以存储任意与当前实例相关的数据。与Tag属性相比,附加属性是为派生自DependencyObject的对象附加自定义数据的一种更加强大、更灵活的机制。附加属性允许为密封类(sealed)的实例添加自定义数据,WPF中有许多这样的做法,常常被忽略。

       尽管在XAML中设置附加属性时需要依靠静态的SetXXX方法,但是我们在程序代码中可以直接通过DependencyObject.SetValue方法完成。这意味着我们可以在程序代码中将任何依赖属性作为附加属性。例如,下例将ListBoxIsTextSearchEnabled属性附加给了Button,并为其设置了一个值:

 

okButton.SetValue(ListBox.IsTextSearchEnabledProperty, true);

 

       尽管这么做没什么意义,而且它确实对Button不起作用,但是我们仍旧可以一种对程序和组件有意义的方式来自由地使用它。

 

       下例展示了一种有趣的元素扩展方式:FrameworkElementTag属性是一个依赖属性,因此我们可以把它附加到GeometryModel3D的实力上。

 

GeometryModel3D model = new GeometryModel3D();

model.SetValue(FrameworkElement.TagProperty, "自定义数据");

      

       这只不过是一种WPF提供灵活性的方式,这种方式不需要继承就可以获得新的属性。 
原创粉丝点击