原文: 格式化绑定的数据
1、数据转换。
WPF中,数据绑定有两种方式:字符串格式化和值转换器。
1.1)、字符串格式化。
用StringFormat属性属性。
语法:StringFormat = "{}{0:格式字符串}" 其中第一个花括号{}是标识转义序列。
<TextBox FontSize="20" Grid.Row="2" Grid.Column="1" Height="30" Text="{Binding Path=ProPrice, StringFormat={}{0:C}}"></TextBox> 这样就会在前面加一个美元符号了。
常用的数字字符串如下:
数字数据的格式字符串 | ||
类型 | 格式字符串 | 示例 |
货币 | C | $1234.50 |
科学计数法(指数) | E | 1234.50E+004 |
百分数 | P | 45.6% |
固定小数 | F | 取决于设置的小数位数,F3格式化数值类似于123.400,F0类似于123 |
时间和日期数据的格式字符串 | ||
格式 | ||
短日期 | d | M/d/yyyy 例如:01/30/2013 |
长日期 | D | Dddd,MMMM dd,yyyy |
长日期和短时间 | f | Dddd,MMMM,dd,YYYY HH:mm:ss aa |
长日期和长时间 | Dddd,MMMM,dd,yyyy HH:mm:ss aa | |
ISO sortable标准 | s | Yyyy-MM-dd HH:mm:ss |
月和日 | M | MMMM dd |
通用格式 | G | M/d/yyyy HH:mm:ss aa |
1.2)、值转换器。
Binding.StringFormat属性是针对简单点的、标准的格式化数字和日期而创建的,但许多数据绑定需要更强大的工具,称之为值转换器(Value Converter)类。值转换器的作用是显而易见的,它负责在目标中显示数据之前转换源数据,并且(对于双向绑定)在将数据应用回源之前转换新的目标值。
WPF可通过以下几种有用的方式来使用:
a)、将数据格式化为字符串表示形式。例如,可将数字转换成货币字符串。
b)、创建特定的WPF对象。例如,可读取一块二进制数据,并创建一副能绑定到Image元素的BitmapImage对象。
c)、根据绑定数据有条件地改变元素中的属性。例如,可创建值转换器,用于改变元素的背景色。
1.2.1)、使用值转换器设置字符串格式。
声明值转换器PriceValueConverter类:
//实现步骤如下:
//1、创建一个类,并继承IValueConverter接口。
//2、为该类声明添加ValueConversion特性,并指定目标数据数据类型。
//3、实现Convert()方法,该方法将数据从原来的格式转换为显示格式。
//4、实现ConvertBack()方法,该方法执行反向变换,将值从显示格式转换为原格式。
[ValueConversion(typeof(decimal),typeof(string))] //第2步。
public class PriceValueConverter : IValueConverter //第1步。
{
//第3步。
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//将值转换成decimal类型。
decimal price = (decimal)value;
return price.ToString("C", culture);
}
//第4步。
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//从显示格式转换回希望的数字更麻烦一些,Deciaml类型的Parse()和TryParse()方法是执行该工作的最合理的选择,
//如果提供NumberStyles.Any的值,如果存在货币符号,就能成功地略过货币符号。
string price = value.ToString();
decimal result;
if(Decimal.TryParse(price, System.Globalization.NumberStyles.Any,culture,out result))
{
return result;
}
return value;
}
}
View Code
导入名称空间:使用值转换器时,将项目名称空间映射到XML名称空间中:
<!--在这里名称空间是Test1-->
xmlns:local="clr-namespace:Test1"
创建PriceValueConverter实例,并将该实例指定给绑定的Converter属性:
<TextBox FontSize="20" Grid.Row="2" Grid.Column="1" Height="30">
<TextBox.Text>
<Binding Path="ProPrice">
<Binding.Converter> <!--通过Binding的Converter属性-->
<local:PriceValueConverter></local:PriceValueConverter>
</Binding.Converter>
</Binding>
</TextBox.Text>
</TextBox>
效果图:
在当前案例中,为每个绑定表达式都要创建一个转换器实例是不合理的,可以在Resource中创建转换器对象。
<Window.Resources>
<local:PriceValueConverter x:Key="PriceConverter1"></local:PriceValueConverter>
</Window.Resources>
<TextBox FontSize="20" Grid.Row="2" Grid.Column="1" Height="30" Text="{Binding Path=ProPrice, Converter={StaticResource PriceConverter1}}"></TextBox>
1.2.2)、使用值转换器创建对象。
要将一块二进制数据转换成一副图像,首先必须创建BitmapImage对象,并将图像数据读入到MemoryStream对象中。然后调用BitmapImage.Begin()方法来设置BitmapImage对象的StreamSource属性,使其指向MemoryStream对象,并调用EndInit()方法来完成图像的加载。
创建ImagePathConverter类:
public class ImagePathConverter:IValueConverter
{
//获取应用程序当前工作目录。
private string imageDirector = Directory.GetCurrentDirectory();
public string ImageDirector
{
get { return imageDirector; }
set { this.imageDirector = value; }
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//合并路径。
string imagePath = Path.Combine(ImageDirector, (string)value);
return new BitmapImage(new Uri(imagePath));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//throw new NotImplementedException();
return "暂不处理";
}
}
在资源中定义:
<Window.Resources>
<local:ImagePathConverter x:Key="ImagePathConverter1"></local:ImagePathConverter>
</Window.Resources>
引用:
<!--<Image Grid.Row="4" Grid.Column="1">
<Image.Source>
<Binding Path="ProImagePath">
<Binding.Converter>
<local:ImagePathConverter></local:ImagePathConverter>
</Binding.Converter>
</Binding>
</Image.Source>
</Image>-->
<Image Grid.Row="4" Grid.Column="1" Source="{Binding Path=ProImagePath, Converter={StaticResource ImagePathConverter1}}"></Image>
1.2.3)、使用值转换器创建对象。
值转换器不光能格式化数据,还可以根据数据规则格式化元素中的外观相关的一些工作。
新建PriceToBackgroundConverter类:
public class PriceToBackgroundConverter:IValueConverter
{
/// <summary>
/// 最小值。
/// </summary>
public decimal MinimumPriceToHighlight { get; set; }
/// <summary>
/// 亮背景。
/// </summary>
public Brush HighlightBrush { get; set; }
/// <summary>
/// 默认背景。
/// </summary>
public Brush DefaultBrush { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//得到值。
decimal price = (decimal)value;
//如果得到的值大于或等于MinimumPriceToHighlight的值。
if (price >= MinimumPriceToHighlight)
{
return HighlightBrush;
}
else
{
return DefaultBrush;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
<Window.Resources>
<local:PriceToBackgroundConverter x:Key="PriceToBackgroundConverter1" DefaultBrush="AntiqueWhite" MinimumPriceToHighlight="8" HighlightBrush="Orange"></local:PriceToBackgroundConverter>
</Window.Resources>
引用(引用的时候是为Border元素引用的):
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName"> </ListBox>
2、评估多个属性。
到目前为止,已经使用绑定表达式将一部分源数据转换成单个格式化的结果,还可以创建能够评估或结合多个源属性信息的绑定。绑定多个属性用MultiBinding对象代替Binding对象,然后使用MultiBinding.StringFormat属性定义绑定属性的排列。
<TextBox FontSize="20" Grid.Row="0" Grid.Column="1" Height="30">
<TextBox.Text>
<MultiBinding StringFormat="{}{0}--->{1}">
<Binding Path="ProId"></Binding>
<Binding Path="ProName"></Binding>
</MultiBinding>
</TextBox.Text>
</TextBox>
如果需要使用两个源字段完成更富有挑战性的工作,而不只是将它们缝合到一块,需要借助于值转换器。可使用这种方法计算(如两个数相乘)或同时使用多个细节进行格式化(比如突出显示特定目录中的所有高价位产品),对于这种情况,值转换器必须实现IMultiValueConverter接口,而不是IValueConverter接口。IMultiValueConverter接口定义了IValueConverter接口中类似的Convert()和ConvertBack()方法,主要区别是,提供了用于保护数值的数组而不是单个值。
3、列表控件继承图和常用属性。
先分析一下关系图:
继承自ItemsControl类的层次结构中,还显示了某些项封装器,例如,不仅看到了期望的Menu和TreeView类,还看到了MenuItem和TreeViewItem类,这是因为这些类能够包含自己的项集合---从而使树控件和菜单能够实现嵌套的层次化结构。另一方面,在这个列表中没有发现ComboBoxItem或ListBoxItem类,因为它们不需要包含项的子集合,所以不需要继承自ItemsControl类。
ItemsControl类中常用属性:
ItemsControl类中与格式化相关的属性 | |
名称 | 说明 |
ItemsSource | 绑定数据源(希望在列表中显示的集合或DataView对象) |
DisplayMemberPath | 希望每个数据项显示的属性。如果使用多个属性组合,需要使用ItemTemplate属性。 |
ItemsStringFormat | 将属性格式化成文本。 |
ItemsContainerStyle | 该属性是一个样式,通过该样式可以设置封装每个项的容器的多个属性。 |
ItemsContainerStyleSelector | 使用代码为列表中每项的封装器选择样式的StyleSelector对象,可以用该属性为列表中的不同项提供不同的样式,必须创建自定义的DataTemplateSelector类。 |
AlternationCount | 在数据中设置的交替集合数量,如果将AlternationCount设置成2,将在两个不同的行样式之间交替。如果将AlternationCoutn设置成3,将在3个不同样式之间交替。 |
ItemTemplate | 此类模板从绑定的对象提取合适的数据,并将提取的数据安排到合适的控件组合中。 |
ItemTemplateSelector | 使用代码为列表中的的每个项选择模板的DataTemplateSelector对象。可以通过这个属性为不同的项应用不同的模板,必须创建自定义的DataTemplateSelector类。 |
ItemsPanel | 定义用于包含列表中项的面板,所有封装器都会添加到这个容器中。 |
GroupStyle | 如果正在使用分组,这个样式定义了应当如何格式化每个分组。 |
GroupStyleSelector | 使用代码为每个分组选择样式的styleSelector对象。 |
4、列表样式。
4.1)、ItemContainerStyle。
WPF的数据绑定系统自动生成列表项对象,因此,为单个项应用所希望的样式是不易的,解决方案是ItemContainerStyle属性。如果设置了ItemContainerStyle属性,当创建列表项时,列表控件会将其向下传递给每个项。对于ListBox控件,每个项由ListBoxItem对象表示。对于ComboBox控件,每个项由ComboBoxItem对象表示。因此,通过ListBox.ItemContainerStyle属性应用的任何样式都将用于设置每个ListBoxItem对象的属性。
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName">
<!--为ListBox设置样式-->
<ListBox.ItemContainerStyle>
<!--指定类型是ListBoxItem-->
<Style TargetType="{x:Type ListBoxItem}">
<!--设置属性-->
<Setter Property="Foreground" Value="YellowGreen"></Setter>
<Setter Property="FontFamily" Value="新宋体"></Setter>
<Setter Property="Margin" Value="5"></Setter>
<Setter Property="Padding" Value="5"></Setter>
<!--设置模板,否则设置背景颜色没效果-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="{ TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
TextBlock.Foreground="{TemplateBinding Foreground}"
VerticalAlignment="{TemplateBinding VerticalAlignment}">
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
添加触发器:
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName">
<!--为ListBox设置样式-->
<ListBox.ItemContainerStyle>
<!--指定类型是ListBoxItem-->
<Style TargetType="{x:Type ListBoxItem}">
<!--设置属性-->
<Setter Property="Foreground" Value="YellowGreen"></Setter>
<Setter Property="FontFamily" Value="新宋体"></Setter>
<Setter Property="Margin" Value="5"></Setter>
<Setter Property="Padding" Value="5"></Setter>
<!--设置模板,否则设置背景颜色没效果-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="{ TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
TextBlock.Foreground="{TemplateBinding Foreground}"
VerticalAlignment="{TemplateBinding VerticalAlignment}">
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!--设置触发器。-->
<Style.Triggers>
<!--如果选中了ListBoxItem项-->
<Trigger Property="IsSelected" Value="True">
<!--设置以下属性。-->
<Setter Property="Background" Value="DarkRed"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="BorderThickness" Value="5"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
5、交替条目样式。
格式化列表的一种常用方式是使用交替行格式化,换句话说,用于区分列表项中每两项的的一套格式特征。WPF通过两个属性为交替项提供了内置支持:AlternationCount和AlternationIndex。其中,AlternationCount用于指定序列中项的数量,经过该数量的项之后交替样式,默认情况下,AlternationCount被设置成0,而且不适用交替格式。假设将AlternationCount设置成2,第1个ListBoxItem将获得值为0的AlternationIndex,第2个将获得值为1的AlternationIndex,第3个将获得值为0的AlternationIndex,第4个将获得值为1的AlternationIndex。使用技巧是使用触发器的ItemContainerStyle中检查AlternationIndex值并应相应改变格式。
<!--设置AlternationCount属性值为2-->
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName" AlternationCount="2">
<!--为ListBox设置样式-->
<ListBox.ItemContainerStyle>
<!--指定类型是ListBoxItem-->
<Style TargetType="{x:Type ListBoxItem}">
<!--设置属性-->
<Setter Property="Foreground" Value="YellowGreen"></Setter>
<Setter Property="FontFamily" Value="新宋体"></Setter>
<Setter Property="Margin" Value="5"></Setter>
<Setter Property="Padding" Value="5"></Setter>
<!--设置模板,否则设置背景颜色没效果-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="{ TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
TextBlock.Foreground="{TemplateBinding Foreground}"
VerticalAlignment="{TemplateBinding VerticalAlignment}">
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!--设置触发器。-->
<Style.Triggers>
<!--如果选中了ListBoxItem项-->
<Trigger Property="IsSelected" Value="True">
<!--设置以下属性。-->
<Setter Property="Background" Value="DarkRed"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="BorderThickness" Value="5"></Setter>
</Trigger>
<!--设置AlternationIndex=1的ListBoxItem的触发器-->
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<!--设置以下属性。-->
<Setter Property="Background" Value="LightBlue"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
效果图:
6、样式选择器。
在之前的案例中,已经看到了如何根据项的选择状态在列表中的位置改变样式,然而,可能需要更多的其他条件,依赖于您提供的数据而不是依赖于存储所提供数据的ListBoxItem容器的标准。为处理这种情况,需要一种为不同的项提供完全不同样式的方法,这个时候需集成StyleSelector类,并实现SelectStyle()方法。
定义StyleSelectorTest类:
//样式选择器的主要步骤:
//1、继承StyleSelector类。
//2、实现SelectStyle方法。
//3、用反射的形式判断并返回样式。
public class StyleSelectorTest:StyleSelector
{
/// <summary>
/// 默认样式。
/// </summary>
public Style DefaultStyle { get; set; }
/// <summary>
/// 突出的样式。
/// </summary>
public Style HighLightStyle { get; set; }
public string PropertyToEvaluate { get; set; }
public string PropertyToHighLight { get; set; }
public override System.Windows.Style SelectStyle(object item, System.Windows.DependencyObject container)
{
//转换成Product对象。
Models.Product product = (Models.Product)item;
//用反射法进行检测。、
Type type = product.GetType();
PropertyInfo propertyInfo = type.GetProperty(PropertyToEvaluate);
//基于属性值决定产品是否应突出显示。
if (propertyInfo.GetValue(product, null).ToString() == PropertyToHighLight)
{
return HighLightStyle;
}
else
{
return DefaultStyle;
}
}
}
在资源中定义要用到的样式:
<Window.Resources>
<!--默认样式-->
<Style x:Key="DefaultStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="LightYellow"></Setter>
<Setter Property="Padding" Value="2"></Setter>
</Style>
<!--突出样式-->
<Style x:Key="HighLightStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="LightSteelBlue"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
<Setter Property="Padding" Value="2"></Setter>
</Style>
</Window.Resources>
引用(创建定义StyleSelectorTest类的实例时,要为其指定两个样式):
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName">
<ListBox.ItemContainerStyleSelector>
<local:StyleSelectorTest DefaultStyle="{StaticResource defaultStyle}" HighlightStyle="{StaticResource highlightStyle}" PropertyPrice="5"></local:StyleSelectorTest>
</ListBox.ItemContainerStyleSelector>
</ListBox>
7、数据模板。
样式提供了基本的格式化能力,但它们不能消除到目前为止看到的列表的最重要的局限性:不管如何修改ListBoxItem,他都只是ListBoxItem,而不是功能更强大的元素组合。并且因为每个ListBoxItem只支持单个的绑定字段(因为是通过DisplayMemberPath属性进行设置),所以不可能实现包含多个字段或图像的富列表。然而,WPF有另一个工具可突破这个相当大的限制,允许组合使用来自绑定对象的多个属性,那就是数据模板:数据模板是一块定义如何显示绑定的数据对象的XAML标记,有两种类型的控件支持数据模板。
a)、内容控件通过【ContentTemplate】属性支持数据模板。内容模板用于显示任何放置在Content属性中的内容。
b)、列表控件(继承自ItemControl类的控件),通过【ItemTemplate】属性支持数据模板。这个模板用于显示作为ItemsSource提供的集合中的每个项(或来自DataTable的某一行)。
基于列表的模板特性实际上以内容控件模板为基础。这是因为列表中的每个项均由内容控件封装,例如用于ListBox控件的ListBoxItem元素,不管列表的ItemTemplate属性指定什么样的模板,模板都被用作列表中每项的ContentTemplate模板。数据模板是一块普通的XAML标记,与其他XAMl标记一样,数据模板可包含任意元素的组合,还应包含一个或多个绑定表达式。
7.1)、使用数据模板。
<!--用DisplayMemberPath属性设置-->
<!--<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName">-->
<!--用数据模板设置-->
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ProName}"></TextBlock>
<!--可放label-->
<!--<Label Content="{Binding Path=ProName}"></Label>-->
<!--可放TextBox-->
<!--<TextBox Text="{Binding Path=ProName}"></TextBox>-->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
当通过设置ItemsSource属性将列表绑定到产品集合时,为每个Product对象创建一个ListBoxItem对象,ListBoxItem.Content属性被设置为恰当的Product对象,并且ListBoxItem.ContentTemplate属性被设置为在上面显示的数据模板,改模板从Product.ProName属性提取值,并在TextBlock元素中显示提取到的值。
7.2)、用数据模板,绑定多个属性:
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20">
<ListBox.ItemTemplate>
<!--使用数据模板-->
<DataTemplate>
<!--数据模板中放置Border元素,Border元素中放置Grid-->
<Border CornerRadius="5" Margin="5" BorderThickness="1" BorderBrush="SteelBlue">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ProName}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ProPrice, StringFormat={}单价是:{0:C}}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
7.3)、分离和重用模板。
所谓的分离和重用模板,就是将模板在资源中,重复使用。
在资源中定义:
<Window.Resources>
<!--设置数据模板-->
<DataTemplate x:Key="ListItemDateTemplate">
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ProName}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ProPrice}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
调用:
<!--通过ItemTemplate属性应用模板,应用模板有两种方式:ContentTemplate和ItemTemplate-->
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" ItemTemplate="{StaticResource ListItemDateTemplate}">
7.4)、自动重用相同的模板。
导入名称空间:
xmlns:modelProduct="clr-namespace:Models;assembly=Models"
在资源中定义数据模板:
<Window.Resources>
<!--要设置DataType属性,此模板用于窗口中任何绑定到Product对象的列表控件或内容控件,而不需要指定ItemTemplate设置-->
<DataTemplate DataType="{x:Type modelProduct:Product}">
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ProName}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ProPrice}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20"> </ListBox>
8、使用更高级的模板。
说白了就是在数据模板中添加Image元素,然后通过值转换器的形式获取图片路径。
在资源中定义模板:
<Window.Resources>
<!--图片转换器-->
<local:ImagePathConverter x:Key="ImagePathConverter1"></local:ImagePathConverter>
<DataTemplate DataType="{x:Type modelProduct:Product}">
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ProName}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ProPrice}"></TextBlock>
<!--添加Image元素-->
<Image Grid.Row="2" Width="80" Height="80" Source="{Binding Path=ProImagePath, Converter={StaticResource ImagePathConverter1}}"></Image>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
另一种效果:说白了就是加一个Button元素,然后为其绑定Click事件,并设置Tag属性。
Xaml代码:
<Window.Resources>
<DataTemplate DataType="{x:Type modelProduct:Product}">
<Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ProName}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ProPrice}"></TextBlock>
<!--设置Click事件,为Tag属性赋值。-->
<Button Click="Button_Click" Grid.Row="0" Grid.RowSpan="2"
Width="50" Tag="{Binding ProId}"
HorizontalAlignment="Right"
Content="{Binding ProId}">
</Button>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
Click事件:
private void Button_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
MessageBox.Show(btn.Tag.ToString());
}
9、改变模板。
使用到目前为止,只能为整个列表使用一个模板,许多情况下,希望采用不同方式灵活地表示不同的数据。可以通过以下几种方式进行实现:
a)、使用数据触发器。可根据绑定的数据对象中的属性值使用触发器修改模型中的属性。
b)、使用值转换器。实现了IValueConverter接口的类,能够将值从绑定的对象转换为可用于设置模块中与格式化相关的属性的值。
c)、使用模板选择器。模板选择器检查绑定的数据对象,并在几个不同模板之间进行选择。
9.1)、模板选择器。
新建DataTemplateSelectorTest类:
//模板选择器实现步骤:
//1、新建1个类,继承自DataTemplateSelector类。
//2、实现SelectTemplate方法。
//3、根据业务逻辑进行判断。
public class DataTemplateSelectorTest : DataTemplateSelector
{
//默认模板。
public DataTemplate DefaultTemplate { get; set; }
//突出模板。
public DataTemplate HighlightTemplate { get; set; }
//设置的产品价格。
public decimal PropertyPrice { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
//获取Product对象。
Models.Product product = (Models.Product)item;
if (product.ProPrice >= PropertyPrice)
{
//如果商品的价格>=设置的价格,就返回突出的模板。
return HighlightTemplate;
}
else
{
//如果商品的价格<设置的价格,就返回默认的模板。
return DefaultTemplate;
}
}
}
在资源中定义模板:
<Window.Resources>
<!--默认模板-->
<DataTemplate x:Key="DefaultTemplate">
<Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue" CornerRadius="5">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ProName}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding Path=ProPrice}"></TextBlock>
<TextBlock></TextBlock>
</Grid>
</Border>
</DataTemplate>
<!--突出模板-->
<DataTemplate x:Key="HighlightTemplate">
<Border Margin="5" BorderThickness="1" BorderBrush="LightYellow" CornerRadius="5">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" FontWeight="Bold" Text="{Binding Path=ProName}"></TextBlock>
<TextBlock Grid.Row="1" FontWeight="Bold" Text="{Binding Path=ProPrice}"></TextBlock>
<TextBlock Grid.Row="2" FontStyle="Italic" HorizontalAlignment="Right">价钱大于5的模板</TextBlock>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
引用模板(通过ItemTemplateSelector属性进行引用):
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20">
<ListBox.ItemTemplateSelector>
<!--设置默认值和突出值-->
<local:DataTemplateSelectorTest DefaultTemplate="{StaticResource DefaultTemplate}"
HighlightTemplate="{StaticResource HighlightTemplate}"
PropertyPrice="5"> <!--设置价格值-->
</local:DataTemplateSelectorTest>
</ListBox.ItemTemplateSelector>
</ListBox>
10、模板与选择。
资源中定义模板:
<DataTemplate x:Key="DataTemplateChoose">
<Grid Margin="5" Background="White">
<Border CornerRadius="5" BorderThickness="1" BorderBrush="SteelBlue" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}}}">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ProName}"></TextBlock>
<TextBlock Grid.Row="1" FontWeight="Bold" Text="{Binding Path=ProPrice}"></TextBlock>
</Grid>
</Border>
</Grid>
</DataTemplate>
<!--设置模板-->
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" ItemTemplate="{StaticResource DataTemplateChoose}">
<ListBox.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0"></Setter>
<!--设置触发器-->
<Style.Triggers>
<Trigger Property="ListBoxItem.IsSelected" Value="true">
<Setter Property="ListBoxItem.Background" Value="DarkRed"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
11、为ComboBox控件绑定。
xaml代码:
<ComboBox Name="combox1" Height="50" DisplayMemberPath="ProName"></ComboBox>
后台代码:
Bll.ProductBll productBll = App.ProductBll;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.combox1.ItemsSource = productBll.GetCollectionProduct();
}
End!