本文主要來自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>
有興趣的人也可以分析上面的
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 意見:
張貼留言