(DataTemplate)
Yang-Big
在WPF中如果我们想要按照自己的想法来为数据制定显示方式,也就是说,数据类型是一定的,但我们可以让它有多种多样的表现方式,比如一个具体的时间,一般使用一个字符串(例如“12:03”)来显示,但我们为什么就不能显示为一个时钟或者其他你想要的表现形式来呈现你的数据。WPF中的数据模板技术应运而生。
数据模板适用于Content Control类控件与Items Control类控件。
最近项目中做的一个例子:
一个简单的类
public class ServicePriceInfo
{
public static ServicePriceInfo ParseFromJson(JToken jData)
{
ServicePriceInfo data = new ServicePriceInfo();
data.ID = jData.GetIntValue("ID").Value;
data.AuthType = jData.GetIntValue("AuthType").Value;
data.AuthTitle = jData.GetValue("AuthTitle");
data.Price = jData.GetFloatValue("Price").Value;
data.SourcePrice = jData.GetFloatValue("SourcePrice").Value;
data.PurchaseType = jData.GetValue("PurchaseType");
return data;
}
public int ID
{
get;
set;
}
public int AuthType
{
get;
set;
}
public string AuthTitle
{
get;
set;
}
public float Price
{
get;
set;
}
public float SourcePrice
{
get;
set;
}
public string PurchaseType
{
get;
set;
}
public override string ToString()
{
return AuthTitle + ":" + Price;
}
}
这个类简单的表示软件授权的相关信息,支付类型,金额等
这里目的是要做一个按钮控件来呈现
ServicePriceInfo数据
当然我们可以用一个ListBox来呈现由多个相同信息组成的列表,在
.net 3.0以前我们可能就只能用文本来列出这些而已,或者花不少的精力来重写列表控件以便在列表中绚丽的显示.
用数据模板来实现如下:
由于该项目没有使用什么框架,所以使用ListBox会略显麻烦,这里直接使用StackPanel来作为容器
<StackPanel Name="Panel_Price" Grid.Row="1" Grid.ColumnSpan="2" Margin="0,5">
</StackPanel>
这里在后台把按钮放置在
StackPanel容器内。
foreach (ServicePriceInfo sp in sps)
{
Button bt = new Button();
bt.Content = sp;
bt.Click += Bt_Click;
this.Panel_Price.Children.Add(bt);
if (idx < itemCount-1)
{
Line l = new Line();
//l.Margin = new Thickness(10, 5, 10, 5);
//l.X1 = 0;
//l.X2 = 450;
//l.Y1 = 0;
//l.Y2 = 0;
//l.Stretch = Stretch.Fill;
//l.Stroke = Brushes.WhiteSmoke;
//l.StrokeThickness = 1;
//l.StrokeDashArray = new DoubleCollection() { 5, 2 };
//l.StrokeDashCap = PenLineCap.Round;
this.Panel_Price.Children.Add(l);
}
idx++;
}
然后写一个按钮控件样式,把按钮的
ContentTemplate 引用数据模板,按钮样式如下:
<Style TargetType="Button">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Margin" Value="0,5"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinWidth" Value="20"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ContentTemplate" Value="{StaticResource DataTemplate_ServicePriceInfo}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="Root" Background="{TemplateBinding Background}">
<Border x:Name="backborder2" Background="{DynamicResource {x:Static RGBCtrl:RGBGlobalResource.MainBrushKey}}" BorderThickness="0" Opacity="0"/>
<Border x:Name="backborder" Background="Transparent" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}"/>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="Root" Value="0.4"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="backborder" Value="{DynamicResource {x:Static RGBCtrl:RGBGlobalResource.MainBrushKey}}"/>
<Setter Property="BorderThickness" TargetName="backborder" Value="1"/>
<Setter Property="Opacity" TargetName="backborder2" Value="0.05"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="BorderBrush" TargetName="backborder" Value="{DynamicResource {x:Static RGBCtrl:RGBGlobalResource.MainBrushKey}}"/>
<Setter Property="BorderThickness" TargetName="backborder" Value="2"/>
<Setter Property="Opacity" TargetName="backborder2" Value="0.1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
注意:
<Setter Property="ContentTemplate" Value="{StaticResource DataTemplate_ServicePriceInfo}"/>
这里使用的就是我们的数据模板了,通过
x:Key="DataTemplate_ServicePriceInfo" 来标识。
<DataTemplate x:Key="DataTemplate_ServicePriceInfo" DataType="AppCore:ServicePriceInfo">
<Grid Margin="0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding AuthTitle}" Margin="3,0,10,0" TextTrimming="WordEllipsis"/>
<TextBlock Text="{Binding SourcePrice, StringFormat=¥{0:F2}}" Grid.Column="1" Margin="10,0" Opacity="0.3" TextDecorations="Strikethrough" ToolTip="原价"/>
<TextBlock Text="{Binding Price, StringFormat=¥{0:F2}}" Grid.Column="2" Margin="10,0" Foreground="{DynamicResource {x:Static RGBCtrl:RGBGlobalResource.MainBrushKey}}" FontWeight="Bold" ToolTip="优惠价"/>
<Border Margin="10,0" Grid.Column="3" HorizontalAlignment="Right" BorderThickness="0" CornerRadius="10" Background="{DynamicResource {x:Static RGBCtrl:RGBGlobalResource.MainBrushKey}}">
<TextBlock Text="{Binding PurchaseType}" Foreground="White" Margin="10,0" VerticalAlignment="Center" TextAlignment="Center" FontSize="12" />
</Border>
</Grid>
</DataTemplate>
来分析一下数据模板 DataTemplate_ServicePriceInfo :
1, DataType="AppCore:ServicePriceInfo" 指定要展示的数据类型,当然前提是要引用数据所在的命名空间
xmlns:AppCore="clr-namespace:Camew.Lottery.App;assembly=Camew.Lottery.App.Core"
2,我们将模板定义为窗口的资源,资源保存在一个资源字典中的,x:Key="DataTemplate_ServicePriceInfo"表示其在资源字典中的键,DataType="AppCore:ServicePriceInfo"表示该数据模板针对的数据类型是AppCore名字空间下的ServicePriceInfo类,接下来在Gird中我们定义了该数据模板的视觉树,这也是我们的工作重心,即该视觉树定义了如何显示我们的数据。(将ServicePriceInfo的对应属性属性绑定到各TextBlock的Text属性上)。
注意到这个数据模板实际上在干什么:它定义了ServicePriceInfo类型对象的表现方式。
以后我们需要ServicePriceInfo对象按这种方式展示给用户的时候,我们只要将该数据模板指定给要显示ServicePriceInfo对象的那个控件就可以了。
<Setter Property="ContentTemplate" Value="{StaticResource DataTemplate_ServicePriceInfo}"/>
这样我们的按钮控件就按照
DataTemplate_ServicePriceInfo定义的方式来显示了。
效果如下: