金融类应用程序示例 > 自定义单元格 |
如果你现在运行这个示例,grid已经工作并按照期望更新数据。您可以更改更新参数以使其更或更少的频繁,在更新过程中滚动grid,等等。
然而,虽然更新正在发生,他们很难理解。有太多的数字在屏幕上随机位置刷新。
自定义单元格通过闪烁以及sparkline提供了一个更好的用户体验。
当单元格包含的值发生改变时,闪烁动画将临时地改变单元格的背景。如果一个数值增加,该单元格将在瞬间变成绿色,然后逐渐的变淡,直至恢复成白色。
Sparkline是显示在每一个单元格中的微型图表。该微型图表展示了该单元格的最后五个值,因此用户可以立即识别出数值变化的趋势(该值在上升,下降,或者持平)。
要使用自定义单元格,我们继续在上一示例中进行修改。我们首先从创建一个FinancialCellFactory类并指定该类的一个实例至grid的CellFactory属性开始。
C# |
拷贝代码
|
---|---|
// 使用自定义单元格工厂 _flexFinancial.CellFactory = new FinancialCellFactory(); // 自定义单元格工厂定义 public class FinancialCellFactory : CellFactory{ public override void CreateCellContent(C1FlexGrid grid, Border bdr, CellRange range) { // 获取单元格信息 var r = grid.Rows[range.Row]; var c = grid.Columns[range.Column]; var pi = c.PropertyInfo; // 检查这是一个我们期望的单元格 if (r.DataItem is FinancialData && (pi.Name == "LastSale" || pi.Name == "Bid" || pi.Name == "Ask")) { // 创建 StockTicker 元素并添加至单元格 var ticker = new StockTicker(); bdr.Child = ticker; // 绑定StockTicker 至该行的FinancialData 对象 var binding = new Binding(pi.Name); binding.Source = r.DataItem; binding.Mode = BindingMode.OneWay; ticker.SetBinding(StockTicker.ValueProperty, binding); // 添加一些信息至 StockTicker 元素 ticker.Tag = r.DataItem; ticker.BindingSource = pi.Name; } else { // 使用默认实现 base.CreateCellContent(grid, bdr, range); } } } |
我们的自定义单元格工厂首先检查该行的数据是否是FinancialData,并且该列是否绑定到数据对象的LastSale, Bid, 或者 Ask属性。如果所有这些条件都满足,单元格工厂将创建一个新的StockTicker元素并绑定到数据。
StockTicker元素将数据显示给用户。它由一个包含以下子元素的四列的Grid元素组成:
元素描述 | 类型 | 名称 |
---|---|---|
当前值 | TextBlock | _txtValue |
最后变化的百分比 | TextBlock | _txtChange |
上/下图标 | Polygon | _arrow |
Sparkline | Polyline | _sparkLine |
这些元素在StockTicker.xaml文件中定义,我们这里不列举完整的清单。
StockTicker.xaml文件中最有趣的部分是用来实现闪烁行为脚本的定义。Storyboard逐渐地改变控件的背景色从当前值至透明:
XAML |
拷贝代码
|
---|---|
<UserControl.Resources> <Storyboard x:Key="_sbFlash" > <ColorAnimation Storyboard.TargetName="_root" Storyboard.TargetProperty= "(Grid.Background).(SolidColorBrush.Color)" To="Transparent" Duration="0:0:1" /> </Storyboard></UserControl.Resources> |
StockTicker的实现代码包含在StockTicker.cs 文件中。有趣的部分注释如下:
C# |
拷贝代码
|
---|---|
/// <summary> /// Interaction logic for StockTicker.xaml /// </summary> public partial class StockTicker : UserControl { public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(double), typeof(StockTicker), new PropertyMetadata(0.0, ValueChanged)); |
我们从定义一个叫做ValueProperty的DependencyProperty开始,该属性将被用作绑定底层的数据值。Value属性实现如下:
C# |
拷贝代码
|
---|---|
public double Value { get { return (double)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ticker = d as StockTicker; var value = (double)e.NewValue; var oldValue = (double)e.OldValue; |
为了实现sparkline,控件需要访问除了当前值和上一个值之外更多的值。这将通过在控件的Tag属性保存FinancialData对象完成,接下来调用FinancialData.GetHistory方法。
实际上,由事件的OldValue属性提供的前一个数值在这种情况下无论如何是靠不住的。这是由于grid将虚拟化显示单元格,StockTicker.Value可能会发生变化,因为控件由于滚动到可见区域刚刚被创建。在这种情况下,控件没有以前的值。从FinancialData对象获取之前的值也需要考虑这个问题。
C# |
拷贝代码
|
---|---|
// 获取历史数据 var data = ticker.Tag as FinancialData; var list = data.GetHistory(ticker.BindingSource); if (list != null && list.Count > 1) { oldValue = (double)list[list.Count - 2]; } |
一旦这些值可用,控件将计算以百分比表示的上一次改变的变化率,并更新控件文本:
C# |
拷贝代码
|
---|---|
// 计算以百分比表示的变化率 var change = oldValue == 0 || double.IsNaN(oldValue) ? 0 : (value - oldValue) / oldValue; // 更新文本 ticker._txtValue.Text = value.ToString(ticker._format); |
百分比表示的变化率也用来更新up/down符号以及文本和闪烁的颜色。如果没有变化,则该up/down符号将被隐藏,并且文本被设置为默认颜色。
如果变化是负向的,则代码将通过设置其ScaleY变换为-1,使得up/down符号向下显示,同时设置符号的颜色,以及闪烁动画的颜色为红色。
如果变化是正向的,则代码将通过设置其ScaleY变换为+1,使得up/down符号向上显示,同时设置符号的颜色,以及闪烁动画的颜色为绿色。
C# |
拷贝代码
|
---|---|
// 更新符号和闪烁动画的颜色 var ca = ticker._flash.Children[0] as ColorAnimation; if (change == 0) { ticker._arrow.Fill = null; ticker._txtChange.Foreground = ticker._txtValue.Foreground; } else if (change < 0) { ticker._stArrow.ScaleY = -1; ticker._txtChange.Foreground = ticker._arrow.Fill = _brNegative; ca.From = _clrNegative; } else { ticker._stArrow.ScaleY = +1; ticker._txtChange.Foreground = ticker._arrow.Fill = _brPositive; ca.From = _clrPositive; } |
下一步,代码通过使用由早先调用FinancialData.GetHistory方法提供的历史值数组更新sparkline多边形的Points属性,以更新sparkline。sparkline的Stretch属性被设置为Fill,因此线形将自动缩放以适应可用的空间大小。
C# |
拷贝代码
|
---|---|
// 更新sparkline if (list != null) { var points = ticker._sparkLine.Points; points.Clear(); for (int x = 0; x < list.Count; x++) { points.Add(new Point(x, (double)list[x])); } } |
最后,如果值真的发生了变化,而且控件不是刚刚被创建,则代码通过调用StoryBoard.Begin方法闪烁单元格
C# |
拷贝代码
|
---|---|
// 闪烁新值(但是不能是控件创建之后立刻就刷新) if (!ticker._firstTime) { ticker._flash.Begin(); } ticker._firstTime = false; } |
总结一下StockTicker控件。如果您运行该示例应用程序,并选择“自定义单元格”复选框,您将立即看到一个具有更多信息显示的界面,单元格将在数值发生变化时闪烁显示,同时sparkline提供一个关于数值变化趋势的快速指示。