(四)Market模块之TrendLineView分析

      这一章我们添加Market模块到RI中。本章代码下载:MyRI_1.zip

clip_image002

 

需求分析:

      要实现一个View,能根据PositionSummaryView的选取,而相应发生变化,我们将其命名为TrendLineView.xaml。这里使用到了一个现成的控件LineChart,它来自于StockTraderRI.ChartControls,我们只需要把它的ItemsSource属性与MarketHistory集合绑定在一起就可以了。另一个需要绑带的就是左上角的文字,根据PositionSummaryView中选取项的不同,而相应的改变文字,比如说STOCK2。

      此外,为了模拟真实世界中股票的时刻变化,在Prism提供的源码中,创建了一个Timer来随机产生数字,从而影响PositionSummaryView中GridView中数据的变化——这无疑增大了理解Prism框架的难度。于是,在我的程序中,暂时抛弃了这一部分,在这个系列的最后一章,会将它加上。

 

准备工作

      1. 创建Data文件夹,在其中创建两个数据文件Market.xml和MarketHistory.xml。然后打开项目的资源文件Resources.resx,选择File选项,把这两个数据文件复制过去。

clip_image004

      2. 创建Service文件夹,创建两个接口IMarketFeedService和IMarketHistoryService,从而为以后实现依赖注入做准备。

public interface IMarketFeedService
{
    decimal GetPrice(string tickerSymbol);
    long GetVolume(string tickerSymbol);
    bool SymbolExists(string tickerSymbol);
}
public interface IMarketHistoryService
{
    MarketHistoryCollection GetPriceHistory(string tickerSymbol);
} 

      可以看到,接口所提供的方法,都是和数据源进行交互。

3. 在Service文件夹中,创建两个Service,即MarketFeedService和MarketHistoryService,分别派生自上面两个接口,分别用来读取上面两个数据文件。

      下面是MarketFeedService在调试时的例子。

clip_image006

下面开始设计TrendLineView。

      1.这次我们使用的是VM模式,即只有View和Model,而把Presenter融入到Model中。同时还实现了V中控件和M的数据绑定(通过DataContext)。

      为此,要创建ITrendLineView和ITrendLinePresentationModel接口,并创建TrendLineView和TrendLinePresentationModel,分别实现上面的接口,其中,TrendLineView还是一个UserControl。

      2.此外,我们采取Presenter-first(而不是View-first)的方式。首先要修改MarketModule,添加依赖注入,注册接口和实现类的mapping关系。

      注意到,在注册Region和View之间Mapping关系的时候,原先是

regionViewRegistry.RegisterViewWithRegion("MarketRegion", typeof(Views.MarketView));

      而现在我们要修改为

this.regionManager.RegisterViewWithRegion(RegionNames.ResearchRegion, () => this.container.Resolve<ITrendLinePresentationModel>().View);

      就是说,把IRegionViewRegistry替换为IRegionManager,我翻阅过Prism实现源码,发现二者是没有区别的,随便使用哪个都可以,这里我们采用IRegionManager,以和原来的版本保持一致。

      P-first和V-first的区别仅在于RegisterViewWithRegion方法的第2个参数,对于typeof(Views.MarketView),就是说先进入View,然后在View中实例化Presenter;而对于

this.container.Resolve<ITrendLinePresentationModel>().View

      ,就是说,先实例化Presenter(这里是Model),然后在Presenter中实例化View并返回。这就产生了二者的构造函数在执行上的先后顺序。

3.设计ITrendLineView和ITrendLinePresentationModel接口

public interface ITrendLineView
{
    ITrendLinePresentationModel Model { get; set; }
}
public interface ITrendLinePresentationModel
{
    ITrendLineView View { get; }

    string TickerSymbol { get; }

    MarketHistoryCollection HistoryCollection { get; }
}

      我们要遵循这样的设计原则。首先,V和M要互相引用,也就是这两个接口中的Model和View属性。其次,要把Model中用于绑定的属性放到接口中,这里是TickerSymbol和HistoryCollection。

4.设计TrendLineView

      View一般都很简单,在xaml中绑定到空间,在后台cs文件中,实现Model属性,将它绑定到这个UserControl的DataContext就可以了。

5.设计TrendLinePresentationModel

      具体的逻辑都在这里。首先,如果有联动属性,那么Model就要实现INotifyPropertyChanged接口,这里TickerSymbol和HistoryCollection都是联动属性。其次,就是构造函数了:

public TrendLinePresentationModel(ITrendLineView view, IMarketHistoryService marketHistoryService, IEventAggregator eventAggregator)
{

    this.View = view;
    this.View.Model = this;
    
    this._marketHistoryService = marketHistoryService;

    eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Subscribe(this.TickerSymbolChanged);
}

      我们要遵循这样的设计原则:既然是P-first,那么就要在Model的构造函数中创建View:

this.View = view;

      ,并指定这个View的Model属性是自己:

this.View.Model = this;

      嗯,好费解的2句话,是不是?——但是缺一不可。

      构造函数的最后是订阅TickerSymbolSelectedEvent事件。当我们在PositionSummaryView的GridView中选中不同的行时,会触发这个事件,于是,饼图会因为数据源的改变而发生变化。考虑到这个事件在PositionSummaryView项目中也会使用到,因此,我们把它放到了类库StockTraderRI.Infrastructure中。

 

      至此,Market创建完毕,但是运行程序后不会看到任何数据,因为Market模块只是订阅者,也就是说,只有当源PositionSummaryView实现了Publish事件,才会看到TrendLineView中的数据。

posted @ 2009-07-13 01:10  包建强  Views(2877)  Comments(2Edit  收藏  举报