我們已經看到綁定一個單獨的對象,還看到綁定一個單獨的對象列表。另一 種非常流行的方式是綁定多個對象列表,尤其是相關的列表。例如,如果你向用 戶顯示一個客戶列表,當他們選中其中一個客戶,就會顯示客戶的相關訂單,這 時,你就會使用主從復合綁定。
主從復合綁定是一種過濾的形式,在主頁面的列表部分,客戶452設置了過濾 器作為聯合到從頁面數據的參數,例如,客戶452的相關訂單。
到我們目前的討論為止,我們並沒有客戶和訂單這樣的模型,但是我們已經 有了家庭和人的定義,於是可以進一步形式化這些,如示例4-47
示例4-47
public class Families : ObservableCollection<Family> {}
public class Family {
string familyName;
public string FamilyName {
get { return familyName; }
set { familyName = value; }
}
People members;
public People Members {
get { return members; }
set { members = value; }
}
}
public class People : ObservableCollection<Person> {}
public class Person {
string name;
public string Name {
get { return name; }
set { name = value; }
}
int age;
public int Age {
get { return age; }
set { age = value; }
}
}
在示例4-47中,我們得到了熟悉的具有Name和Age屬性的Person類,聚合在熟 悉的People集合中。進一步,我們有Family這樣的類,具有FamilyName屬性和 People類型的Members屬性。最後,我們有一個Families集合,聚合了Family類 型的對象。換句話說,families包含著members,後者由帶有年齡和名稱的 people組成。。
你可以想象到Families,Family,People以及Person的實例如圖4-19所示。
在圖4-19中,Families集合構成了主頁面的數據,保存著Family類的實例, 每一個Family實例中持有一個People類型的Members屬性,People類型中保存著 Person這樣的數據。你可以導入這種數據結構的實例,正如示例4-48所示。
圖4-19
示例4-48
<!-- Window1.xaml -->
<?Mapping
XmlNamespace="local" ClrNamespace="MasterDetailBinding" ?>
<Window xmlns:local="local">
<Window.Resources>
<local:Families x:Key="Families">
<local:Family FamilyName="Stooge">
<local:Family.Members>
<local:People>
<local:Person Name="Larry" Age="21" />
<local:Person Name="Moe" Age="22" />
<local:Person Name="Curly" Age="23" />
</local:People>
</local:Family.Members>
</local:Family>
<local:Family FamilyName="Addams">
<local:Family.Members>
<local:People>
<local:Person Name="Gomez" Age="135" />
<local:Person Name="Morticia" Age="121" />
<local:Person Name="Fester" Age="137" />
</local:People>
</local:Family.Members>
</local:Family>
</local:Families>
</Window.Resources>
</Window>
在頂級綁定到這些數據,例如,顯示這些家庭的姓名,如示例4-49所示。
示例4-49
<!-- Window1.xaml -->
<?Mapping ?>
<Window >
<Window.Resources>
<local:Families x:Key="Families"></local:Families>
</Window.Resources>
<Grid DataContext="{StaticResource Families}">
<!-- Families Column -->
<TextBlock Grid.Row="0" Grid.Column="0">Families:</TextBlock>
<ListBox Grid.Row="1" Grid.Column="0"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock TextContent="{Binding Path=FamilyName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
在示例4-49中,我們在Families列(第0行)做了兩件事。第一件將標題設置 為常量“Families”字符串;第二件是形成了body主體,這是一個Family對象的 清單,位於Families集合中,現時每一個家庭的FamilyName屬性,正如圖4-20所 示。
圖4-20
圖4-20並不是一個主從復合結構,因為選中一個主頁面的家庭並不會顯示這 個家庭聯合到的詳細信息。為了這麼做,我們需要綁定到下一級,如示例4-50所 示。
示例4-50
<Grid DataContext="{StaticResource Families}">
<!-- Families Column -->
<!-- Members Column -->
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
<TextBlock TextContent="{Binding Path=FamilyName}" />
<TextBlock TextContent=" Family Members:" />
</StackPanel>
<ListBox Grid.Row="1" Grid.Column="1"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Members}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock TextContent="{Binding Path=Name}" />
<TextBlock TextContent=" (age: " />
<TextBlock TextContent="{Binding Path=Age}" />
<TextBlock TextContent=" )" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
在Members列(第1列),我們也設置了標題和主體,但是這次標題綁定到當 前選中的Family對象的FamilyName。
同樣,回憶在Families列,我們的列表框的源條目通過不帶Path屬性的 Binding語句,綁定到了整個集合。然而,在從頁面中,我們想告訴數據綁定引 擎,我們想要綁定到當前選中的Family對象的Members屬性,這是一個Person對 象的列表。圖4-21顯示了主從綁定的效果。
圖4-21
但是,稍等:有點過了。主從綁定並沒有在這兩個級別停下來,根本沒有。 你可以走得盡可能深,每一個從頁面都是主頁面的下一級。為了看到這個效果, 讓我們多增加一級從頁面到我們的數據類,如示例4-51所示。
示例4-51
public class Person {
string name;
public string Name {
get { return name; }
set { name = value; }
}
int age;
public int Age {
get { return age; }
set { age = value; }
}
Traits traits;
public Traits Traits {
get { return traits; }
set { traits = value; }
}
}
public class Traits : ObservableCollection<Trait> {}
public class Trait {
string description;
public string Description {
get { return description; }
set { description = value; }
}
}
現在,不由僅家庭有了家庭名和成員——帶有姓名和年齡的人,而每一個人 都有一組特性,每一個都有其獨自的描述。張開我們的xaml一小塊,包括了這些 特性,如示例4-52所示。
示例4-52
<local:Families x:Key="Families">
<local:Family FamilyName="Stooge">
<local:Family.Members>
<local:People>
<local:Person Name="Larry" Age="21">
<local:Person.Traits>
<local:Traits>
<local:Trait Description="In Charge" />
<local:Trait Description="Mean" />
<local:Trait Description="Ugly" />
</local:Traits>
</local:Person.Traits>
</local:Person>
<local:Person Name="Moe" Age="22" ></local:Person>
</local:People>
</local:Famil.Members>
</local:Family>
</local:Families>
我們可以綁定第3級從頁面,如示例4-53所示。
示例4-53
<Grid DataContext="{StaticResource Families}">
<!-- Families Column -->
<!-- Members Column -- >
<!-- Traits Column -->
<StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal">
<TextBlock TextContent="{Binding Path=Members/Name}" />
<TextBlock TextContent=" Traits:" />
</StackPanel>
<ListBox Grid.Row="1" Grid.Column="2"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Members/Traits}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock TextContent="{Binding Path=Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
在Families的列標題中,回想我們沒有任何綁定;這個文本是硬編碼的:
<TextBlock …>Families:</ TextBlock>
在Members的列標題中,我們綁定了當先選中的Family對象FamilyName如下:
<TextBlock … TextContent=”{Binding Path=FamilyName}” />
邏輯上講,你可以認為是對如下進行了擴展:
<TextBlock … TextContent=”{Binding Path=Family.FamilyName}” />
這裡family是當前選中的Family對象。
取得這一級的更深一級,在具有特性的列標題中,我們將當前選中Family的 Members屬性綁定到當前選中Person的Name屬性,綁定如下:
<TextBlock …
TextContent=”{Binding Path=Members/Name}” />
再次,從邏輯上,你可以把它當作這樣的擴展:
<TextBlock …
TextContent=”{Binding Path=family.Members.person.Name}” />
這裡family是當前選中的Family對象,person是當前選中的Person對象。綁 定表達式“/”擔當了對象間的分隔符,每一級的對象假定為“當前選中的”。
列表框的源條目的綁定以同樣的方式工作,除非我們想要的是當前選中 Person的TRaits集合,而不是Name。我們的多級主從綁定的示例如圖4-22所示。
圖4-22