1.
依赖属性(Dependency
Property)
.NET Framework 3.0引入了一个新的属性类型叫依赖属性, WPF,WF都在使用依赖属性用来实现样式化,数据绑定等.我们更多的使用依赖属性是为了让父元素的属性值在逻辑树上慢慢的传递到其子元素中,从而可以在整个可是父元素的逻辑子元素中共享属性值.WF就是依靠依赖属性来在工作流中的各Activity间传递属性值的. 所以,依赖属性内建的传递变更通知的能力是其最大特征.
如果你想让属性在一个包含内容子控件树的整个逻辑控件树中都有效并共享值时,你仅仅只需要将这个属性声明为依赖属性即可, WPF会通过内建的架构来支持属性的共享. 而在工作流中我们经常需要用到依赖属性,它保证了在一个工作流实例中,多个组件共享了同一个值. 幸运的是在WPF中大部分空间的属性都是依赖属性,这让我们应用时非常方便,而你并不需要着后边发生了什么。
a)
依赖属性的实现
依赖属性其实也是普通的.NET属性,只是通过DependencyProperty.Register方法将普通的.NET属性注册为依赖属性。在依赖属性的声明中,其实对应的普通.NET属性并不是必需的,因为其内部的GetValue和SetValue方法是公开的,依赖属性的使用者可以通过调用GetValue/SetValue而放弃对普通.NET属性的依赖。但建立在普通.NET熟悉之上更符合我们通常的做法,而且这样有利于在XAML中设置属性。
下面的代码展示了定义依赖属性的通常做法:
public class
Button:ButtonBase
{
//依赖属性
public static readonly DependencyProperty
IsDependencyProperty;
static Button()
{
//注册依赖属性
Button.IsDependencyProperty =
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)
{...}
}
在上面的示例代码中是标准的实现依赖属性定义的方法,这里需要注意的是依赖属性始终是定义在普通.NET属性之上的用Register静态方法注册的特殊属性,即便这里的.NET属性(如IsDefault属性)不是必需的。另一个需要注意的地方是,通过声明依赖属性,我们多了一个控制属性改变时的回调方法(即便这同样可以通过声明委托和事件来定义,但这里我们什么都没做,为什么不用呢?),这样的好处是我们可以在属性改变的时候做些我们想做的事情,而我们却省去了手动声明事件的任务。其实回调函数的存在是为了让我们保证在属性包装器中(IsDefault属性)仅仅使用标准的GetValue/SetValue而不用任何其他逻辑,转而将自定义逻辑写入回调函数--这样做是为了遵循WPF的统一设计原则,让XAML 设置属性与使用过程式代码设置属性保持一致。
b) 触发器
依赖属性的实现让我们可以在一个局部范围内保持属性值的共享,这样的好处是对于内存的节约,因为GetValue/SetValue内部使用了高效的稀疏存储系统. 前边提到过,依赖属性的一大特征是变更通知,意思就是当某些依赖属性的只改变了,WPF就会更具属性的元数据触发一系列动作.
WPF中有三种方式来实现这样的变更通知:
l
属性触发器: 在属性发生改变时执行自定义动作,而不用改任何过程代码。
l
数据触发器: 当普通.NET属性的值改变时调用自定义动作。
l
事件触发器: 当路由事件被触发时调用自定义动作。
属性触发器(Trigger)
当某个属性有一个特定的值时,属性触发器会执行一个Setter的集合来设置相关对象的属性,而当属性失去这个值的时候,属性触发器会撤消该Setter集合.
这样的好处是大大简化了我们用声明事件的办法来处理满足一定条件时的逻辑,我们在XAML中就可以完成相应的简单逻辑(如果复杂还是需要过程式代码)。
下边的示例显示了旋转变换仅在鼠标移到按钮上方时才会发生,并会设置相应属性。
<Style
x:key="buttonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger
Property="IsMouseOver" Value="True">
<Setter
Property="RenderTransform">
<Setter.Value>
<RotageTransform
Angle="10" />
</Setter.Value>
</Setter>
<Setter
Property="Foreground" Value="Black" />
</Trigger>
</Style.Trigger>
<Setter
Property="FontSize" Value="22" />
<Setter
Property="Foreground" Value="White" />
<Setter
Property="Background" value="Purple" />
<Setter
Property="Height" Value="50" />
<Setter
Property="Width" Value="50" />
<Setter
Property="RenderTransformOrigin" Value="0.5,0.5" />
</Style>
对于属性触发器的应用我们更多的还可以通过自定义验证规则来实现对样式和Tooltip的自动设置。
数据触发器(Data
Trigger)
数据触发器与属性触发器几乎一样,只是数据触发器可以由任何.NET属性触发,而不仅仅是依赖属性.为了使用数据触发器,可向Triggers集合中添加DataTrigger对象然后指定属性/值对.同时可以用Binding来指定相关属性而不仅仅是属性名.
以下示例通过binding指定当自己的值为disabled的时候将自己禁用. 注意:Text属性不是依赖属性.
<StackPanel
Width="200">
<StaticPanel.Resources>
<Style TargetType="{x:Type
TextBox}">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeResource=
{RelativeResource
Self}, Path=Text}"
value = "disabled">
<Setter
Property="IsEnabled" Value="false" />
/DataTrigger>
</Style.Triggers>
<Setter
Property="Background" Value=
"{Binding
RelativeResource={RelativeResource Self}, Path=Text}" />
</Style>
</StackPanel.Resources>
<TextBox Margin="3" />
</StackPanel>
事件触发器(Event
Trigger)
当一个已选择的事件产生时事件触发器会被激活.这个事件由触发器的RouteEvent属性指定,他在Action集合中包含一个或多个动作(从抽象类TriggerAction继承来的对象).
下边的示例展示了在Button的Click事件被触发时执行DoubleAnimation动作.
<Button>OK
<Button.Triggers>
<EventTrigger
RouteEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard
TargetProperty="width">
<DoubleAnimation
From="50" To="100"
Duration="0:0:5"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
c) 属性值继承
2. 附加属性(Attached Property)
附加属性也属于XAML为了扩展其能力的一种,它提供了让用户在父属性里设置自己没有的子属性值的能力。而在应用附加属性时,通常它也可以体现WPF中属性传递的优点:例如,当你给一个Panel中的某类元素设置了附加属性,那么在不显式声明相关属性值(重写)的情况下,此类元素共同从父元素得到这个值。
作为附加属性,之所以说是扩展了XAML的应用是因为它提供了通过父元素来设置子元素并在其可视树范围内默认共享的能力。那么我们可以通过给Panel来设置TextElement.FontSize属性来让所有在这个Panel下的拥有TextElement.FontSize相关属性的控件自动拥有这个值,而不需要我们挨个对所有此类空间设置。例如:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal"
TextElement.FontSize=”10”>
<Image Source="Fireworks.bmp"/>
<StackPanel Width="200">
<TextBlock Margin="5,0" VerticalAlignment="center">Fireworks</TextBlock>
<TextBlock Margin="5"
VerticalAlignment="center" TextWrapping="Wrap">
Sleek, with a black sky containing
fireworks. When you need to
celebrate PowerPoint-style, this
design is for you!
</TextBlock>
<Button x:name=”btnSubmit”>Agree</Button>
<Button x:name=”btnCancel”>Disagree</Button>
</StackPanel>
</StackPanel>
</ UserControl >
在上述的例子中,由于在StackPanel中设置了TextElement.FontSize,所以在它的内部的可视控件自动有用这个属性:TextBlock,Button都会以FontSize=10来显示。