例说MVVM和ThreadPoolCompleteVersion.docx

上传人:b****0 文档编号:12734343 上传时间:2023-04-21 格式:DOCX 页数:16 大小:164.91KB
下载 相关 举报
例说MVVM和ThreadPoolCompleteVersion.docx_第1页
第1页 / 共16页
例说MVVM和ThreadPoolCompleteVersion.docx_第2页
第2页 / 共16页
例说MVVM和ThreadPoolCompleteVersion.docx_第3页
第3页 / 共16页
例说MVVM和ThreadPoolCompleteVersion.docx_第4页
第4页 / 共16页
例说MVVM和ThreadPoolCompleteVersion.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

例说MVVM和ThreadPoolCompleteVersion.docx

《例说MVVM和ThreadPoolCompleteVersion.docx》由会员分享,可在线阅读,更多相关《例说MVVM和ThreadPoolCompleteVersion.docx(16页珍藏版)》请在冰豆网上搜索。

例说MVVM和ThreadPoolCompleteVersion.docx

例说MVVM和ThreadPoolCompleteVersion

前言

随着微软WindowsPresentationFoundation(简称WPF)的绚丽外衣诱惑以及快捷灵活的开发方式的深入人心,WPF将取代.net的WindowsForms以及传统的MFC成为新一代应用开发的主力军,成为必然。

本文将通过一个股票小软件的例子,来说明WPF中的新的架构模式“MVVM”以及.net的强大ThreadPool的使用。

股票小软件?

前一段时间,一个证券公司的朋友推荐我试用了一下某证券公司的股票软件,里面的一个资金统计功能非常喜欢,但是由于这个软件是试用版,所以想长期使用的话,还是需要自己想办法自力更生。

于是,逆向这个软件程序,拿到反汇编的程序,调试,抓数据包,最后分析出这个资金统计功能的机理。

哈哈,其实实现起来不难。

这就开工自己写个类似的功能给自己用吧。

什么是MVVM

MVVM是Model-View-ViewModel的简写,是从MVC演化而来的,特定于WPF的一个架构模式,其实与MVC类似,只是其中的Controller转换成了ViewModel。

但是就是这一个转换,给WPF的程序架构和设计带来的异常的灵活性。

不论MVVM还是MVC,起核心宗旨都是关注分离,让专人负责专工。

但是传统的MVC在经过这么多年的实践当中,出现的问题是,并不能真正的做到Model,View与Controller的分离。

微软推出MVVM后,使ViewModel成为Model与View的联系纽带。

MVVM帮助我们把业务与UI展示分离的更彻底。

这样的好处就有一大堆啦:

简化测试,降低维护成本,可以更容易的跨设备、跨平台(可以在WP7,Silverlight上使用),提高代码重用率,让开发人员与UI设计人员更容易的协作,等等。

那么,在MVVM中,Model,View,ViewModel的职责具体是什么呢?

⏹View,用来封装UI以及UI逻辑;

⏹Model,封装应用程序的业务逻辑和数据;(注:

在实际应用中,这个Model有所谓的贫血与充血之分。

我在例子中采用的是贫血模型)

⏹ViewModel,封装展示逻辑与状态。

下面上图,来直观的看看MVVM:

(本图来自Prism4)

View部分

理想情况下,View部分的所有代码都应该位于XAML中,在codebehind中只包含一个构造方法,调用InitializeCoomponent方法。

但是在某些情况下,codebehind也会包含一些与XAML有关系的代码,例如一些复杂的动画。

但是不应该把任何需要做单元测试的代码放在codebehind中。

ViewModel部分

Viewmodel封装的是view部分的展示逻辑也数据。

它并没有直接引用view,也不依赖与具体的view实现。

Viewmodel中提供view所需的属性以及command,使用变更通知事件将这些属性和command绑定到view上。

Model部分

Model中包含业务逻辑,用来读取和管理应用数据。

通常,model代表的是应用程序的客户端的领域模型。

基于应用程序的数据模型,可以在model中定义相应的数据结构,实现相应的业务和校验逻辑。

还可以把数据访问与缓存逻辑也放在model中。

为了通知view,model的数据有变化,WPF引入了“变更通知事件”。

WPF定义了接口INotifyPropertyChanged,INotifyCollectionChanged。

可以根据具体情况,让model实现INotifyPropertyChanged和/或INotifyCollectionChanged接口,使view能够感知到model的数据变化,并刷新view。

股软的资金统计功能的分析

股软的资金统计功能,就是定时的到某个服务器上读取最新的数据,并展示给用户。

为了能够定时启动程序读取数据,在程序里需要使用一个timer来计时。

由于股票数量很多,而每个请求只能读取一个股票的数据,所以这里需要使用多线程的方式来在每次timer启动时,读取多个股票的数据。

于是,就有了下图中的设计:

针对这个图,下面我们分析下其中的Model,View和ViewModel:

Model部分

Capital是model。

它继承自ObservableObject,而ObservableObject实现接口INotifyPropertyChanged。

这也就意味着,Captial对象的数据有变化时,view可以感知到这个变化,并刷新view。

ObservableObjectclass

publicabstractclassObservableObject:

INotifyPropertyChanged

{

#regionINotifyPropertyChangedMembers

publiceventPropertyChangedEventHandlerPropertyChanged;

#endregion

#regionProtectedMethods

///

///RaisesthePropertyChangedevent.

///

///Thenameofthepropertythatwaschanged.

protectedvoidRaisePropertyChangedEvent(stringpropertyName)

{

if(PropertyChanged!

=null)

{

vare=newPropertyChangedEventArgs(propertyName);

PropertyChanged(this,e);

}

}

#endregion

}

Capitalclass

publicclassCapital:

ObservableObject

{

privateintstockId;

privatestringstockCode;

privatestringstockType;

privatedoublemainInitiativeBuyVol;

privatedoublemainInitiativeSellVol;

privatedoubleretailInitiativeBuyVol;

privatedoubleretailInitiativeSellVol;

privatedoubleinstitutionBuyVol;

privatedoubleinstitutionSellVol;

privateDateTimecapitalDateTime;

publicCapital(stringstockType,stringstockCode,intstockId)

{

this.stockType=stockType;

this.stockCode=stockCode;

this.stockId=stockId;

}

publicintStockId

{

get{returnthis.stockId;}

set{this.stockId=value;}

}

publicstringStockType

{

get{returnthis.stockType;}

set{this.stockType=value;}

}

publicstringStockCode

{

get{returnthis.stockCode;}

set{this.stockCode=value;}

}

publicdoubleMainInitiativeBuyVol

{

get{returnthis.mainInitiativeBuyVol;}

set

{

this.mainInitiativeBuyVol=value;

base.RaisePropertyChangedEvent("MainInitiativeBuyVol");

}

}

publicdoubleMainInitiativeSellVol

{

get{returnthis.mainInitiativeSellVol;}

set

{

this.mainInitiativeSellVol=value;

base.RaisePropertyChangedEvent("MainInitiativeSellVol");

}

}

publicdoubleRetailInitiativeBuyVol

{

get{returnthis.retailInitiativeBuyVol;}

set

{

this.retailInitiativeBuyVol=value;

base.RaisePropertyChangedEvent("RetailInitiativeBuyVol");

}

}

publicdoubleRetailInitiativeSellVol

{

get{returnthis.retailInitiativeSellVol;}

set

{

this.retailInitiativeSellVol=value;

base.RaisePropertyChangedEvent("RetailInitiativeSellVol");

}

}

publicdoubleInstitutionBuyVol

{

get{returnthis.institutionBuyVol;}

set

{

this.institutionBuyVol=value;

base.RaisePropertyChangedEvent("InstitutionBuyVol");

}

}

publicdoubleInstitutionSellVol

{

get{returnthis.institutionSellVol;}

set

{

this.institutionSellVol=value;

base.RaisePropertyChangedEvent("InstitutionSellVol");

}

}

publicDateTimeCapitalDateTime

{

get{returnthis.capitalDateTime;}

set

{

this.capitalDateTime=value;

base.RaisePropertyChangedEvent("CapitalDateTime");

}

}

}

在每个属性值有变化时,都需要使用RaisePropertyChangedEvent(stringpropName)方法来通知view。

ViewModel部分

CapitalListViewModel是一个viewmodel。

它使用CapitalListService定时读取股票数据,并更新Capital对象。

CapitalListViewModelclass

publicclassCapitalListViewModel

{

privateCapitalListService_capitalListService;

publicCapitalListViewModel()

{

_capitalListService=newCapitalListService();

_capitalListService.Start();

Debug.WriteLine("CreateCapitalListViewModel.");

}

publicListCapitalList

{

get{return_capitalListService.CapitalList;}

}

#regionoverridemethods;

protectedoverridevoidOnDispose()

{

base.OnDispose();

_capitalListService.Stop();

}

#endregion

}

CapitalListViewMode暴露的属性CapitalList,用来绑定到view上。

CapitalListServiceclass

publicclassCapitalListService

{

privateListcapitalList;

privateboolisRunning=false;

privateintnumPerRequest=5;

privateTimertimer;

publicCapitalListService()

{

capitalList=ReadFile("sh");

capitalList.AddRange(ReadFile("sz"));

capitalList.AsReadOnly();

}

publicListCapitalList

{

get{returnthis.capitalList;}

}

publicvoidStart()

{

Debug.WriteLine("StartingCapitalListService...");

if(isRunning)

{

Stop();

}

isRunning=true;

timer=newTimer(newTimerCallback(Process),null,0,1000);

Debug.WriteLine("CapitalListServiceStarted.");

}

intcurrIndex=0;

privatevoidProcess(objectstateInfo)

{

timer.Change(Timeout.Infinite,Timeout.Infinite);

intnumOfRequest=(currIndex+numPerRequest>capitalList.Count)?

(capitalList.Count-currIndex):

numPerRequest;

Debug.WriteLine("CapitalListServiceisREFRESHING"+numOfRequest+"datas...");

if(numOfRequest==0)

{

currIndex=0;

}

else

{

ListsubList=capitalList.GetRange(currIndex,numOfRequest);

currIndex+=numOfRequest;

CapitalDataEngine.GetData(subList);

Thread.Sleep(2000);

}

if(isRunning)

timer.Change(0,1000);

else

timer=null;

}

publicvoidStop()

{

if(isRunning)

{

isRunning=false;

while(timer!

=null)

{

Thread.Sleep(5);

}

}

}

#regionInitializecapitaldata

privateListReadFile(stringstockType)

{

returnnull;//TODO:

LoadCapitallistfromdatabase.

}

#endregion

}

在CapitalListService中,创建了一个timer,并设置timer每1秒中启动一次,调用CaptialDataEngine读取数据,更新Capital对象列表。

CapitalDataEngineclass

internalclassThreadStateObject{

publicThreadStateObject(Capitalcapital,ManualResetEventresetEvent){

this.CapitalData=capital;

this.ResetEventObject=resetEvent;

}

publicCapitalCapitalData{get;set;}

publicManualResetEventResetEventObject{get;set;}

}

publicstaticclassCapitalDataEngine

{

publicstaticvoidGetData(ListcapitalList){

ManualResetEvent[]mResetEvents=newManualResetEvent[capitalList.Count];

for(inti=0;i

Capitalcapital=capitalList[i];

mResetEvents[i]=newManualResetEvent(false);

ThreadStateObjectstate=newThreadStateObject(capital,mResetEvents[i]);

ThreadPool.QueueUserWorkItem(newWaitCallback(RequestCapital),state);

}

WaitHandle.WaitAll(manualEvents,newTimeSpan(0,0,5),false);

}

privatestaticvoidRequestCapital(objectstate){

ThreadStateObjectthreadStateObject=(ThreadStateObject)state;

Capitalcaptial=threadStateObject.CapitalData;

stringreturnString=string.Empty;//TODO:

sendrequesttoloadstockdata.

ParseCapitalData(returnString,captial);

threadStateObject.ResetEventObject.Set();

}

///

///Parsedatafromreturnedstringandupdatecapitalobject.

///

///

///

privatestaticvoidParseCapitalData(stringcapitalData,Capitalcapital){

//TODO:

implementsthelogictoparsedataandupdatecapitalobject.

}

}

在CapitalDataEngine中,最值得关注的部分就是ThreadPool的使用。

.netframework内部提供了一个ThreadPool,因此这里就直接使用这个ThreadPool,来实现多线程读取数据,更新Captial对象列表的任务。

ThreadPool的使用,包括三部分:

1,定义State对象,把要在线程里使用的数据,以及一个线程任务执行结束的开关对象封装在里面。

内部类ThreadStateObject就是这里所用到的state对象。

2,将workitem放入线程池队列。

在上面代码中,ThreadPool.QueueUserWorkItem(newWaitCallback(RequestCapital),state);把workitem放入了线程队列中。

3,监控处理任务的线程的结束状态。

这里使用的是WaitHandle.WaitAll(manualEvents,newTimeSpan(0,0,5),false);来等待所有的ManualResetEvent都set了以后,才退出这个方法调用

View部分

CapitalListView是一个view。

CapitalListViewModel关联到这个view,并使用binding机制,将Captial对象列表绑定到view上。

CapitalListView.xaml

Class="MyStock.View.CapitalListView"

xmlns="

xmlns:

x="

xmlns:

my="

Title="CapitalListView"Height="300"Width="765">

DataGridAutoGenerateColumns="False"Name="MainGrid"ItemsSource="{BindingCapitalList}">

DataGrid.Columns>

DataGridTextColumnHeader="StockCode"IsReadOnly="True"Width="75"Binding="{BindingPath=StockCode}"/>

DataGridTextColumnHeader="主力买量"IsReadOnly="True"Width="90"Binding="{BindingPath=MainInitiativeBuyV

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 幼儿读物

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1