- A+
为了得到更人性化的外观,需要设计如何修剪数据列表和数据字段。
数据转换
在基本绑定中,信息从源到目标传递过程没有任何变化。但有时候希望将信息转换到更友好的内容再呈现到界面上。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>