WPF_15_格式化繫結的資料
為了得到更人性化的外觀,需要設計如何修剪資料列表和資料欄位。
資料轉換
在基本繫結中,資訊從源到目標傳遞過程沒有任何變化。但有時候希望將資訊轉換到更友好的內容再呈現到介面上。WPF提供了兩個工具:
- 字串格式化
- 值轉換器
單個屬性
Binding.StringFormat 屬性針對簡單的,標準的格式化數字和日期而建立的。
<TextBox Text="{Binding Path=UnitCost, StringFormat={}{0:C}}"/> <TextBox Text="{Binding Path=UnitCost, StringFormat=The value is {0:C}.}"/> <ListBox DisplayMemberPath="UnitCost" ItemStringFormat="{0:C}"/>
值轉換器功能更強大,建立值轉換器需要4個步驟:
- 建立一個實現了 IValueConverter 介面的類
- 為該類宣告新增 ValueConversion 特性,並指定目標資料型別
- 實現 Convert() 方法
- 實現 ConvertBack() 方法
[ValueConversion(typeof(decimal), typeof(string))] public class PriceConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { decimal price = (decimal)value; return price.ToString("C", culture); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { string price = value.ToString(cultere); decimal result; if(Decimal.TryParse(price, NumberStyles.Any, cultere, out result)) return result; return value; } }
<!--在Resources中建立轉換器物件,可以用於多個繫結-->
<Window.Resources>
<local:PriceConverter x:Key="PriceConverter"/>
</Window.Resources>
<TextBox Text="{Binding Path=UnitCost, Converter={StaticResource PriceConverter}}"/>
多個屬性
<TextBlock> <TextBlock.Text> <!--使用 MultiBinding 替換 Binding--> <MultiBinding StringFromat="{1}, {0}"> <Binding Path="FirstName"/> <Binding Path="LastName"/> </MultiBinding> </TextBlcok.Text> </TextBlock>
如果希望完成更復雜的工作,需要使用值轉換器:
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource ValueInStockConverter}">
<Binding Path="UnitCost"/>
<Binding Path="UnitsInStock"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
public class VallueInStockConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
decimal unitCost = (decimal)values[0];
int unitsInStock = (int)value[1];
return unitCost * unitsInStock;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
列表控制元件
ItemsControl 類為封裝列表中的控制元件定義了基本功能,所有列表控制元件都繼承自該類。
屬性名 | 說明 |
---|---|
ItemsSource | 資料來源 |
DisplayMemberPath | 期望資料項顯示的屬性 (更復雜的顯示使用ItemTemplate) |
ItemStringFormat | 為每個項格式化文字 |
ItemContainerStyle | 通過樣式可以設定封裝每個項的容器的多個屬性。自動建立這些封裝器物件 |
ItemContainerStyleSelector | 為每項的封裝器選擇樣式的StyleSelector物件 |
AIternationCount | 在資料中設定的交替集合數量 |
ItemTemplate | 模板從繫結的物件提取合適的資料並安排到合適的控制元件組合中 |
ItemTemplateSelector | 為每個項選擇模板的 DataTemplateSelector 物件 |
ItemsPanel | 用於包含列表中項的面板,所有封裝器都新增到這個容器中 |
GroupStyle | 定義應當如何格式化每個分組 |
GroupStyleSelector | 為每個分組選擇樣式的 StyleSelector 物件 |
列表樣式
ItemContainerStyle
當建立列表項時,列表控制元件會將其向下傳遞 ItemContainerStyle 屬性,每個列表項都將應用該樣式。
<ListBox Name="lstProducts" Margin="5" DisplayMemberPath="ModelName">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="LightSteelBlue"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Padding" Value="5"/>
<!--觸發器使得樣式更加精彩-->
<style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="DarkRed"/>
<Setter Property="Forground" Value="White"/>
<Setter Property="BorderBrush" Value="Blcak"/>
<Setter Property="BorderThickness" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<ListBox.ItemContainerStyle>
</ListBox>
可以讓每個 ListBoxItem 物件在項文字的旁邊顯示單選按鈕或複選框
<Window.Resources>
<Style x:Key="RadioButtonListStyle" TargetType="{x:Type ListBox}">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem">
<Setter Property="Margin" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton Focusable="False" IsChecked="{Binding Path=IsSelected,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}">
<!--ContentPresenter獲取最初在項中顯示的內容-->
<!--可能是文字,也可能是複雜的表示形式-->
<ContentPresenter/>
</RadioButton>
<!-- 多選框
<CheckBox Focusable="False" IsChecked="{Binding Path=IsSelected, Model=TwoWay,RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter/>
</CheckBox>
-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
交替條目樣式
AlternationCount指定序列中項的數量,經過改數量後交替樣式。如果設定為2,第一個ListBoxItem的 AlternationIndex=0,第二個為1,第三個為0,第四個為1……。
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="LightBlue"/>
</Trigger>
</Style.Triggers>
資料模板
樣式提供了基本的格式化能力,但不管如何修改ListBoxItem,它都只是ListBoxItem.資料模板是一塊定義如何顯示繫結的資料物件的XAML,有兩種型別的控制元件支援資料模板:
- 內容控制元件通過 ContentTemplate 屬性支援資料模板
- 列表控制元件通過 ItemTemplate 屬性支援資料模板
分離和重用模板
與樣式類似,通常也將模板宣告為視窗或程式的資源。
<Window.Resources>
<DataTemplate x:Key="ProductDataTemplate">
<Border Margin="5" BorderThickness="1" BorderBrush="StellBlue" CornerRadius="4"
Background="{Binding Path=Background, RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RouDefinition>
<TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}"/>
<TextBlock Grid.Row="1" Text="{Binding Path=ModelName}"/>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
通過 StaticResource 引用來為列表新增資料模板:
<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch"
ItemTemplate="{StaticResource ProductDataTemplate}"/>
如果希望在不同型別的控制元件中自動重用相同的模板,可以通過設定 DataTemplate.DataType 屬性來確定使用模板的繫結資料的型別。
<Window.Resources>
<!--模板將用於視窗中任何繫結到Product物件的列表控制元件或內容控制元件-->
<DataTemplate DataType="{x:Type local:Product}">
...
</DataTemplate>
</Window.Resources>
改變模板
目前只能為整個列表使用一個模板,如果希望採用不同方式靈活展示不同的資料:
- 使用資料觸發器
- 使用值轉換器
- 使用模板選擇器
模板選擇器檢查繫結物件並使用提供的邏輯選擇合適的模板,需要建立繼承自 DataTemplateSelector 的類。
ComboBox控制元件
與ListBox類不同的是,ComboBox類增加了另外兩個部分:顯示當前選擇項的選擇框和用於選擇項的下拉列表。
ComboBox提供了自動完成輸入功能,當鍵入內容時,WPF使用第一個匹配自動完成建議的項填充選擇框中的剩餘內容。可以通過設定 ComboBox.IsTextSearchEnabled 屬性設定為 false 禁用該功能。
如果IsEditable屬性為 true,ComboBox控制元件不是顯示選擇項的副本,而是顯示選擇項的文字形式表示,WPF簡單呼叫ToString()方法。可以通過設定 TextSearch.TextPaht 附加屬性來定義選擇框顯示的內容:
<ComboBox IsEditable="True" IsReadOnly="True" TextSearch.TextPath="ModelName">
...
</ComboBox>