65.9K
CodeProject 正在变化。 阅读更多。
Home

Coppock 图表

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.25/5 (4投票s)

2010年5月25日

CPOL

7分钟阅读

viewsIcon

38721

downloadIcon

1310

本文演示了如何构建一个基于 Web 的交互式图表,并尝试将 Visual Studio 2010 和 .NET Framework 4 中出现的一些最新更新和最佳实践融入到我的软件知识体系中。

引言

跟上新的工具、技术和方法是一项对软件开发人员的挑战,需要付出专门的努力。本文演示了如何构建一个基于 Web 的交互式图表,并尝试将 Visual Studio 2010 和 .NET Framework 4 中出现的一些最新更新和最佳实践融入到我的软件知识体系中。

要求

本文的图表是对 ASX(澳大利亚证券交易所)的 Coppock 指标的可视化。Coppock 指标为投资者提供了一个信号,表明熊市已经结束,是时候开始建立对优质公司的长期头寸了。该图表将集成到一个讨论股票投资风险管理的网页中。Coppock 指标有超过 100 年的数据可供使用,因此该图表的交互性将是能够过滤其范围的能力。

CoppockChart.png

图表基础知识

图表是数学中最有趣的部分之一。它们以一目了然的视觉方式显示了变量之间的关系。Coppock 指标的变量是日期和相应的数值。日期被识别为自变量(其值不受其他因素影响),数值被识别为因变量(受股票随时间平均价格影响)。通常将自变量显示为水平(X)轴,因变量显示为垂直(Y)轴。最适合 Coppock 指标的图表是点对点图,因为 Coppock 指标是在每个月的最后一天计算的。

选择技术

我的背景是使用 C# 和 .NET Framework 进行企业开发,并且我一直想尝试最新的 Visual Studio 2010。那么技术选择就取决于要定位的平台(Web、桌面或移动设备)才能覆盖到目标用户。我最初的研究始于 Silverlight,因为它能够通过 Silverlight Toolkit 中丰富的组件库来定位所有平台(包括 Windows Phone 7 的移动设备)。另一个考虑的选项是 HTML 和 JavaScript 的 Web 标准平台(JQuery 具有图表组件)。Web 标准平台的工具轻量级,并且在针对在线用户时,可能是理想的选择。作为一名企业开发人员,我想专注于面向广泛受众(客户、外部业务合作伙伴和企业内部用户)的学习。我选择构建 Coppock 指标图表的技术是 Silverlight 4 和 ASP.NET,它将展示 Silverlight 将丰富的用户体验封装到可以集成到网页中的组件中的能力。您可以从 Silverlight 网站下载编译和运行 Coppock 图表应用程序所需的工具。

Silverlight 的图表组件

Silverlight Toolkit 包含图表组件,这些组件在设计时提供支持,并在运行时底层数据源发生变化时进行动态更新。这些图表控件的行为与标准的 Silverlight 控件一致,例如,它们在 XAML 中是声明式定义的,如果需要达到期望的布局,可以对其进行样式设置或完全重新模板化。图表组件还提供了可扩展性点;例如,如果现有功能不满足要求,图表的各个部分可以用自定义构建的组件替换。注意:Silverlight Toolkit 中的图表组件仍处于“预览版”阶段,在添加到 Silverlight SDK 之前可能会发生更改。

声明式用户界面

XAML 是 Silverlight 的一个基本组成部分。它确保了用户界面布局和应用程序逻辑之间的关注点分离,因为在 XAML 中,只能声明界面组件及其交互(通过触发器)。业务逻辑必须在代码中指定,并通过数据绑定到属性、事件和命令来连接到 XAML 中的元素。XAML 可以被认为是声明式编程,因为每个元素都对应于 .NET Framework 中的一个类或您的应用程序中的一个类。

<chartingToolkit:Chart x:Name="coppockChart"
     Title="{Binding ChartTitle}"
     Margin="10,5,15,5"
     Style="{StaticResource chartStyle}">
     <chartingToolkit:LineSeries Title="Coppock Indicator"
         DependentValuePath="Value"
         IndependentValuePath="Date"
         ItemsSource="{Binding Source={StaticResource coppockDataView}}"
         DataPointStyle="{StaticResource simpleLineDataPointStyle}"
         LegendItemStyle="{StaticResource legendItemStyle}"
         Loaded="LineSeries_Loaded">
     </chartingToolkit:LineSeries>
     <chartingToolkit:Chart.Axes>
        <chartingToolkit:LinearAxis Orientation="Y"
             Minimum="{Binding AxisYMinimum}"
             Maximum="{Binding AxisYMaximum}"
             Interval="{Binding AxisYInterval}"
             ShowGridLines="True"
             Location="Right" />
         <chartingExtensions:EndOfMonthAxis Orientation="X"
             Title=""
             ShowGridLines="False"
             Maximum="{Binding AxisXMaximum}"
             Interval="{Binding AxisXInterval}"
             IntervalType="{Binding AxisXIntervalType}"
             AxisLabelStyle="{StaticResource axisLabelStyle}">
         </chartingExtensions:EndOfMonthAxis>
    </chartingToolkit:Chart.Axes>
</chartingToolkit:Chart>

数据绑定和 MVVM

Silverlight 中的数据绑定机制确保了用户界面和底层代码之间的修改值是同步的;例如,当 Coppock 图表的日期范围改变时,图表标题会更新以显示新的日期值。实现 INotifyPropertyChanged 接口的代码类可以参与此数据绑定机制。

Title="{Binding ChartTitle}"
代码
/// <summary>
/// Gets ChartTitle.
/// </summary>
public string ChartTitle
{
    get
    {
        return this.chartTitle;
    }

    private set
    {
        this.chartTitle = value;
        this.NotifyPropertyChanged("ChartTitle");
    }
}

Silverlight 编程中出现的一个重要最佳实践是 MVVM(模型-视图-视图模型)设计模式。此模式鼓励将业务逻辑代码与直接操作视图的代码分开。ViewModel 类可以在 XAML 中静态初始化。

<UserControl.Resources>
    <viewModels:CoppockViewModel x:Key="coppockViewModel"
        SeriesChanged="ViewModel_SeriesStartChanged" />
</UserControl.Resources>

然后,通过 DataContext 属性将 ViewModel 提供给 XAML 中的所有元素。

DataContext="{Binding Source={StaticResource coppockViewModel}}"

使图表交互化

单击 Coppock 图表的日期范围 HyperlinkButton 将执行一个命令,该命令会过滤图表的数据源,以反映所选的日期范围。应用于 HyperlinkButtonCommand 属性的绑定将其连接到 ViewModel 类中位于 ChangeSeriesStart DelegateCommand。

<HyperlinkButton x:Name="TenYearSeriesButton"
        Content="10yr"
        HorizontalContentAlignment="Center"
        Command="{Binding ChangeSeriesCommand}"
        CommandParameter="{Binding TenYearSeriesCommandParameter}"
        Width="30" />

ChangeSeriesStart DelegateCommand 初始化时,会为其 Action 属性分配一个事件处理程序,该处理程序在 HyperlinkButton 被单击时执行。

this.ChangeSeriesCommand = new DelegateCommand(this.ChangeSeriesStart, 
                                               this.CanChangeSeriesStart);

通过 CollectionViewSource 类型实现了按日期范围过滤图表的功能。CollectionViewSource 包装了 CoppockChart 的实际 DataSource,并公开了一个在发生数据绑定时被隐式使用的 View 属性。当 CollectionViewSource 刷新时,它会执行 Filter 事件,该事件允许检查 DataSource 中的每个项,以确定它是否应包含在 View 中。数据绑定可确保对 CollectionViewSource View 的此更改会反映在图表中。

<Grid.Resources>
    <CollectionViewSource x:Key="coppockDataView"
        Source="{Binding CoppockData.SeriesData}"
        Filter="SeriesData_Filter"/>
</Grid.Resources>
代码
/// <summary>
/// Handle SeriesData_Filter event.
/// </summary>
/// <param name="sender">sender parameter</param>
/// <param name="e">FilterEventArgs parameter</param>
private void SeriesData_Filter(object sender, FilterEventArgs e)
{
    if (e.Item != null)
    {
        e.Accepted = this.ViewModel.IncludeInSeries(((DataItem)e.Item).Date);
    }
}

定位信号标记

Coppock 指标的目的是提供一个指示熊市何时结束的信号。确定此信号的算法查找值为负但大于上个月值的 DataItemLinkedList 用作底层数据源,因为需要检查前一个项。当找到代表 Coppock 指标信号的 DataItem 时,会在图表上覆盖一个 Canvas 上绘制一条线和一个带有 ToolTip 的椭圆(此技术在另一篇 文章 中有所描述)。

/// <summary>
/// Handle ViewModel_SeriesStartChanged event.
/// </summary>
/// <param name="sender">sender parameter</param>
/// <param name="e">SeriesStartChangedEventArgs parameter</param>
private void ViewModel_SeriesStartChanged(object sender, SeriesStartChangedEventArgs e)
{
    this.DataView.View.Refresh();

    this.ChartAreaCanvas.Children.Clear();                
    this.DrawSignalMarkers();            
}

图表的可扩展性

构成图表的每个组件(SeriesAxis)都是一个控件。这意味着可以应用样式,修改模板,或者从控件派生,甚至完全用自定义组件替换它。为了正确显示 Coppock 指标数据,水平 X 轴需要月末值。Silverlight Toolkit 提供的 DateTimeAxis 不支持此场景,但通过创建一个派生自标准 DateTimeAxis 的类,可以重写 GetMajorAxisValues 方法并修改显示的 DateTime 值。

/// <summary>
/// EndOfMonth Axis control.
/// </summary>
public class EndOfMonthAxis : DateTimeAxis
{
    /// <summary>
    /// Get MajorAxisValues.
    /// </summary>
    /// <param name="availableSize">availableSize parameter</param>
    /// <returns>IEnumerable of DateTime</returns>
    protected override IEnumerable<DateTime> GetMajorAxisValues(Size availableSize)
    {           
        // Roll each date value back one day i.e. to the last day of the month.
        return base.GetMajorAxisValues(availableSize).
            Select(date => date.AddDays(-1));
    }
}

然后将样式应用于 AxisLabelStringFormat 属性来格式化日期。

<Style x:Key="axisLabelStyle" TargetType="chartingToolkit:AxisLabel">
    <Setter Property="StringFormat" Value="{}{0:MMM yy}" />
</Style>

与 HTML 页面的集成

此 Coppock 指标图表强调 Silverlight 作为网页组件的一部分。我认为这需要 Silverlight 应用程序能够被其托管页面配置;也就是说,当网页在浏览器中呈现时,我想对 Silverlight 组件执行自定义初始化。初始化参数在托管 Silverlight 应用程序的 object 标签的 initParams 成员中指定为名称-值对。可以在 initParams 值中指定任意数量的名称-值对(逗号分隔)。Silverlight 插件对象参考 提供了与 HTML 页面的集成完整参考。

<name="initParams" value="Chart=Coppock" />

Application_Startup 事件的 StartupEventArgs 中可以提取这些参数。

/// <summary>
/// Handle Application_Startup event.
/// </summary>
/// <param name="sender">sender parameter</param>
/// <param name="e">StartupEventArgs parameter</param>
private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new MainView(e.InitParams);                     
}

使用 Keys 属性迭代参数。

foreach (var key in this.InitParams.Keys)
{
    var param = this.InitParams[key];
     ...
}

为 Silverlight 制定业务案例

Silverlight 是一种基于 .NET Framework 的富客户端技术,可以使用 Visual Studio 提供的丰富的调试环境来定位 Web、桌面和移动平台。Silverlight 使我们更接近于单一代码库支持多个平台的理念。本文讨论了 Silverlight 作为网页组件在 HTML 和 JavaScript 集成方面的能力。这种使用 Silverlight 的方法为 SAAS(软件即服务)打开了大门,其中 Silverlight 应用程序可以独立托管,例如使用 Windows Azure Platform,并通过指定 Web 地址集成到网站中。这使得企业可能希望将其应用程序功能暴露给业务合作伙伴,或者希望消耗外部构建的组件并将其集成到基于 Web 的应用程序中的场景成为可能。

参考文献

历史

  • 2010 年 5 月 29 日:版本 1.0(初始版本)。
© . All rights reserved.