WPF Silverlight WP7 - RelativeSource

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>

 

 

 

image

 

可以看到 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>

 

 

 

改成這樣以後顯示的畫面如下

image

這的確是我們要的~~但是如果想要在 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 就變成

image

 

 

 

恩~~不管幾號Help都是好人~

關於 RelativeSource 的其他用法可以參考MSDN

不過 RelativeSource 的 Mode 有些只有在 WPF 有支援,在 Silverlight 支援的比較少

還有就是 Mode .TemplatedParent 只有在ControlTemplate的時候支援