在实际项目开发中,为控件属性赋值时经常会遇到属性值在设计时处于未知状态,而只有在应用程序运行时才能获取到。这种情况下,直接赋值方法是无法满足的,可以使用XAML标记扩展(Markup Extensions)来轻松实现。XAML中常用的标记扩展包括如下四种。
(1)Binding
Binding是绑定标记扩展,用于把源对象的属性值绑定到目标对象的属性值上,同样也可以将目标对象的属性值绑定到源对象的属性值上,可通过设置Binding的Mode属性来规定是单向绑定还是双向绑定。使用Binding标记扩展为元素的属性赋值的语法格式如下所示:
<targetObject targetProperty="{Binding}" ...> ... </targetObject>
-or-
<targetObject targetProperty="{Binding [Path=]bindingSourceProperty}" ...> ... </targetObject>
其中,targetObject是目标对象;targetProperty是目标对象的属性;Path是Binding的属性,用于指定源对象的属性,通常可以省略不写;bindingSourceProperty是源对象的属性。
下面通过一个例子来演示如何使用Binding标记扩展为元素的属性赋值。在一个打开的Windows应用商店项目中新建一个空白页,并命名为BindingPage。双击打开此页面的BindingPage.xaml文件,在Grid元素中添加如下代码:
<TextBox Name="BindingUserName" Text="{Binding Path=UserName}"/>
上面代码中定义了一个TextBox控件,名称为BindingUserName,并且使用Binding标记扩展将源对象的UserName属性绑定到Text属性上,这样TextBox就是目标对象,Text是目标属性,UserName是源对象的属性。
然后在BindingPage.xaml.cs文件中定义一个User类,并添加UserName属性用于获取用户名。代码如下所示:
public class User
{
public User() { }
public User(string name)
{
UserName = name;
}
public string UserName { get; set; }
}
接下来在BindingPage构造方法中为TextBox控件的Text属性设置绑定源。相应的代码片段如下所示:
public BindingPage()
{
this.InitializeComponent();
BindingUserName.DataContext = new User("李明");
}
在上面的代码中,先实例化一个User类的对象,然后赋值给BindingUserName文本框的DataContext属性。这样,如果User对象的UserName属性值发生变化,那么前台界面中TextBox控件的Text属性值也会发生相应的更新。运行此页面,效果如图3-3所示。
图3-3 使用Binding标记扩展为元素属性设置数据源的效果
(2)StaticResource
StaticResource是静态资源标记扩展,使用该标记扩展可以引用在资源字典中定义的静态资源来为元素的属性赋值,所以这种引用资源的方式称为静态引用。使用StaticResource标记扩展为元素的属性赋值的语法格式如下所示:
<object propertyName="{StaticResource resourceKey}" ...> ... </ object>
其中resourceKey是被引用的资源的键,由资源定义时的x:Key或x:Name指定。
使用StaticResource标记扩展引用资源为元素的属性赋值时,XAML语法解析器会在所有可用的资源字典中查找所请求的资源的键,查找过程是:首先在该元素所定义的资源字典中查找所请求的资源的键;如果没有在该元素本身所定义的资源字典中找到,那么查找过程向外扩展,到达父元素及其资源字典;如果前面两步都没有找到,查找过程会进一步向外扩展,可能会到达应用程序级的资源字典。
(3)TemplateBinding
在XAML应用程序的开发中,为了让控件表现更复杂的外观,经常需要为控件定义模板来控制控件的表现形式,那么在定义模板时可以使用TemplateBinding(模板绑定)标记扩展为模板对象的属性设置数据源。在通常情况下,TemplateBinding标记扩展用在ControlTemplate(控件模板)元素定义内。
使用TemplateBinding标记扩展为元素的属性赋值的语法格式如下所示:
<templateObject templateProperty="{TemplateBinding [Property=] sourceObjectProperty}" ...> ... </templateObject>
其中,templateObject是模板对象;templateProperty是模板对象的属性;Property是TemplateBinding的属性,用于指定被绑定对象的属性,通常可省略不写;sourceObjectProperty是被绑定对象的属性。
下面通过一个例子来演示如何使用TemplateBinding标记扩展为元素的属性赋值。在一个打开的Windows应用商店项目中新建一个空白页,并命名为TemplateBindingPage。双击打开此页面的TemplateBindingPage.xaml文件,在Grid元素中添加如下代码:
<Grid.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">
<Border BorderThickness="2" Background="Gray">
<TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Grid.Resources>
<Button Height="33" Foreground="White" FontSize="20" Template="{StaticResource ButtonTemplate}" Content="按钮示例" Margin="145,360,0,375" Width="93"/>
在上面的代码中,首先定义了一个键为"ButtonTemplate"的按钮模板资源,把按钮的外观设置成边框和文本块的组合形式,并使用TemplateBinding标记扩展将Button元素的Content属性绑定到TextBlock元素的Text属性上;然后添加了一个按钮,为按钮的Content属性赋值为"按钮示例",设置Foreground属性值为White,同时使用StaticResource标记扩展引用ButtonTemplate资源赋值给按钮的Template属性,并使用鼠标将控件拖动到合适的位置。
运行此页面,可以看出,文本块中显示的内容就是按钮的Content属性值,效果如图3-4所示。
图3-4 使用TemplateBinding标记扩展为元素属性设置数据源的效果
(4)RelativeSource
RelativeSource标记扩展具有两种应用模式,分别是Self模式和TemplatedParent模式,其中TemplatedParent模式通常使用于ControlTemplate元素定义中。使用RelativeSource标记扩展为元素的属性赋值的语法格式如下所示:
<object property="{Binding RelativeSource={RelativeSource [Mode=]modeValue}}" ...> ... </object>
-或-
<object property="{Binding RelativeSource={RelativeSource [Mode=]modeValue},[Path=]bindedProperty}" ...> ... </object>
在上面语法格式中,Mode是RelativeSource的属性,用于设置绑定模式,属性值有Self和TemplatedParent。当Mode属性取值为Self时,表示元素自身或自身的属性是绑定源;取值为TemplatedParent时,表示引用ControlTemplate模板的元素或元素的属性是绑定源。Path也是RelativeSource的属性,用于指定被绑定的属性。
例如,在向一个页面中添加一个TextBlock文本块时,设置文本块的FontSize属性值为30像素, 并使用RelativeSource标记扩展的Self模式将TextBlock自身的FontSize属性绑定到Text属性上,作为文本内容显示出来,相应的XAML代码片段如下所示:
<TextBlock FontSize="30" Text="{Binding RelativeSource={RelativeSource Self}, Path=FontSize}"/>
值得注意的是,在ControlTemplate元素内部使用RelativeSource标记扩展的TemplatedParent模式绑定字符串类型的属性时,"Binding RelativeSource={RelativeSource TemplatedParent}}"的执行效果等价于TemplateBinding标记扩展。
另外,XAML还允许开发人员自定义标记扩展,方法是使自定义的标记扩展类继承MarkupExtension基类,并覆盖基类中的ProvideValue方法。