
為了得到更人性化的外觀,需要設計如何修剪資料清單和資料字段。
資料轉換
在基本綁定中,資訊從源到目标傳遞過程沒有任何變化。但有時候希望将資訊轉換到更友好的内容再呈現到界面上。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 屬性,每個清單項都将應用該樣式。
<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>