Page tree
Skip to end of metadata
Go to start of metadata

本节介绍了一个在金融行业中应用的示例应用程序。财务应用程序通常依赖于大量的数据,通常是从强大的、专用的服务器中获得的。
在这些应用中的信息的实时性要求快速更新(应用程序必须跟上来自服务器的数据流)和机制,以一个清晰,有效的方式向用户传达变化。
一种常用的方法来实时显示实时的变化是使用闪烁的元素。例如,当它的值改变时,grid单元可能会闪烁不同的颜色。闪光只持续短短的时间,就足以把用户的注意力吸引到该变化来。
另一个日益流行的机制,用于在一个快速和紧凑的形式传达丰富的信息,称作sparkline。一个Sparkline是一个小型的图表,用来显示趋势以及总结信息,相对于一个长长的数据列表,这将更加清晰有效。
本节描述了一个具有数据实时更新,闪烁单元格以及Sparkline的示例金融类应用程序。在Silverlight及WPF中提供的实力突出展示了C1FlexGrid的性能。
下面的图片显示我们的示例金融应用程序的作用。该图像无法展示应用程序动态变化以展示不断变化的数据的特性,因此我们建议您有机会尝试运行该示例。

 产生数据

我们的金融类应用程序使用一个模拟实际的提供不断更新的动态数据的实际服务器。
由于我们并非金融业专家,我们就从维基百科上获得了一些启示({+}http://en.wikipedia.org/wiki/Market_data+。我们的数据源由FinancialData对象组成,用来表示典型的来自于NYSE,TSX,Nasdaq股票市场的数据信息。每个数据对象都包含这样的信息:

股票代码

IBM

 

竞标价

89.02

 

询问价

89.08

 

竞标规模

300

 

询问大小

1000

 

最终销售价

89.06

 

最终size

200

 

股票代码

 

IBM

 

报价时间

 

14:32:45

 

交易时间

 

14.32.44

 

成交量

 

7808

 

事实上,这些信息通常是一个不同数据源的聚合,如报价数据(bid, ask, bid size, ask size),以及交易数据(last sale, last size, volume)均产生自不同的数据源。
为捕捉到的数据的动态性,我们的数据源对象提供了一个具有大约4000个FinancialData对象的FinancialDataList,
以及一个计时器,按照预定的计划修改对象。调用方可以决定数值更新的频率以及单次更新发生时数值修改的范围大小。将FinancialDataList绑定到grid,并在程序运行期间修改数据源更新参数,以查看grid如何做到紧跟数据源变化。为检查数据源实现细节,请参见示例源代码中的FinancialData.cs文件。
将grid绑定至金融类数据源非常简单。我们创建了一个PagedCollectionView 做为一个中介的角色,提供常规的
Currency,排序,分组,以及筛选服务,而不是直接将grid绑定到FinancialDataList。这里是代码:

C#

// 创建数据源
var list = FinancialData.GetFinancialData(); var view = new PagedCollectionView(list);
// 绑定数据源到grid
_flexFinancial.ItemsSource = view;
和前面的示例一样,我们设置AutoGenerateColumns属性为false并使用XAML创建grid的列:

XAML

<fg:C1FlexGrid x:Name="_flexFinancial"
MinColumnWidth="10"
MaxColumnWidth="300"
AutoGenerateColumns="False" >
<fg:C1FlexGrid.Columns>
<fg:Column Binding="{Binding Symbol}" Width="100" />
<fg:Column Binding="{Binding Name}" Width="250" />
<fg:Column Binding="{Binding Bid}" Width="150" Format="n2" HorizontalAlignment="Right" />
<fg:Column Binding="{Binding Ask}" Width="150" Format="n2" HorizontalAlignment="Right" />
<fg:Column Binding="{Binding LastSale}" Width="150" Format="n2" HorizontalAlignment="Right" />
<fg:Column Binding="{Binding BidSize}" Width="100" Format="n0" HorizontalAlignment="Right" />
<fg:Column Binding="{Binding AskSize}" Width="100" Format="n0" HorizontalAlignment="Right" />
<fg:Column Binding="{Binding LastSize}" Width="100" Format="n0" HorizontalAlignment="Right" />
<fg:Column Binding="{Binding Volume}" Width="100" Format="n0" HorizontalAlignment="Right" />
<fg:Column Binding="{Binding QuoteTime}" Width="100"
Format="hh:mm:ss" HorizontalAlignment="Center" /> <fg:Column Binding="{Binding TradeTime}" Width="100"
Format="hh:mm:ss" HorizontalAlignment="Center" />
</fg:C1FlexGrid.Columns>
</fg:C1FlexGrid>

 搜索及过滤

就像iTunes用户,金融分析师通常不会对一次查看全部的数据感兴趣,因此我们需要一些过滤或者搜索机制。
一个真实的应用程序允许分析师选择查看特定的页,并有可能将这些视图保存下来,并在它们中间切换查看。我们的示例采取了一些简单的方法,简单地重用了在iTuns示例(在线文档)中描述的SearchBox控件。我们的用户可以在搜索框中输入"bank"或者"electric"以筛选数据,以替代直接选择特定的数据项。把搜索框关联到金融数据源的代码如下:

C#

// 创建数据源
FinancialDataList list = FinancialData.GetFinancialData(); var view = new PagedCollectionView(list);
// 绑定数据源到grid
_flexFinancial.ItemsSource = view;
// 关联搜索框(用户可以通过公司名称或代码搜索)
_srchBox.View = view;
var props = _srchCompanies.FilterProperties; props.Add(typeof(FinancialData).GetProperty("Name")); props.Add(typeof(FinancialData).GetProperty("Symbol"));

 自定义单元格

如果你现在运行这个示例,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

SparklinePolyline 这些元素在StockTicker.xaml文件中定义,我们这里不列举完整的清单。

_sparkLine

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提供一个关于数值变化趋势的快速指示。

  • No labels