帮酷LOGO
0 0 评论
文章标签:Cairngorm  Silverlight  
SilverlightCairngorm.jpg

介绍

目前,有一些指导或者框架,但是它们都不能很容易地应用到 Silverlight。 从 2006年开始,adobe已经广泛应用于 Flex RIA应用程序;它具有easy-to-understand概念,可以识别设计模式,并证明可以很好地扩展大型的line-of-business应用程序开发。 本文介绍了如何将Cairngorm移植到 Visual Studio 2008 SP1中,详细介绍了采用的概念/类,以及如何将它的应用到demonstrate中,以及如何使用它,以及如何使用。 为潜在大规模的用户应用程序创建Silverlight原型有助于我,希望这项工作是有用的。

Cairngorm简介

Cairngorm是基于Flex或者AIR开发的富互联网应用程序的轻量级微结构。 它的目标应用是企业RIA或者中等规模的LOB RIA。 在介绍Cairngorm文档中,Cairngorm的主要好处是:

  • 将新功能或者更改添加到现有功能中更容易: 通过添加新视图。模型。事件。命令和委托可以为"插入"提供新功能( 请注意: 不是. NET 委托,它指的是Cairngorm委托,不改变/影响其他特性
  • 支持敏捷团队开发流程: 设计人员( 视图),前端开发人员( 模型,事件,命令,委托,数据绑定) 和数据服务开发人员( Web服务 ) 可以并行工作
  • 更易于维护和调试,更易于单元测试业务逻辑代码。

Cairngorm根据角色/职责帮助开发人员识别。组织和分离代码;在最高级别上,它具有以下主要组件:

  • 模型通过 ModelLocator 保存数据对象和数据的状态
  • 控制器处理Cairngorm事件并通过 FrontController 执行相应的Command
  • 命令是处理业务逻辑的非ui组件,它通常同时实现 ICommandIResponder 接口
  • 事件是触发业务对象的自定义事件( 例如。 命令),通常由视图( 应用程序事件,用户输入事件,等等 ) 处理程序引发的事件引发
  • ServiceLocator 是预先配置的客户端/服务器通信组件的存储库
  • Cairngorm代理是知道如何使用 Web服务 与通信并通过 IResponder 接口将结果和错误事件路由到命令的类
  • 视图呈现模型数据并使用事件与控制器通信,它还通过数据绑定监视模型数据更改

开发者文档可以找到更多关于Cairngorm的信息。

在 Silverlight Cairngorm中发生了什么变化

在为Silverlight实现Cairngorm时,除了 ServiceLocator 之外,所有主要概念/组件都被保留。 详情如下:

1.不需要在委托中直接使用 WebClient 进行 ServiceLocator

WebClient 和生成的服务代理用异步方式与服务交互,这样就更方便Cairngorm委托对象实例化 WebClient 或者生成的服务代理,从而消除了 ServiceLocator的需求。

但是在源代码中,如果你还需要 C# 定义,我仍然为 IServiceLocator 接口包含一个定义,对于 ServiceLocator,你可以找到一个抽象的C# 类,如果你需要它们的话。

更新说明:Silverlight Cairngorm v 0.0.0.1 线程安全实现从源代码中删除了 ServiceLocatorIServiceLocator 接口,它可以在这里下载。

2.ModelLocator成为抽象并实现 INotifyPropertyChanged

方法将抽象的应用程序实现为一个接口,并确保导出的模型是面向视图的,并保证了该模型的有效性。 我真的希望将来的Silverlight版本与 [Bindable] 属性像属性一样,然后开发人员无需担心 INotifyPropertyChanged 接口。 NotifyPropertyChanged("PropertyName") 在所有的setter方法中,它是一个更适合编译器的工作。 然后,设计模型并使它的具有可以绑定性将更容易。

publicabstractclass ModelLocator : INotifyPropertyChanged
{
 #region INotifyPropertyChanged Memberspublicevent PropertyChangedEventHandler PropertyChanged;
 protectedvoid NotifyPropertyChanged(string propertyName)
 {
 if (PropertyChanged!= null)
 {
 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 }
 }
 #endregion}

应用程序模型类将从上面列出的ModelLocator 派生。 派生类将实现 Singleton Pattern,还需要为属性方法的public 绑定setter调用 NotifyPropertyChanged。 你将在示例应用程序中看到一个示例。

[ 更新笔记 9/14/2008] 一个线程安全实现 ModelLocatorINotifyPropertyChange 接口在这里的另一篇文章。

3。FrontController变为抽象,而CairngormEventDispatcher变为内部

正在制作 CairngormEventDispatcher内部 将实际简化应用程序代码以引发Cairngorm事件: 它强制不访问 CairngormEventDispatcher ;相反,只实例化 CairngormEvent 对象,然后调用它的Dispatch 方法。 CairngormEventDispatcher 只在Cairngorm程序集中内部,应用程序需要关注它。

namespace SilverlightCairngorm.Control
{
 ///<spanclass="code-SummaryComment"><summary></span>/// Used to dispatch system events, by raising an event that the/// controller class subscribes to every time any system event is/// raised./// Client code has no need to use this class. (internal class)///<spanclass="code-SummaryComment"></summary></span>internalclass CairngormEventDispatcher
 {
 privatestatic CairngormEventDispatcher instance;
 ///<spanclass="code-SummaryComment"><summary></span>/// Returns the single instance of the dispatcher///<spanclass="code-SummaryComment"></summary></span>///<spanclass="code-SummaryComment"><returns>single instance of the dispatcher</returns></span>publicstatic CairngormEventDispatcher getInstance()
 {
 if ( instance == null )
 instance = new CairngormEventDispatcher();
 return instance;
 }
 ///<spanclass="code-SummaryComment"><summary></span>/// private constructor///<spanclass="code-SummaryComment"></summary></span>private CairngormEventDispatcher()
 {
 }
 ///<spanclass="code-SummaryComment"><summary></span>/// The subscriber to a system event must accept as argument/// the CairngormEvent raised (within a CairngormEventArgs/// object)///<spanclass="code-SummaryComment"></summary></span>publicdelegatevoid EventDispatchDelegate
 (object sender, CairngormEventArgs args);
 ///<spanclass="code-SummaryComment"><summary></span>/// The single event raised whenever a Cairngorm system event occurs///<spanclass="code-SummaryComment"></summary></span>publicevent EventDispatchDelegate EventDispatched;
 ///<spanclass="code-SummaryComment"><summary></span>/// dispatchEvent raises a normal. net event, containing the/// instance of the CairngormEvent raised - to be handled by/// the Controller Class///<spanclass="code-SummaryComment"></summary></span>publicvoid dispatchEvent(CairngormEvent cairngormEvent)
 {
 if (EventDispatched!= null)
 {
 CairngormEventArgs args = new CairngormEventArgs(cairngormEvent);
 EventDispatched(null, args);
 }
 }
 }
}

在 Silverlight Cairngorm中保留 Cairngorm FrontController:

namespace SilverlightCairngorm.Control
{
 ///<spanclass="code-SummaryComment"><summary></span>/// The system controller's parent, implementing the event-command/// relationship inner-workings, using a dictionary relating/// event names to commands.////// subscribes to the EventDispatched event of the CairngormEventDispatcher/// to handle all system events.///<spanclass="code-SummaryComment"></summary></span>publicabstractclass FrontController
 {
 ///<spanclass="code-SummaryComment"><summary></span>/// The dictionary of eventNames and corresponding commands to be executed///<spanclass="code-SummaryComment"></summary></span>private Dictionary eventMap = new Dictionary();
 public FrontController()
 {
 CairngormEventDispatcher.getInstance().EventDispatched +=
 new CairngormEventDispatcher.EventDispatchDelegate(ExecuteCommand);
 }
 ///<spanclass="code-SummaryComment"><summary></span>/// Whenever the CairngormEventDispatcher raises an event, this/// method gets the CairngormEvent inside it - and calls/// the execute() method on the corresponding ICommand///<spanclass="code-SummaryComment"></summary></span>void ExecuteCommand(object sender, CairngormEventArgs args)
 {
 if (eventMap.ContainsKey(args.raisedEvent.Name))
 {
 eventMap[args.raisedEvent.Name].execute(args.raisedEvent);
 }
 }
 ///<spanclass="code-SummaryComment"><summary></span>/// register a Cairngorm event to FrontController///<spanclass="code-SummaryComment"></summary></span>publicvoid addCommand(string cairngormEventName, ICommand command)
 {
 eventMap.Add(cairngormEventName, command);
 }
 }
}
4.添加了新的抽象类: CairngormDelegate

所有 CairngormDelegate 类都将从此类派生;它要求所有派生类都具有对 IResponder的引用。

namespace SilverlightCairngorm.Business
{
 publicabstractclass CairngormDelegate
 {
 protected IResponder responder;
 ///<spanclass="code-SummaryComment"><summary></span>/// A Constructor, Receiving an IResponder instance,/// used as"Callback" for the delegate's results -/// through its OnResult and OnFault methods.///<spanclass="code-SummaryComment"></summary></span>protected CairngormDelegate(IResponder responder)
 {
 this.responder = responder;
 }
 }
}
5.从SilverlightCairngorm中移除的类/接口

已经删除 IValueObject 接口和 ValueObject 类型。 因为很可能这些数据传输对象会被生成。

ViewHelperViewLocator 也不在 SilverlightCairngorm 中,因为在数据绑定方案中直接使模型访问视图不是一个好主意。

在链式事件/命令的情况下,SequenceCommand 不能移植,可以从命令中的onResult 方法中引发一个Cairngorm事件。

HTTPServiceWebServiceRemoteObject 都是特定于Flex的,不包含在 SilverlightCairngorm 中。

Silverlight Cairngorm示例应用程序

这个示例应用程序演示了如何在Silverlight应用程序中使用 Cairngorm。 如果安装了 Silverlight,就可以在这里运行应用程序。 它非常简单:允许用户键入使用 FlickR REST API 搜索照片,将搜索结果绑定到左手导航 List。 当然,当用户在 List 中选择一个图像时,显示图像将被更新。

1.在XAML中定义视图

自动生成的插件扩展了应用程序布局,它为应用程序标题提供了一个简单的文本动画。 它还在它的布局标记中引用了三个 UserControl。 这个想法是 Page.xaml 只提供一个入口点和视图的应用程序布局。 实际的函数视图由 UserControl 定义,以适应来自设计器的潜在UI设计更改。

所有 UserControl的定义都在视图子文件夹中定义,并将 SilverlightCairngormDemo.View 作为它们的名称空间。 PhotoSearch.xaml 只有一个textbox用户输入搜索项和一个按钮触发搜索照片动作。 PhotoList.xaml 有一个 ListBoxItemTemplate,用于将搜索结果作为文本标题呈现( 照片 List )。 PhotoSearchPhotoList 在 Page.xaml 中垂直堆叠,作为左边的导航列表。

ContentZone.xaml 只包含一个 Image 控件来显示所选照片。

2.定义 SearchPhotoDelegate

让我们从底层开始使用 SilverlightCairngorm。 by CairngormDelegate 是唯一一个理解如何与 FlickR REST API ----发送请求和路由 onResultonFault 调用到 IResponsder的类的类:

namespace SilverlightCairngormDemo.Business
{
 publicclass SearchPhotoDelegate : CairngormDelegate
 {
 public SearchPhotoDelegate(IResponder responder)
 : base(responder)
 {
 }
 publicvoid SendRequest(string searchTerm)
 {// SilverPhotoService svcLocator = SilverPhotoService.getInstance();// WebClient flickrService =// svcLocator.getHTTPService(SilverPhotoService.FLICKR_SEV_NAME);string apiKey = "[[You can get your API key for free from FlickR]]";
 string secret = "[[Yours goes here]]";
 string url = String.Format("http://api.flickr.com/services/rest/?" +
 "method=flickr.photos.search&api_key={1}&text={0}",
 searchTerm, apiKey, secret);
 WebClient flickRService = new WebClient();
 flickRService.DownloadStringCompleted +=
 new DownloadStringCompletedEventHandler(
 flickRService_DownloadStringCompleted);
 flickRService.DownloadStringAsync(new Uri(url));
 }
 privatevoid flickRService_DownloadStringCompleted(object sender,
 DownloadStringCompletedEventArgs e)
 {
 if (null!= e.Error)
 responder.onFault("Exception! (" + e.Error.Message + ")");
 else responder.onResult(e.Result);
 }
 }
}
3。定义 SearchPhotoCommand

SearchPhotoCommand 实现 IResponderICommand 接口。 当控制器处理Cairngorm事件时将调用 ICommand.execute,并且当没有异常发生时,IResponder.onResult 将从 SearchPhotoDelegate 调用。 IResponder.onFault 处理来自 SearchPhotoDelegate 或者无效数据的异常和错误:

namespace SilverlightCairngormDemo.Command
{
 publicclass SearchPhotoCommand : ICommand, IResponder
 {
 private SilverPhotoModel model = SilverPhotoModel.getInstance();
 #region ICommand Memberspublicvoid execute(CairngormEvent cairngormEvent)
 {
 //get search term from modelstring toSearch = model.SearchTerm;
 //begin talk to web service SearchPhotoDelegate cgDelegate = new SearchPhotoDelegate(this);
 cgDelegate.SendRequest(toSearch);
 }
 #endregion#region IResponder Memberspublicvoid onResult(object result)
 {
 string resultStr = (string)result;
 if (String.IsNullOrEmpty(resultStr))
 {
 onFault("Error! (Server returns empty string)");
 return;
 }
 XDocument xmlPhotos = XDocument.Parse(resultStr);
 if ((null == xmlPhotos) ||
 xmlPhotos.Element("rsp").Attribute("stat").Value == "fail")
 {
 onFault("Error! (" + resultStr + ")");
 return;
 }
 //update the photoList data in model model.PhotoList = xmlPhotos.Element("rsp").Element(
 "photos").Descendants().Select( p =>new FlickRPhoto
 {
 Id = (string)p.Attribute("id"),
 Owner = (string)p.Attribute("owner"),
 Secret = (string)p.Attribute("secret"),
 Server = (string)p.Attribute("server"),
 Farm = (string)p.Attribute("farm"),
 Title = (string)p.Attribute("title"),
 } ).ToList<flickrphoto/>();
 if (model.PhotoList.Count >0)
 model.SelectedIdx = 0; //display the 1st imageelse onFault("No such image, please search again.");
 }
 publicvoid onFault(string errorMessage)
 {
 //display the error message in PhotoList model.SelectedIdx = -1;
 model.PhotoList = new List<flickrphoto/>()
 { new FlickRPhoto() { Title = errorMessage } };
 }
 #endregion }
}

在这里,我们使用线程池线程来执行XML解析和数据转换到对象集合来测试线程的安全性。

4.定义 SilverPhotoController

SilverPhotoController 派生自 FrontController,用 SearchPhotoCommand 注册特定的事件 NAME,并在运行时将事件路由到相应的命令。

namespace SilverlightCairngormDemo.Control
{
 publicclass SilverPhotoController : FrontController
 {
 publicconststring SC_EVENT_SEARCH_PHOTO = "cgEvent_SearchPhoto";
 privatestatic SilverPhotoController instance;
 ///<spanclass="code-SummaryComment"><summary></span>/// Returns the single instance of the controller///<spanclass="code-SummaryComment"></summary></span>///<spanclass="code-SummaryComment"><returns>single instance of the dispatcher</returns></span>publicstatic SilverPhotoController getInstance()
 {
 if ( instance == null )
 instance = new SilverPhotoController();
 return instance;
 }
 ///<spanclass="code-SummaryComment"><summary></span>/// private constructor///<spanclass="code-SummaryComment"></summary></span>private SilverPhotoController()
 {
 base.addCommand(SC_EVENT_SEARCH_PHOTO, new SearchPhotoCommand());
 }
 }
}
5.定义模型

模型中的第一种类型是 FlickRPhoto 类;它表示从搜索中返回的照片数据对象。

namespace SilverlightCairngormDemo.Model
{
 publicclass FlickRPhoto
 {
 publicstring Id { get; set; }
 publicstring Owner { get; set; }
 publicstring Secret { get; set; }
 publicstring Server { get; set; }
 publicstring Farm { get; set; }
 publicstring Title { get; set; }
 publicstring ImageUrl
 {
 get {
 if (String.IsNullOrEmpty(Farm) || String.IsNullOrEmpty(Server) ||
 String.IsNullOrEmpty(Id) || String.IsNullOrEmpty(Secret))
 returnnull;
 returnstring.Format
 ("http://farm{0}.static.flickr.com/{1}/{2}_{3}.jpg",
 Farm, Server, Id, Secret);
 }
 }
 }
}

现在是时候定义 SilverPhotoModel 了;它从 ModelLocator 派生,并作为单独的实现实现。 请注意 NotifyPropertyChanged("PropertyName") 调用,这些对于数据绑定至关重要。

namespace SilverlightCairngormDemo.Model
{
 publicclass SilverPhotoModel : ModelLocator
 {
 privatestatic SilverPhotoModel instance;
 ///<spanclass="code-SummaryComment"><summary></span>/// Returns the single instance of the app model///<spanclass="code-SummaryComment"></summary></span>///<spanclass="code-SummaryComment"><returns>single instance of the app model</returns></span>publicstatic SilverPhotoModel getInstance()
 {
 if (instance == null)
 instance = new SilverPhotoModel();
 return instance;
 }
 ///<spanclass="code-SummaryComment"><summary></span>/// Private constructor for singleton object///<spanclass="code-SummaryComment"></summary></span>private SilverPhotoModel()
 {
 if ( instance!= null )
 {
 thrownew CairngormError(CairngormMessageCodes.SINGLETON_EXCEPTION,
 "App model (SilverPhotoModel) should be a singleton object");
 }
 }
 private List<flickrphoto> _photoList = new List<flickrphoto>();
 ///<spanclass="code-SummaryComment"><summary></span>/// Data model for search result data binding///<spanclass="code-SummaryComment"></summary></span>public List<flickrphoto> PhotoList
 {
 get { return _photoList; }
 set { _photoList = value; NotifyPropertyChanged("PhotoList"); }
 }
 privateint _selectedIdx = -1;
 ///<spanclass="code-SummaryComment"><summary></span>/// Data model for selected photo index///<spanclass="code-SummaryComment"></summary></span>publicint SelectedIdx
 {
 get { return _selectedIdx; }
 set {
 _selectedIdx = value;
 NotifyPropertyChanged("SelectedIdx");
 NotifyPropertyChanged("SelectedPhotoSource");
 }
 }
 ///<spanclass="code-SummaryComment"><summary></span>/// Read-only property for image displaying///<spanclass="code-SummaryComment"></summary></span>public BitmapImage SelectedPhotoSource
 {
 get { return (SelectedIdx <0 || PhotoList.Count <2 )? null :
 new BitmapImage(new Uri(PhotoList[SelectedIdx].ImageUrl)); }
 }
 privatestring _searchTerm = "Cairngorm";
 ///<spanclass="code-SummaryComment"><summary></span>/// Data model for search term///<spanclass="code-SummaryComment"></summary></span>publicstring SearchTerm
 {
 get { return _searchTerm; }
 set { _searchTerm = value; }
 }
 }
}
6.将它的放在一起

首先,我们需要为 SilverPhotoController 创建一个实例。 我将实例化代码放在 Application_startup 事件处理程序中的App.xaml.cs 中。

privatevoid Application_Startup(object sender, StartupEventArgs e)
{
 this.RootVisual = new Page();
 //create Cairngorm controller instance SilverPhotoController cntrller = SilverPhotoController.getInstance();
}

其次,我们需要将 DataContext 分配为 SilverPhotoModel。 当主XAML在 Page.xaml.cs 中加载时,将在 Loaded 事件处理程序中设置该属性。

privatevoid UserControl_Loaded(object sender, RoutedEventArgs e)
{
 InitLoadEffect.Begin();
 //create Cairngorm model instance SilverPhotoModel model = SilverPhotoModel.getInstance();
 //bind model to view LayoutRoot.DataContext = model;
}

由于 DataContext 继承特性,我们无需将它的设置为视图的视图,因为它们会自动从Silverlight中的可视化继承相同的DataContext

第三,我们需要在标记的UserControl XAML中编写一些数据绑定表达式。

PhotoSearch.xaml: 与 model.SearchTerm的双向绑定。

<TextBoxx:Name="searchTermTextBox"Height="30"Margin="8"VerticalAlignment="Center"FontSize="16"Text="{Binding Path=SearchTerm, Mode=TwoWay}"/>

PhotoList.xaml: 为 ItemTemplate 绑定到 model.PhotoListmodel.selectedIdxFlickRPhoto.Title的方法。

<ListBoxx:Name="formListBox"Width="Auto"Height="Auto"ItemsSource="{Binding Path=PhotoList}"SelectedIndex="{Binding Path=SelectedIdx, Mode=TwoWay}"SelectionChanged="formListBox_SelectionChanged"><ListBox.ItemTemplate><DataTemplate><TextBlockText="{Binding Path=Title}"Width="Auto"Height="Auto"FontSize="12"></TextBlock></DataTemplate></ListBox.ItemTemplate></ListBox>

ContentZone.xaml: 一种与 model.SelectedPhotoSource 绑定的方法。

<Imagex:Name="searchResultsImage"Source="{Binding Path=SelectedPhotoSource}"Stretch="UniformToFill"VerticalAlignment="Center"HorizontalAlignment="Center"Margin="8"/>

最后,使用 CairngormEvent 将"转到"按钮 Click 事件连接起来。 下面是按钮事件处理程序的Click 代码,该代码位于 PhotoSearch.xaml.cs file: 中

privatevoid searchBtn_Click(object sender, RoutedEventArgs e)
{
 SilverPhotoModel model = SilverPhotoModel.getInstance();
 if (!String.IsNullOrEmpty(model.SearchTerm))
 {
 CairngormEvent cgEvent =
 new CairngormEvent(SilverPhotoController.SC_EVENT_SEARCH_PHOTO);
 cgEvent.dispatch();
 }
}

确认

感谢 MVC在 CodePlex 项目,它给了我移植到 Silverlight 2 ( 测试版 2 ) 一个不错的起点。

另外,感谢 FlickR的博客 blog,它肯定会让FlickR更容易使用。

历史记录

  • 2008.09.01 post
  • 2008.09.14增加了关于线程安全更改到 Silverlight Cairngorm的更新说明。
  • 2008.10.05 - Silverlight Cairngorm v。 0.0.1.2 ( 线程安全 Silverlight Cairngorm的可以下载源代码和演示项目更新)
    • 新的Cairngorm事件将由相应的Cairngorm命令来处理,这将使 Silverlight Cairngorm的工作方式与flex的flex 2.2.1 FrontController的Cairngorm一样。 源和演示项目都可以从这里下载到
    • 还更新了演示项目以反映在命令类型中传递的新 addCommand 签名,而不是命令的实例
    • 演示项目也被更新为使用 Silverlight 2 RC0


文章标签:Silverlight  Cairngorm  

Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语