WPF Silverlight - RelativeSource
寫 XAML 幾乎都會用到 Binding 和 DataTemplate,
先來做個簡單的模擬
首先~我有兩個資料物件如下
1: public class DetailModel {
2: public string Name { get; set; }
3: public string Type { get; set; }
4: }
5:
6:
7: public class DataModel{
8: public string Title { get; set; }
9: public List<DetailModel> Details { get; set; }
10:
11: public DataModel() {
12: Title = "RelativeSourceTest";
13: InitDetails();
14: }
15:
16: private void InitDetails() {
17: Details = new List<DetailModel>();
18: Details.Add(new DetailModel() { Name = "Help0", Type = "好人" });
19: Details.Add(new DetailModel() { Name = "Help1", Type = "好人" });
20: Details.Add(new DetailModel() { Name = "Help2", Type = "好人" });
21: Details.Add(new DetailModel() { Name = "Help3", Type = "好人" });
22: }
23: }
DetailModel 和 DataModel
然後 XAML 加入一個 Listbox 和 Textblock,並且將資料 binding 上去,XAML 如下
<Window x:Class="RelativeSourceTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RelativeSourceTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:DataModel x:Key="DataModelSource"/>
</Window.Resources>
<Grid DataContext="{StaticResource DataModelSource}">
<ListBox Margin="12,47,276,12" Name="Data" ItemsSource="{Binding Details}"/>
<TextBlock Margin="12,12,384,276" Name="Title" Text="{Binding Title}" />
</Grid>
</Window>
可以看到 Listbox 裡面預設會顯示 Class.ToString() 的內容,通常都不會是我們 想要的
所以要對 ListBox 的 ItemTemplate 做修改
<Window x:Class="RelativeSourceTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RelativeSourceTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:DataModel x:Key="DataModelSource"/>
<DataTemplate x:Key="ListboxItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text=" 是一個 "/>
<TextBlock Text="{Binding Type}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid DataContext="{StaticResource DataModelSource}">
<ListBox Margin="12,47,276,12" Name="Data" ItemsSource="{Binding Details}"
ItemTemplate="{StaticResource ListboxItemTemplate}" />
<TextBlock Margin="12,12,384,276" Name="Title" Text="{Binding Title}" />
</Grid>
</Window>
改成這樣以後顯示的畫面如下
這的確是我們要的~~但是如果想要在 ListBox 裡面的項目增加下拉式選單,然後內容是連結外部資料該怎麼辦?
由於下拉式選單的資料是相同的,總不好在 DetailModel 裡面增加吧,通常這類的資料會放在 DataModel 裡面
所以我將 DataModel 改成
1: public class DataModel{
2: public string Title { get; set; }
3: public List<string> Types { get; set; }
4: public List<DetailModel> Details { get; set; }
5:
6: public DataModel() {
7: Title = "RelativeSourceTest";
8: InitTypes();
9: InitDetails();
10: }
11:
12: private void InitTypes() {
13: Types = new List<string>();
14: Types.Add("好人");
15: Types.Add("壞人");
16: Types.Add("爛人");
17: Types.Add("笨蛋");
18: }
19:
20: private void InitDetails() {
21: Details = new List<DetailModel>();
22: Details.Add(new DetailModel() { Name = "Help0", Type = "好人" });
23: Details.Add(new DetailModel() { Name = "Help1", Type = "好人" });
24: Details.Add(new DetailModel() { Name = "Help2", Type = "好人" });
25: Details.Add(new DetailModel() { Name = "Help3", Type = "好人" });
26: }
27: }
我加了 Types 當作下拉選單的內容
此時問題就來了,ListBox 的 ItemsSource 是和 Details 聯繫,因此 DataTemplate 的聯繫資料是 DetailModel
如果我們直接在 DateTemplate 裡增加
<ComboBox ItemsSource="{Binding Types}"/>
是沒有任何作用的,因為 DetailModel 裡並沒有 Types 這個欄位
這時候就是 RelativeSource 出現的時候了(鋪梗鋪真久)
將 CombpBox 改成這樣
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Grid}, Path=DataContext.Types}" SelectedItem="{Binding Type}"/>
RelativeSource 是相對來源的意思,和相對路徑有點類似的感覺
Mode=FindAncestor 選擇往上找目標,目標的類別是 Grid,Path 就是找到目標後要聯繫的資料
由於 DataModel 是連繫在 Grid.DataContext 上,所以 Path 就是 DataContext.Types
這樣 ListBox 就變成
恩~~不管幾號Help都是好人~
關於 RelativeSource 的其他用法可以參考MSDN
不過 RelativeSource 的 Mode 有些只有在 WPF 有支援,在 Silverlight 支援的比較少
還有就是 Mode .TemplatedParent 只有在ControlTemplate的時候支援