1. RelativeSource
用来指定一个相对的source位置,通常有以下三种方式:
(1): RelativeSource.Self
允许把自己当作相对的元素。
(2): RelativeSource.FindAncestor
根据指定的类型(AncestorType)和向上寻找的层次(AncestorLevel)向祖先寻找。例如AncestorLevel
设为1,则把第一次找到的匹配的object作为参照对象,如果为2,则把第二次匹配的object作为参照对象。
<StackPanel Background="Blue">
<TextBlock Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType= {x:Type StackPanel}, AncestorLevel=1}, Path=Background}" />
</StackPanel>
(3): RelativeSource.TemplatedParent
当自己在一个template里面的时候,取被设置了该template的对象作为参照对象。
<Window>
<Window.Resources>
<ControlTemplate x:Key="myButtonTemplate" TargetType="{x:Type Button}">
<StackPanel Background="{Binding RelativeSource={RelativeSource
TemplatedParent},Path=Background}"/>
</ControlTemplate>
</Window.Resources>
<Button Template="{StaticResource myButtonTemplate}" Background="Blue"/>
</Window>
在这里,设置了Button的Background是什么,则template里的StackPanel的Background就是什么了。
2. TemplateBinding和ContentPresenter
Binding提供了更灵活的绑定方式,但是开销较大。TemplateBinding只可以在以下一种场景使用,
但是很方便:当一个元素在一个template里面的时候,用TemplateBinding将该元素的某一个属性和
被templat的元素的某一个属性绑定,这样被template的元素的这个属性值就会传给这个元素。
例如下面的例子:
<Window x:Class="CommonQuestions.Window1″
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommonQuestions" Height="300″ Width="300″
>
<Window.Resources>
<ControlTemplate TargetType="{x:Type Button}" x:Key="buttonTemplate">
<Border BorderBrush="{TemplateBinding Property=Background}"
BorderThickness="3″ >
<ContentPresenter Margin="10″/>
</Border>
</ControlTemplate>
</Window.Resources>
<StackPanel Margin="20″>
<Button Template="{StaticResource buttonTemplate}"
HorizontalAlignment="Center" Background="SteelBlue">Hello</Button>
</StackPanel>
</Window>
在这里,Button的Background值"SteelBlue"就会传递给它所应用的ControlTemplate里的Border的
BorderBrush属性了。TemplateBinding是可以和Binding的RelativeSource.TemplatedParent转换的,
作用一样。上面的也可以写成:
<Border BorderBrush="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Background}"/>注意这里只能是OneWay的传递方式,要实现其它的传递方式,
就必须用Binding了。另外,TemplateBinding也提供了Converter和ConverterParameter,满足在
不同类型间传递了。
ContentPresenter
ContentPresenter是一个用在ContentControl的template里的元素,ContentPresenter is used to
specify where the content should go in the ContentControl's template(也就是说用它指定
应用这个template的Control的Content显示在template里的哪个位置)。
例如上例中所用到的<ContentPresenter Margin="10″/>实际上等价于下面的语句:
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate=
"{TemplateBinding ContentTemplate}" ContentTemplateSelector="{TemplateBinding
ContentTemplateSelector}" Margin="10″/>
因为对于这里的Content和ContentTemplate的声明是任何时候都有的情况,所以我们省略
成了上面的形式。但是这里说明了我们可以显式的另外指定ContentPresenter 的Content,
如果我们改成:
<ContentPresenter Content="{TemplateBinding Background}" Margin="10″/>
以前是把"Hello"显示在template的Border里,现在显示的却是"#FF4682B4″,也就是说被
新设的值覆盖了。
3. IValueConverter
ConvertBack里的实现,如果return Binding.DoNothing,例如:
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if(...)
return Binding.DoNothing;
return ....;
}
如果return Binding.DoNothing,则In a one-way binding, the data goes from
Source -> Converter -> Target. If you return Binding.DoNothing in
the Convert method, the data transfer goes from Source -> Converter
and stops there, it does not continue to the target. Future changes
in the Source will still trigger a
data transfer,也就是说这次数据传递被取消,但是以后的传递如果不出错,还是
会继续传递的。如果return null,则当null是合法的source值时,会赋给source,
但不合法时会报错。因为在OneWay binding时,ConvertBack是不会执行的,所以这里
最好是throw一个Exception,如果有错误执行到这里了,程序会报错,我们也容易发现错误了。
4. ItemsPanelTemplate
对于ItemsControl,用ItemsPanelTemplate指定它的所有Items的Layout,例如
Items水平或竖直排列,对齐方式等等。
例如下面的例子创建一个所有Items以水平方向排列的ListBox:
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
这里用到了ItemsPanel属性。
下面的方式不设置ItemsPanel属性,而是通过更改ListBox的ControlTemplate,
创建了一个带有rounded corners的水平排列的ListBox:
<Style TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Border CornerRadius="5" Background="{TemplateBinding ListBox.Background}">
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center"
IsItemsHost="True"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
但是这种方式注意的是要设置StackPanel的IsItemsHost属性为true。
然而,我们可以在ControlTemplate中使用ItemsPresenter,让它调用
ItemsPanelTemplate来layout所有的Items:
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border CornerRadius="5"
Background="{TemplateBinding ListBox.Background}">
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
又如下面的例子,
<!--Define a control template for a HeaderedItemsControl-->
<Style TargetType="HeaderedItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedItemsControl}">
<StackPanel>
<Grid>
<Rectangle Fill="{TemplateBinding Background}"/>
<ContentPresenter ContentSource="Header"/>
</Grid>
<Grid>
<Rectangle Stroke="{TemplateBinding BorderBrush}"/>
<ItemsPresenter Margin="2,0,0,0"/>
</Grid>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<HeaderedItemsControl xmlns:sys="clr-namespace:System;
assembly=mscorlib"
Header="My colors"
Background="SteelBlue"
BorderBrush="DarkSlateBlue">
<sys:String>Red</sys:String>
<sys:String>Yellow</sys:String>
<sys:String>Blue</sys:String>
<sys:String>Green</sys:String>
</HeaderedItemsControl>
注意这里用ContentPresenter的ContentSource属性指定了将被template
的元素的Header属性显示在第一个Rectangle内。后面的ItemsPresenter
属性指定了在这个Rectangle内显示所有Items,并且所有的Items有Margin
为2,0,0,0。
5. Binding的NotifyOnSourceUpdated和NotifyOnTargetUpdated属性
例如NotifyOnTargetUpdated就指示是否触发TargetUpdated事件当数据从source到target传递的时候。默认是false,例如:
<TextBlock Grid.Row="1" Grid.Column="1" Name="RentText"
Text="{Binding Path=Rent, Mode=OneWay, NotifyOnTargetUpdated=True}"
TargetUpdated="OnTargetUpdated"/>
public void OnTargetUpdated(Object sender, DataTransferEventArgs args)
{
// Handle event
}
NotifyOnSourceUpdated则是相反方向传递的情况。
6.注意x:Static的用法
The code entity referenced must be one of the following:
- constant
- static property
- field
- enumeration value
例如:
<Button FontSize="8" Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="5"
HorizontalAlignment="Left"
Height="{x:Static SystemParameters.CaptionHeight}"
Width="{x:Static SystemParameters.IconGridWidth}">
SystemParameters
</Button>
7. Binding Path的几种特殊写法
- To bind to an attached property, place parentheses around the attached property. For example, to bind to the attached property DockPanel.Dock, the syntax is Path=(DockPanel.Dock).
- When the source is a collection view, the current item can be specified with a slash (/). For example, the clause Path=/ sets the binding to the current item in the view. When the source is a collection, this syntax specifies the current item of the default collection view.
- Property names and slashes can be combined to traverse properties that are collections. For example, Path=/Offices/ManagerName specifies the current item of the source collection, which contains an Offices property that is also a collection. Its current item is an object that contains a ManagerName property.
- Optionally, a period (.) path can be used to bind to the current source. For example, Text="{Binding}" is equivalent to Text="{Binding Path=.}".