[Silverlight]MVVM+MEF框架Jounce学习(2):标记和绑定

这一部分主要讲解如何绑定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);

}

你可能感兴趣的:(silverlight)