XAML语言
以下内容转载于
————————————————————————————命名空间————————————————————————————————
2.3 XAML命名空间(NameSpace)
长期以来,各大公司或个人开发了大量面向对象的软件。在C#或C++里,为了区分模块间的同名类,我们引入命名空间。本书采用的命名空间规则是笔者本人的名字加上章节名。比如第3章中的例子,都放在Yingbao.Chapter3这个命名空间中;而第4章中的例子,都放在Yingbao.Chapter4这个命名空间中;这样,即使有两个在第3章和第4章同名的类,比如TreeView。那么可以用Yingbao.Chapter3.TreeView和Yingbao.Chapter4.TreeView来区分用的究竟是哪个类。
同样的道理,在XAML中也使用命名空间这个概念。 XML的命名空间,则用xmlns来表示,例如前面的例子:
<Window x:Class="Yingbao.Chapter2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="
送元二使安西" Height="300" Width="300" >
在这个例子中,使用了两个命名空间,一个是http://schemas.microsoft.com/winfx/2006/xaml/presentation;另一个是http://schemas. microsoft.com/winfx/2006/xaml。注意,XAML中的命名空间和.NET的命名空间密切相关,但XAML的命名空间和.NET的命名空间之间并不是一一对应的,而是一对多的关系,即一个XAML命名空间对应多个.NET的命名空间。这样做的好处是,不必在XAML中书写过多的命名空间。一般来说在XAML里使用这两个命名空间就包括了WPF中所有的命名空间。
这种让一个XAML命名空间对应多个.NET命名空间的做法不仅微软可以用,任何软件开发人员也都可以使用。比如我可以把Yingbao.Chapter2和Yingbao.Chapter3两个命名空间合并为一个XAML命名空间,方法是在项目的AssemblyInfo.cs文件中使用XmlnsDefinition属性:
[assembly: XmlnsDefinition("http://Yingbao.Com/WPFExample",
"Yingbao.Chapter3")]
[assembly: XmlnsDefinition("http://Yingbao.Com/WPFExample",
"Yingbao.Chapter4")]
这里我把Yingbao.Chapter3和Yingbao.Chapter4两个.NET命名空间合并到http://Yingbao.Com/
WPFExample 这一个命名空间中。注意:这里的http://Yingbao.Com 并不是真的有一个这样的网址。
WPF使用这一技术,把所有WPF类命名空间映射到一个XAML命名空间:
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
注意xmlns后面没有冒号,这表示WPF在XAML中默认命名空间。另一个WPF中常用的命名空间是:xmlns:x=http://schemas.microsoft.com/ winfx/2006/xaml,它包含了XAML的某些特定功能,比如控制XAML编译器等。
如果要在XAML中使用非WPF命名空间中的类,那么你就要在XAML中引入相应的命名空间,例如,我们要在Window中使用ADO.NET中的类,我们可以直接在XMAL中引入System.Data命名空间:
<Window x:Class="Yingbao.Chapter2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml
/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ado="clr-namespace:System.Data;assembly= System.Data,
Version=2.0.0.0,Culture=Meutral,PublicKeyToken=b77a5c561934e089"
Title="送元二使安西" Height="300" Width="300" >
可以使用同样的方法在XAML中引用笔者自己开发的类,例如在本书第11章中的例子:
<Window x:Class="Yingbao.Chapter11.BookBanding.BookWindow"
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:Yingbao.Chapter11.BookBanding"
Title=".NET对象绑定到界面元素" Height="200" Width="340" >
这里xmlns:src就是在XAML中引入Yingbao.Chapter11.BookBanding命名空间。
—————————————————————————————相关属性———————————————————————————
2.6 相关属性(Dependency Property)
XAML中元素的属性大部分为相关属性,有关相关属性的概念,将在第4章进行详细介绍。这里,先讲述XAML中相关属性的表示方法。一种表示方法是:
<DockPanel Background="White">
…
</ DockPanel>
这里的Background就是DockPanel中的相关属性,也可以用第二种表示方法:
<DockPanel>
<DockPanel.Background>White </ DockPanel.Background >
…
DockPanel>
笔者比较喜欢第一种,因为它比较简洁。但有时必须要用第二种。比如,若要用梯度画刷来绘制DockPanel的背景:
<DockPanel>
<DockPanel.Background>
<LinearGradientBrush StartPoint ="0,0" EndPoint ="0,1">
<GradientStop Color="Red" Offset="0.0" />
<GradientStop Color="Orange" Offset="0.17" />
<GradientStop Color="Yellow " Offset="0.33" />
<GradientStop Color="Green" Offset="0.5" />
<GradientStop Color="Blue" Offset="0.67" />
<GradientStop Color="Indigo" Offset="0.84" />
<GradientStop Color="Violet" Offset="1" />
</LinearGradientBrush>
</DockPanel.Background>
</DockPanel>
这时,必须使用第二种表示方法。有关梯度画刷,将在第5章详细讨论。
————————————————————————————附加属性————————————————————————————
2.7 附加属性(Attached Property)
WPF属性系统引入了附加属性的概念,有关附加属性将在第4章详细讨论,这里介绍附加属性在XAML中的表示。并列举一个使用网格(Grid)排版的例子:
<Grid Name="MyGrid" Background="Wheat"
ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Name ="InputText" Grid.Column ="0" Grid.Row ="0"
Grid.ColumnSpan ="9" FontSize ="12" FontWeight =
"DemiBold" Margin ="5,2,10,3"/>
<Button Name="B7" Click="DigitBtn_Click" Grid.Column="1"
Grid.Row="2" Margin ="2">7</Button>
<Button Name="B8" Click="DigitBtn_Click" Grid.Column="1"
Grid.Row="2" Margin ="2">8</Button>
<Button Name="B9" Click="DigitBtn_Click" Grid.Column="1"
Grid.Row="2" Margin ="2">9</Button>
</Grid>
Grid中的Column和Row都是附加属性。Grid中的控件在说明其位置时,直接设置该附加属性。写法如上面的XAML,TextBox和Button并不含有Grid.Row或Grid.Column属性,却可以很方便地应用Grid中的附加属性。
——————————————————————————————标记扩展————————————————————————————————
2.8 XAML标记扩展
有时候我们要在XAML里引用静态或动态对象实例,或在XAML中创建带有参数的类。这时,我们需要用到XAML扩展。XAML扩展常用来设定属性值。
标记扩展本身是一系列类,其基类为MarkupExtension,这是一个抽象类。从这个类中派生出十二个类,即:ResourseKey、TypeExtension、StaticExten-sion、BindingBase、RelativeSourse、ColorConvertedBitmapExtension、Dyna-micResourceExtension、ArrayExtension、NullExtension、StaticResource-Extension、Template-BindingExtension和ThemeDictionaryExtension。其中ResourceKey、BindingBase等类又派生出其他的一些类。这些标记扩张可分为两大类:
WPF标记扩展。这类扩展包括:StaticResource、DynamicResource、Binding、RelativeSource和TemplateBinding。
XAML本身定义的标记扩展。这类扩展包括:x:Type、x:Static、x:null和x:Array。
在语法上,XAML使用大括号{}来表示扩展。例如,下面这句XAML:
<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path =PersonName}"/>
这里有两处使用了XAML扩展,一个是Binding,另一个是StaticResource,这种用法又称为嵌套扩展,TextBlock元素的Text属性的值为{}中的结果。当XAML编译器看到大括号{}时,把大括号中的内容解释为XAML标记扩展。
必要时,你也可以使用自己的扩展, 其方法是从MarkupExtension中派生出你自己的标记扩展类,并覆盖基类中的ProvideValue方法。
2.8.1 静态资源扩展(StaticResourceExtension)
前面已经用了StaticResource标记扩展,这个扩展用来获取静态属性值,这是XAML编译器在编译时完成的。为了让读者感受一下这种用法,可以看一个完整的例子:
"Yingbao.Chapter2.SimpleBindingEx.AppWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Static Markup Extension" Height="100" Width="200">
"personName">李白
"200" Height="50" Text="{Binding
Source={StaticResource personName}}"/>
C#代码是Visual Studio产生的:
namespace Yingbao.Chapter2.SimpleBindingEx
{
public partial class AppWin : Window
{
public AppWin()
{
InitializeComponent();
}
}
}
笔者在窗口的资源部分定义了一个简单的字符串,注意字符串位于.NET平台的System命名空间中;因此,在window标记中加了xmlns:sys。这段XAML的运行结果如图2-1所示。
2.8.2 动态资源扩展(DynamicResourceExtension)
动态资源扩展和静态资源扩展相对,其区别是获取资源的值一个是在编译时完成的(静态),另一个是在运行时完成的。当使用动态资源扩展时,若该资源属性在运行时发生了变化,那么其获取的值也会发生相应的变化。让我们来看一个例子:
<Window x:Class="Yingbao.Chapter2.DynamicResourceEx.AppWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="动态资源扩展" Height="100" Width="300">
<StackPanel>
<TextBlock Name="exText" Background="
{DynamicResource {x:Static
SystemColors.ActiveCaptionBrushKey}}" Height="30"
FontSize="24">清泉石上流
</TextBlock>
</StackPanel>
</Window>
在这个例子中,笔者利用动态资源扩展把TextBlock的背景色设置为具有输入焦点的视窗标题的背景色。上述XAML的运行结果如图2-2所示。
接下来,笔者在运行这段程序的同时,来改变Vista的"Appearance Settings"。方法是在"Control Panel"中,选择"Appearance and Personalization / Custom Color/Open classic appearance properties for more color options",设置Vista窗口属性的方法如图2-3所示。
把Color Scheme设置为"High Contrast #2"时的结果如图2-4所示。
2.8.3 数据绑定扩展(Binding)
Binding扩展专门在XAML中用作数据绑定,将在第11章中详细讨论WPF中的数据绑定。现在来看一个简单绑定的XAML语法:
<Window x:Class="Yingbao.Chapter2.SimpleUIBanding.AppWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="绑定" Height="373" Width="337">
<StackPanel>
<ScrollBar Name="scroll" Orientation ="Horizontal " Margin
="24" Maximum ="100" LargeChange ="10" SmallChange ="1"/>
<Label Height ="30">OneWay:</Label>
<TextBox Name="scrollValue1" Height ="20" Width ="200"
HorizontalAlignment="Center" Text ="{Binding
ElementName=scroll, Path=Value, Mode=OneWay}"/>
使用Binding扩展,设定了Mode、Path,等等属性。
2.8.4 相对数据源扩展(RelativeSource)
在数据绑定时,有时候需要把目标对象绑定到和目标对象自身有相关关系的对象上,这时就需要用到相对数据源扩展。例如:
<Window x:Class="Yingbao.Chapter2.RelativeEx.AppWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="相对绑定" Height="100" Width="300">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock FontSize="20" Text="{Binding
RelativeSource={RelativeSource self}, Path=FontSize}"/>
<TextBlock Margin="10,1,1,5" FontSize="20" Text="{Binding
RelativeSource={RelativeSource AncestorType={x:Type
StackPanel}}, Path=Orientation}"/>
</StackPanel>
</Window>
在这个例子中,笔者使用了两个相对数据源扩展,第一个TextBlock的Text绑定到自身的字体大小上;第二个TextBlock的Text则绑定到其父节点StackPanel的Orientation属性上。这段XAML的运行结果如图2-5所示。
2.8.5 模板绑定(TemplateBinding)
WPF提供强大的模板功能,本书第10章,将详细介绍WPF模板。模板绑定扩展是用来把原对象中的属性和模板对象中的属性联系起来。请看下面的例子:
<Window x:Class="Yingbao.Chapter2.TemplateBindingEx.AppWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="模板绑定扩展" Height="134" Width="226">
<Window.Resources>
<ControlTemplate TargetType="{x:Type Button}"
x:Key="buttonTemp">
<Border BorderThickness="3" Background
="{TemplateBinding Foreground}">
<TextBlock Name="txtblk" FontStyle ="Italic"
Text="{TemplateBinding Content}"
Foreground="{TemplateBinding Background}"/>
</Border>
</ControlTemplate>
</Window.Resources>
<StackPanel Orientation="Vertical" Height="362" Width="394">
<Button Height="50" Foreground="Red" FontSize="24"
Template="{StaticResource buttonTemp}">按钮1</Button>
<Button Height="50" Foreground="Yellow" FontSize="24"
Template="{StaticResource buttonTemp}">按钮2</Button>
</StackPanel>
</Window>
在ControlTemplate中,笔者使用TemplateBinding扩展把TextBlock的前景色设置为控件的背景色。
2.8.6 x:Type扩展
x:Type扩展在XAML中取对象的类型, 相当于C#中的typeof操作, 这种操作发生在编译时刻,如前面的例子中的:
- <ControlTemplate TargetType="{x:Type Button}"
- x:Key="buttonTemp">
它等价于下面的C#语句:
- ControlTemplate tmplt = new ControlTemplate();
- tmplt.TargetType = typeof(Button);
- .....
注意:类型名字和命名空间有关。
2.8.7 x:Static扩展
静态扩展用来把某个对象中的属性或域的值赋给目标对象的相关属性。这个扩展总是而且只有一个参数,这个参数就是源对象的属性。例如:
- <TextBlock Name="exText" Background="
- {DynamicResource {x:Static
- SystemColors.ActiveCaptionBrushKey}}" Height="30"
- FontSize="24">清泉石上流
- </TextBlock>
这是前面见过的例子。{ x:Static SystemColors.ActiveCaptionBrushKey} 静态扩展取的ActiveCaptionBrush值。
2.8.8 x:null扩展
x:null扩展是一种最简单的扩展,其作用就是把目标属性设置为null。例如:
- <TextBlock Name="exText" Background="{x:null} />
这句XAML把TextBlock的Background属性设置为null。如果用C#来重写这句,那就是下面这段程序:
- TextBlock tb= new TextBlock();
- tb.Name = "exText";
- tb.Background = null;
在WPF中,把相关属性设置为null会打断相关属性的继承链,关于这一点,将在本书第4章详细讨论。
2.8.9 x:Array扩展
Array扩展就是在XAML里创建一个数组。使用Array扩展创建数组很容易,但在词法上和其他XAML扩展不同,它不需要使用大括号"{}",原因在于Array里面含有多个元素。一个整数数组的例子如下:
<Window x:Class="Yingbao.Chapter2.ArraryEx.AppWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="在XAML中使用数组" Height="151" Width="244">
<Window.Resources>
<x:ArrayExtension Type="{x:Type sys:Int32}"
x:Key="numArray">
<sys:Int32>10</sys:Int32>
<sys:Int32>20</sys:Int32>
<sys:Int32>30</sys:Int32>
<sys:Int32>40</sys:Int32>
</x:ArrayExtension>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{StaticResource numArray}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Width="100"
Height="30" Margin="4"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
在这段XAML中,笔者用ListBox列出了数组numArray,其运行结果如图2-6所示。