【WPF學習】第三十章 元素繫結——繫結到非元素物件
前面章節一直都在討論如何新增連結兩個各元素的繫結。但在資料驅動的應用程式中,更常見的情況是建立從不可見物件中提取資料的繫結表示式。唯一的要求是希望顯示的資訊必須儲存在公有屬性中。WPF資料繫結資料結構不能獲取私有資訊或公有欄位。
當繫結到非元素物件時,需要放棄Binding.ElementName屬性,並使用以下屬性中的一個:
- Source:該屬性是指向源物件的引用——換句話說,是提供資料的物件。
- RelativeSource:這是引用,使用RelateSource物件指向源物件。有了這個附加層,可在當前元素(包含繫結表示式的元素)的基礎上構建引用。這似乎無謂地增加了複雜程度。但實際上,RelativeSource屬性是一種特殊工具,當編寫控制元件模板以及資料模板時很方便的。
- DataContext:如果沒有使用Source或RelativeSource屬性指定源,WPF就從當前元素開始在元素樹中向上查詢。檢查每個元素的DataContext屬性,並使用第一個非空的DataContext屬性。當我要將同一個物件的多個屬性繫結到不同的元素時,DataContext屬性是非常有用的,因為可在更高層次的容器物件上(而不是直接在目標元素上)設定DataContext屬性。
一、Source屬性
Source屬性非常簡單。唯一的問題是為了進行繫結,需要具有資料物件。在稍後將看到可使用集中方法獲取資料物件。可從資源中提取資料物件,可通過編寫程式碼生成資料物件,也可在資料提供的幫助下獲取資料物件。
最簡單的選擇是將Source屬性指向一些已經準備好了的靜態物件。例如,可在程式碼中建立一個靜態物件並使用該物件。或者,可使用來自.NET類庫的元件。如下所示:
<TextBlock Margin="5" Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}"></TextBlock>
這個繫結表示式獲取由靜態屬性SystemFonts.IconFontFamily提供的FontFamily物件(注意,為了設定Binding.Source屬性,需要藉助靜態標記擴充套件)。然後將Binding.Path屬性設定為FontFamily.Source屬性,給屬性給出了字型家族的名稱。結果是一行文字。在Windows Vista或Windows 7中,顯示的字型名稱segoe UI。
另一種選擇是繫結到先前作為資源建立的物件。例如,下面的標記建立指向Calibri字型的FontFamily物件:
<Window.Resources> <FontFamily x:Key="CustomFont">Calibri</FontFamily> </Window.Resources>
並且下面的TextBlock元素會被繫結到該資源:
<TextBlock Margin="5" Text="{Binding Source={StaticResource CustomFont}, Path=Source}"></TextBlock>
現在將會看到文字Calibri。
二、RelativeSource屬性
通過RelativeSource屬性可根據相對於目標物件的關係指向源物件。例如,可使用RelativeSource屬性將元素繫結到自身或其父元素(不知道在元素樹中從當前元素到繫結的父元素到繫結的父元素之間有多少代)。
為設定Binding.RelativeSource屬性,需要使用RelativeSource物件,這會使語法變得更加複雜,因為出了需要建立Binding物件外,還需要在其中建立巢狀的RelativeSource物件。一種選擇是使用屬性設定語法而不是使用Binding標識擴充套件。例如,下面的程式碼為TextBlock.Text屬性建立了一個Binding物件,這個Binding物件時候用查詢父視窗並顯示視窗標題的RelativeSource物件:
<TextBlock> <TextBlock.Text> <Binding Path="Title"> <Binding.RelativeSource> <RelativeSource Mode="Findncestor" AncestorType="{x:Type Window}" /> </Binding.RelativeSource> </Binding> </TextBlock.Text> </TextBlock>
RelativeSource物件使用FindAncestor模式,該模式告知查詢元素樹直到發現AncestorType屬性定義的元素型別。
編寫繫結更常用的方法是使用Binding和RelativeSource標記擴充套件,將其合併到一個字串中,如下所示:
<TextBlock Text="{Binding Path=Title,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Window}} }"> </TextBlock>
當建立RelativeSource物件時,FindAncestor模式有4中,下表列出了所有4中模式。
表 RelativeSourceMode列舉值
RelativeSource屬性看似多餘,並且會標記變得複雜。畢竟,為什麼不使用Source或Element屬性直接繫結到希望使用的源呢?然而,並不總是可以使用Source或ElementName屬性,這通常是因為源物件和目標物件在不同的標記塊中。當建立控制元件模板和資料模板時會出現這種情況。例如,如果正在構建改變列表項顯示方式的資料模組,可能需要訪問頂級ListBox物件以讀取屬性。
三、DataContext屬性
在某些情況下,會將大量元素繫結到同一個物件。例如,分析下面的一組TextBlock元素,每個TextBlock元素都使用類似的繫結表示式提取與預設圖示字型相關的不同細節,包括行間距,以及第一個字型的樣式和粗細(這兩個都是簡單的正則表示式)。可為每個TextBlock元素使用Source屬性,但這會使標記變得非常長:
<TextBlock Margin="5" Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=LineSpacing}"></TextBlock> <TextBlock Margin="5" Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=FamilyTypefaces[0].Style}"></TextBlock> <TextBlock Margin="5" Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=FamilyTypefaces[0].Weight}"></TextBlock>
對於這種情況,使用FrameworkElement.DataContext屬性一次性定義繫結源會更清晰,也更靈活。在這個示例中,為包含所有TextBlock元素的StackPanel面板設定DataContext屬性是合理的(甚至還可在更高層次上設定DataContext屬性——例如整個視窗——但是為了使意圖更清晰,在儘可能小的範圍內進行定義效果更好)。
可使用和設定Binding.Source屬性相同的方法設定元素的DataContext屬性。換句話說,可提供內聯物件,從靜態屬性中提取,或從資源中提取,如下所示:
<StackPanel Margin="10" DataContext="{x:Static SystemFonts.IconFontFamily}">
現在可通過省略源資訊來精簡繫結表示式:
<TextBlock Margin="5" Text="{Binding Path=Source}"></TextBlock>
當在繫結表示式中省略源資訊時,WPF會檢查元素的DataContext屬性。如果屬性值為null,WPF會繼續向上在元素樹中查詢第一個不為null的資料上下文(最初,所有元素的DataContext屬性都是null)。如果找到了一個數據上下文,就為繫結使用找到的資料上下文。如果沒有找到,繫結表示式不會為目標屬性應用任何值。
&n