2009/08/04

Data Binding
本文主要來自Piping Value Converters一文,有興趣的人也可以直接看MSDN Data Binding Overview

先來看看該文範例執行時的畫面如下:


而用來 Data Binding 的資料如下:

<?xml version="1.0" encoding="utf-8" ?>
<Tasks>
  <Task Name="Paint the living room" Status="0" />
  <Task Name="Wash the floor" Status="-1" />
  <Task Name="Study WPF" Status="1" />
  <Task Name="Rebuild kernel" Status="-1" />
  <Task Name="Kernel Install" Status="0" />
  <Task Name="Rebuild Driver" Status="1" />
</Tasks>

問題:

根據所提供的資料,如何把它依某一個屬性(範例中是用 Status)轉換成不同的 UI 元素?以此例除了 Pending, Complete, Active 外,還有顏色。

說明:

資料在繫結時(Data Binding),若有設定 Converter 屬性的話會先經過 IValueConverter型別的 Converter 方法轉換,對單一的轉換就是這麼簡單,可是若要多重轉換時怎麼處理才好呢?該文撰寫了一個繼承自 IValueConverter 的 ValueConverterGroup 來達成此目標。在實作上, ValueConverterGroup 可以擁有多個 Converters, 在呼叫 Converter 時,會依序呼叫。比較特別需要注意的是,前一個 converter 的輸出會當成下一個 converter 的輸入,直到最後一個 converter 的輸出被當成 target 為止。

由 ValueConverterGroup 實例來看問題:

範例中定義下面三個 ValueConverterGroup 實例(Instance), 關於 ValueConverterGroup 的定義則見後面的說明。重溫前面的問題,我們只有一個 Status 屬性,可是要做三件事,一是將數值型態的 Status 轉換成易讀的 Pending, Complete, Active,一是不同的 Status 以不同的顏色來顯示,三是對不同的 Status 顯示不同的 Tooltip.

    <!-- 第一部份: 將 Status 屬性值轉換成像 Pending, Complete, Active 這樣的字串以便顯示在 GUI 上 -->
    <local:ValueConverterGroup x:Key="statusDisplayNameGroup">
      <local:IntegerStringToProcessingStateConverter  />
      <local:EnumToDisplayNameConverter />
    </local:ValueConverterGroup>

    <!-- 第二部份: 根據 Status 屬性值轉換成不同的顏色,以便反應不同的狀態 -->
    <local:ValueConverterGroup x:Key="statusForegroundGroup">
      <local:IntegerStringToProcessingStateConverter  />
      <local:ProcessingStateToColorConverter />
      <local:ColorToSolidColorBrushConverter />
    </local:ValueConverterGroup>

    <!-- 第三部份: 根據 Status 屬性值轉換成不同的 Tooltip -->
    <local:ValueConverterGroup x:Key="statusDescriptionGroup">
      <local:XmlAttributeToStringStateConverter />
      <local:IntegerStringToProcessingStateConverter  />
      <local:EnumToDescriptionConverter />
    </local:ValueConverterGroup>   

從上面的實例可以看到三個 Key 值分別是 statusDisplayNameGroup, statusForegroundGroup, statusDescriptionGroup,使用上可以由 <DataTemplate> 來引用,範例如下,剛好可以看到三個 Converter 分別引用了這三個 Key 值:

    <DataTemplate x:Key="taskItemTemplate">
      <StackPanel
        Margin="2"
        Orientation="Horizontal"
        ToolTip="{Binding XPath=@Status, Converter={StaticResource statusDescriptionGroup}}"
        >
        <TextBlock Text="{Binding XPath=@Name}" />
        <TextBlock Text="   (" xml:space="preserve" />
        <TextBlock
          Text="{Binding XPath=@Status, Converter={StaticResource statusDisplayNameGroup}}"
          Foreground="{Binding XPath=@Status, Converter={StaticResource statusForegroundGroup}}" />
        <TextBlock Text=")" />
      </StackPanel>
    </DataTemplate>


有興趣的人也可以分析上面的 來看最後 GUI 所呈現的每一筆資訊都由 @Name, (, @Status, ) 四個部份組成,運用上將 Status 用三個 Converter 來轉換成需要的元素即達成目的。

ValueConverterGroup 的定義:

這邊我只列出 Convert 的定義:

object IValueConverter.Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
object output = value;

for( int i = 0; i < this.Converters.Count; ++i )
{
IValueConverter converter = this.Converters[i];
Type currentTargetType = this.GetTargetType( i, targetType, true );
output = converter.Convert( output, currentTargetType, parameter, culture );

// If the converter returns 'DoNothing' then the binding operation should terminate.
if( output == Binding.DoNothing )
break;
}

return output;
}

上面定義被標示成紅色斜線的部份,也就是上面在說明一節提到的「前一個 converter 的輸出會當成下一個 converter 的輸入」的實作方式。順帶一提的是,在 Data Binding 時對錯誤資料的處理可以透過 Binding.DoNothing 來終止繫結。

0 意見: