在前一個Post中,曾提到將要重點研究Dependency屬性的三個方面:變化通知;屬性值的繼承;支持多個提供對象。下面,我將分別就這三個內容進行簡單地說明。
【變化通知】
在任何時候,只要Dependency屬性的值發生了變化,WPF可以自動地根據屬性的元數據觸發不同的行為。前面提到過:Dependency屬性最大的特點就是內建的變化通知功能。這種內建變化通知所提供的最值得注意的就是屬性觸發器(Property Trigger),就是它使用我們不需要編寫任何的程序代碼就能在屬性變化使執行自定義行為。請看下面XAML編碼的一個屬性觸發器例子:
<Trigger Property=”IsMouseOver” Value=”True”>
???? <Setter Property=”Foreground” Value=”Blue”/>
</Trigger>
它的功能就是在屬性值IsMouseOver變為True的時,將屬性Foreground的值設置為Blue。而且,它會在IsMouseOver變為False時自動將Foreground的值設置為原來的值。就是這樣簡單的三行代碼完成了我們曾經需要多個函數、變量才能實現的功能。
使用屬性觸發器時需要注意:觸發器默認適用于每個類對象。 而且,在WPF 3.0中由于人為的限制,Property Trigger將不能應用在單獨的元素。只能應用在某個Style對象之中。因此,如果想在某個單獨對象上實現Property Trigger。必須用如下的XAML進行封裝:
<Button MinWidth=”75” Margin=”10”>
<Button.Style>
???? <Style TargetType=”{x:Type Button}”>
???? <Style.Triggers>
?????????? <Trigger Property=”IsMouseOver” Value=”True”>
??????????????? <Setter Property=”Foreground” Value=”Blue”/>
?????????? </Trigger>
???? </Style.Triggers>
???? </Style>
</Button.Style>
???? OK
</Button>
【屬性值繼承】
屬性值繼承是指在設置邏輯樹某個結點元素的屬性后,它的所有之結點都繼承這個屬性值(當然,前提是子元素必須支持這個屬性)。我們仍然利用閑話WPF之八中的一個例子進行說明:
<Window FontSize=”30”>
???? <StackPanel>
?????????? <Label>LabelText</Lable>
???? </StackPanel>
</Window>
我們修改了Window是FontSize屬性為30。通過實際觀察將發現它的子元素Label的FontSize也變為了30。注意這里的StackPanel是一個容器元素,它本身并不支持FontSize屬性。
現在我們給上面的Window添加一個狀態欄。XAML代碼如下:
<Window ......>
???? <StackPanel>
?????????? <Label>LabelText</Lable>
?????????? <StatusBar>This is a Statusbar</StatusBar>
???? </StackPanel>
</Window>
這時你會發現:雖然StatusBar支持這個FontSize這個屬性,它也是Window的子元素,但是它的字體大小卻沒有變化。為什么呢?因為并不是所有的元素都支持屬性值繼承。還存在如下兩種例外的情況:
1、部分Dependency屬性在用Register注冊時可以指定Inherits為不可繼承。
2、如果有其他更高優先級方法設置了其他的值。(關于優先級的介紹且看下面分解。)
部分控件如StatusBar、Menu和Tooptip內部設置它們的字體屬性值以匹配當前系統的設置。這樣用戶通過控制面板可以修改它們的外觀。這種方法存在一個問題:StatusBar等截獲了從父元素繼承來的屬性,并且不影響其子元素。比如,如果我們在StatusBar中添加了一個Button。這個Button的字體屬性會因為StatusBar的截斷沒沒有改變,將保留其默認值。
附加說明:屬性值繼承的最初設計只適用于元素Tree,現在已經進行多個方面的擴展。比如,值可以傳遞下級看起來像Children,但在邏輯或者視覺Tree中并不是Children的某些元素。這些偽裝的子元素可以是觸發器、屬性的值,只要它是從Freezable繼承的對象。對于這些內容沒有很好的文檔說明。我們只需要能使用就行不必過多關心。