[Silverlight]MVVM+MEF框架Jounce学习(2):标记和绑定
2012-11-29 10:09 slmk 阅读(1739) 评论(0) 编辑 收藏 举报这一部分主要讲解如何绑定View和View Model。
IApplicationService接口
Jounce实现了 IApplicationService接口,作为一个服务插入到Silverlight应用程序的生命周期。这样,Jounce就有机会在silverlight应用程序开始、结束或者出现未处理的异常时,接管程序的执行。使用Jounce框架的应用程序,App.xaml必须这样定义:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Services="clr-namespace:Jounce.Framework.Services;assembly=Jounce" x:Class="SilverlightApplication.App" > <Application.Resources> </Application.Resources> <Application.ApplicationLifetimeObjects> <Services:ApplicationService IgnoreUnhandledExceptions="False" LogSeverityLevel="Verbose"/> </Application.ApplicationLifetimeObjects> </Application>
StartService方法
当应用程序启动时,自动调用IApplicationService接口的StartService方法:
public void StartService(ApplicationServiceContext context) { var logLevel = LogSeverityLevel; if (context.ApplicationInitParams.ContainsKey(Constants.INIT_PARAM_LOGLEVEL)) { logLevel = (LogSeverity) Enum.Parse(typeof (LogSeverity), context.ApplicationInitParams[Constants.INIT_PARAM_LOGLEVEL], true); } _mainCatalog = new AggregateCatalog(new DeploymentCatalog()); _container = new CompositionContainer(_mainCatalog); CompositionHost.Initialize(_container); CompositionInitializer.SatisfyImports(this); if (Logger == null) { ILogger defaultLogger = new DefaultLogger(logLevel); _container.ComposeExportedValue(defaultLogger); } else { Logger.SetSeverity(logLevel); } DeploymentService.Catalog = _mainCatalog; DeploymentService.Container = _container; _mefDebugger = new MefDebugger(_container, Logger); }
这里,Jounce主要做了这么几件事情:
设置默认的日志级别,你也可以通过以下方式制定某一日志级别:
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"> <param name="initParams" value="Jounce.LogLevel=Verbose" /> </object>
Jounce为当前程序集定义了一个默认的部署类别DeploymentCatalog(Jounce可以为不同的XAP定义不同的部署类别)。
定义了一个ILogger对象的导入:
[Import(AllowDefault = true, AllowRecomposition = true)] public ILogger Logger { get; set; }
AllowDefault告诉MEF这个导入不是一定要满足的,允许为null。如果你实现并导出了自己的ILogger对象,会满足这个导入条件;如果没有定义,Jounce使用一个默认的DefaultLogger。这个默认的logger会将日志写到调试窗口,MefDebugger使用此日志对象,输出一些Jounce日志,让我们可以跟踪Jounce做了什么。当View和ViewModel不能按我们预想的绑定时,查看这些日志会很有帮助。看一看MefDebugger的源代码,会对我们理解MEF的如何工作很有帮助。
MEF: Found part: SilverlightApplication.ViewModels.MainViewModel
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With import: Jounce.Core.ViewModel.BaseViewModel.EventAggregator (ContractName="Jounce.Core.Event.IEventAggregator")
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With import: Jounce.Core.ViewModel.BaseViewModel.Router (ContractName="Jounce.Core.ViewModel.IViewModelRouter")
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With import: Jounce.Core.ViewModel.BaseViewModel.Logger (ContractName="Jounce.Core.Application.ILogger")
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With export: SilverlightApplication.ViewModels.MainViewModel (ContractName="Jounce.Core.ViewModel.IViewModel")
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With key: ViewModelType = SilverlightApplication.ViewModels.MainViewModel
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With key: ExportTypeIdentity = Jounce.Core.ViewModel.IViewModel
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With export: SilverlightApplication.ViewModels.MainViewModel (ContractName="Jounce.Core.ViewModel.IViewModel")
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With key: ViewModelType = SilverlightApplication.ViewModels.MainViewModel
2012/11/29 10:25:46 Verbose Jounce.Core.MefDebugger :: With key: ExportTypeIdentity = Jounce.Core.ViewModel.IViewModel
Starting方法
另一个重要的事件是Starting,在visual tree完全激活之前调用。
public void Starting() { if (!IgnoreUnhandledExceptions) { Application.Current.UnhandledException += _CurrentUnhandledException; } var viewInfo = (from v in Views where v.Metadata.IsShell select v).FirstOrDefault(); if (viewInfo == null) { var grid = new Grid(); var tb = new TextBlock { Text = Resources.ApplicationService_Starting_Jounce_Error_No_view }; grid.Children.Add(tb); Application.Current.RootVisual = grid; Logger.Log(LogSeverity.Critical, GetType().FullName, Resources.ApplicationService_Starting_Jounce_Error_No_view); return; } Application.Current.RootVisual = viewInfo.Value; Logger.LogFormat(LogSeverity.Information, GetType().FullName,Resources.ApplicationService_Starting_ShellResolved, MethodBase.GetCurrentMethod().Name, viewInfo.Value.GetType().FullName); Logger.Log(LogSeverity.Information, GetType().FullName, MethodBase.GetCurrentMethod().Name); EventAggregator.Publish(viewInfo.Metadata.ExportedViewType.AsViewNavigationArgs()); }
Jounce捕捉未处理的异常,并用事件聚合发布异常,令我们的代码可以容易的订阅并处理他们。Jounce只可以标记一个View作为Shell,这个View将作为应用程序的root visual。如果找不到shell,Jounce会抛出异常。
所有的View元数据会被导入到ApplicationService对象中:
[ImportMany(AllowRecomposition = true)] public Lazy<UserControl, IExportAsViewMetadata>[] Views { get; set; }
然后,Jounce记录一些消息并触发一个导航事件,Jounce导航到根视图,并绑定到视图模型。
标记View和View Model
如何标记Shell根视图:
[ExportAsView("Welcome",IsShell = true)] public partial class Welcome { public Welcome() { InitializeComponent(); } }
标记View和ViewModel很灵活,可以像上面那样使用字符串(名称必须唯一),也可以使用强类型标记。ViewModel这样标记:
[ExportAsViewModel("Welcome")] public class WelcomeViewModel : BaseViewModel { public WelcomeViewModel() { Title = "Welcome to Jounce!"; } private string _title; public string Title { get { return _title; } set { _title = value; RaisePropertyChanged(()=>Title); } } public override void _Activate(string viewName) { GoToVisualState("WelcomeState",true); } }
BaseViewModel定义了可以override的方法:
_Initialize在view model第一次创建时调用,_Activate当视图View绑定到ViewModel并激活时调用,
_Deactivate在view不再激活时调用。当导航Navigation发生时,Jounce拦截并绑定ViewModel,然后调用相应的这些方法。
如何绑定View和ViewModel呢?
[Export] public ViewModelRoute Binding { get { return ViewModelRoute.Create("Welcome", "Welcome"); } }
ViewRouter类
实现了IEventSink接口,
public class ViewRouter : IPartImportsSatisfiedNotification, IEventSink<ViewNavigationArgs>
可以接收全局导航消息并调用ViewModelRouter的ActivateView方法:
public bool ActivateView(string viewName) { Logger.LogFormat(LogSeverity.Verbose, GetType().FullName, Resources.ViewModelRouter_ActivateView,MethodBase.GetCurrentMethod().Name,viewName); if (HasView(viewName)) { var view = ViewQuery(viewName); var viewModelInfo = GetViewModelInfoForView(viewName); if (viewModelInfo != null) { var firstTime = !viewModelInfo.IsValueCreated; var viewModel = viewModelInfo.Value; if (firstTime) { viewModel.GoToVisualState = (state, transitions) => JounceHelper.ExecuteOnUI(() => VisualStateManager.GoToState(view, state, transitions)); _BindViewModel(view, viewModel); viewModel.Initialize(); } viewModel.Activate(viewName); } return true; } return false; }
viewModel的GoToVisualState可以在不引用view的情况下改变view的VisualState:
public override void _Activate(string viewName) { GoToVisualState("WelcomeState",true); }