二、 依賴屬性的優先級
由於WPF 允許我們可以在多個地方設置依賴屬性的值,所以我們就必須要用一個標准來保證值的優先級別。比如下面的例子中,我們在三個地方設置了按鈕的背景顏色,那么哪一個設置才會是最終的結果呢?是Black、Red還是Azure呢?
<Window x:Class="WpfApp1.WindowDepend" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WindowDepend" Height="400" Width="400"> <Grid> <Button x:Name="myButton" Background="Azure"> <Button.Style> <Style TargetType="{x:Type Button}"> <Setter Property="Background" Value="Black"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Red" /> </Trigger> </Style.Triggers> </Style> </Button.Style> Click </Button> </Grid> </Window>
通過前面的簡單介紹,我們了解了簡單的依賴屬性,每次訪問一個依賴屬性,它內部會按照下面的順序由高到底處理該值。詳細見下圖

由於這個流程圖偏理想化,在實際的工作過程中我們會遇到各種各樣的問題,我也不可能都碰到,也就無法徹底把這些問題說清楚,所以當我們遇到問題之后,再進行仔細分析,查找原因,不斷總結、舉一反三。
三、 依賴屬性的繼承
屬性值繼承是 Windows Presentation Foundation (WPF) 屬性系統的一項功能。 屬性值繼承使元素樹中的子元素可以從父元素那里獲取特定屬性的值,並繼承該值,就好像它是在最近的父元素中的任意位置設置的一樣。 父元素還可以通過屬性值繼承來獲得其值,因此系統有可能一直遞歸到頁面根元素。 屬性值繼承不是屬性系統的默認行為;屬性必須用特定的元數據設置來建立,以便使該屬性能夠對子元素啟動屬性值繼承。
依賴屬性繼承的最初意願是父元素的相關設置會自動傳遞給所有層次的子元素 ,即元素可以從其在樹中的父級繼承依賴項屬性的值。這個我們在編程當中接觸得比較多,如當我們修改窗體父容器控件的字體設置時,所有級別的子控件都將自動 使用該字體設置 (前提是該子控件未做自定義設置)。接下來,我們來做一個實際的例子。代碼如下:
<Window x:Class="WpfApp1.WindowInherited" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WindowInherited" Height="400" Width="500" Loaded="Window_Loaded" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="101*"/> <RowDefinition Height="80"/> <RowDefinition Height="80"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" > <Label Content="繼承自Window的FontSize" /> <TextBlock Name="textBlockInherited" Text="重寫了繼承,沒有繼承Window的FontSize" FontSize="36" TextWrapping="WrapWithOverflow"/> <StatusBar>沒有繼承自Window的FontSize,Statusbar</StatusBar> </StackPanel> <WrapPanel Grid.Row="1"> <Label Content="窗體字體大小" /> <ComboBox Name="drpWinFontSize"></ComboBox> <Button Name="btnFontSize" Click="btnFontSize_Click">改變window字體</Button> </WrapPanel> <WrapPanel Grid.Row="2"> <Label Content="文本字體大小" /> <ComboBox Name="drpTxtFontSize"></ComboBox> <Button Name="btnTextBlock" Click="btnTextBlock_Click">改變TextBlock字體</Button> </WrapPanel> </Grid> </Window>
代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// WindowInherited.xaml 的交互邏輯
/// </summary>
public partial class WindowInherited : Window
{
public WindowInherited()
{
InitializeComponent();
}
private void btnFontSize_Click(object sender, RoutedEventArgs e)
{
this.FontSize =Convert.ToInt32(drpWinFontSize.Text);
}
private void btnTextBlock_Click(object sender, RoutedEventArgs e)
{
this.textBlockInherited.FontSize = Convert.ToInt32(drpTxtFontSize.Text);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<int> listFontSize = new List<int>();
for (int i = 0; i <= 60; i++)
{
listFontSize.Add(i + 4);
}
drpTxtFontSize.ItemsSource = listFontSize;
drpWinFontSize.ItemsSource = listFontSize;
}
}
}
效果圖如下:

Window.FontSize 設置會影響所有的內部元素字體大小,這就是所謂的屬性值繼承,如上面代碼中的第一個Label沒有定義FontSize ,所以它繼承了Window.FontSize的值。但一旦子元素提供了顯式設置,這種繼承就會被打斷,如第二個TextBlock定義了自己的 FontSize,所以這個時候繼承的值就不會再起作用了。
這個時候你會發現一個很奇怪的問題:雖然StatusBar沒有重寫FontSize,同時它也是Window的子元素,但是它的字體大小卻沒 有變化,保持了系統默認值。那這是什么原因呢?作為初學者可能都很納悶,官方不是說了原則是這樣的,為什么會出現表里不一的情況呢?其實仔細研究才發現並 不是所有的元素都支持屬性值繼承。還會存在一些意外的情況,那么總的來說是由於以下兩個方面:
1、有些Dependency屬性在用注冊的時候時指定Inherits為不可繼承,這樣繼承就會失效了。
2、有其他更優先級的設置設置了該值,在前面講的的“依賴屬性的優先級”你可以看到具體的優先級別。
屬性值繼承通過混合樹操作。持有原始值的父對象和繼承該值的子對象都必須是 FrameworkElement 或 FrameworkContentElement,且都必須屬於某個邏輯樹。 但是,對於支持屬性繼承的現有 WPF 屬性,屬性值的繼承能夠通過邏輯樹中沒有的中介對象永久存在。 這主要適用於以下情況:讓模板元素使用在應用了模板的實例上設置的所有繼承屬性值,或者使用在更高級別的頁級成分(因此在邏輯樹中也位於更高位置)中設置的所有繼承屬性值。 為了使屬性值的繼承在這兩種情況下保持一致,繼承屬性必須注冊為附加屬性。
這里的原因是部分控件如StatusBar、Tooptip和Menu等內部設置它們的字體屬性值以匹配當前系統。這樣用戶通過操作系統的控制 面板來修改它們的外觀。這種方法存在一個問題:StatusBar等截獲了從父元素繼承來的屬性,並且不影響其子元素。比如,如果我們在 StatusBar中添加了一個Button。那么這個Button的字體屬性會因為StatusBar的截斷而沒有任何改變,將保留其默認值。所以大家 在使用的時候要特別注意這些問題。
