MVVM Light需要注意的10个问题

MVVM Light需要注意的10个问题

从使用XAML技术基础开始(实际上并不是很久以前),我便关注MVVM(Model – View – ViewModel)模式。偶然接触到MVVM Light不久后便喜欢上它的工作方式。不光我包括业余和专业开发人员在内的很多开发人员都喜欢这个函数库。按照开发者意愿,MVVM Light 不是一个框架而是函数库,该函数库注重于探究建立一个MVVM结构并且提供一些额外的帮助类以便于应用。


MVVM Light在发展过程中改变了很多,很多元素被加入又有很多元素被舍弃。但一直保留了快速、易用的轻量级架构。作者Laurent Bugnion,关注于倾听MVVM Light用户的声音,合并特性请求并帮助开发者。当和一些跟我一起的开发者聊天的时候,我数次发现某些MVVM Light中的元素他们都没听说过在其它地方也是如此。我跟用户讨论的时候也学到了很多MVVM Light的新东西。回顾一番后便有了写博客的想法,并以常用的“10个关于……”作为标题,这里会告你比较容易忽视的10个MVVM Light隐藏的宝玉。


1、 MVVM Light 安装

这个问题比较显而易见,但是在通过NuGet安装会失去一些优点,MVVM的MSI安装方式不仅给你安装上二进制文件,同时也能在VS中提供工程、项目模板和很多snippets。万一VS2012更新2删除了你的模板,可以从C:\Program Files (x86)\Laurent Bugnion (GalaSoft)\Mvvm Light Toolkit\Vsix中重新安装VSIX,这样就能在VS中获取工程模板。

 

2、 构造器注入

该功能非常强大,实际上是多数依赖注入(DI)架构的特性。MVVM Light在应用程序开启后(应用程序生命周期内)使用SimpleIoc注册viewmodel和service类。构造器注入是指在类的构造函数中可以指定参数,当该类被实例化,SimpleIoc就试探查找相同类型的注册类作为参数,如果能查找到,这个实例就作为构造函数的参数被注入。下面这个例子,演示在ViewModelLocator中注册navigation service.

Code Snippet

SimpleIoc.Default.Register<INavigationServiceNavigationService>();

我们打算在控制反转(IOC)容器中注册INavigationService,当它创建实例,我们希望它的类型是NavigationService。目前IOC 容器中该“记录”并没有实例,当首次从容器中获取时才能被实例化。有时候当你注册后立马想创建实例,SimpleIoc重载函数Register<T>即可实现该功能。


Code Snippet

1. SimpleIoc.Default.Register<INavigationServiceNavigationService>(true);

仅仅将参数输入为true就能立马创建实例,现在,我们打算在MainViewModel中使用NavigationService

Code Snippet

1. ///<summary>

2. /// Initializes a new instance of the MainViewModel class.

3. ///</summary>

4. public MainViewModel(INavigationService navigationService)

5. {

6.     

7. }

SimpleIoc 会查找注册的INavigationService 类型并且在构造函数中注入它。这节省了我们手工接触IOC容器和索取正确实例的麻烦。

警告:要注意,在IOC容器中注册类的顺序非常重要,特别是使用重载函数创建实例时。如果在NavigationService 被注册之前创建MainViewModel ,那就会得到空引用的异常。

 

3. 替代SimpleIoc 

SimpleIoc库运行的很棒,是一个很酷的轻量级的MVVM Light添加物,而且它的确很轻量级。它是非常实用的方案但是对于比较大的应用就不那么好用了(或者你像我一样打算试一下替代它有多困难)。在这个例子中我会用AutoFac替代SimpleIocAutoFac是一个很有名且强大的IOC service


首先,我们要获取AutoFac库和extra库以像SimpleIoc一样使用ServiceLocator。因此不论从控制台还是用户界面为AutoFac添加CommonServiceLocator extraAutoFac库是被依赖库因此要安装正确。使用全新的Windows Phone8项目开始MVVM Light项目模板。


Code Snippet

1. Install-Package Autofac.Extras.CommonServiceLocator


仅仅需要被修改代码的一个地方在ViewModeeLocator

在这个新的ViewModelLocator构造函数中,我注释了原先SimpleIoc代码方便于两者对比。


Code Snippet

1. static ViewModelLocator()

2. {

3.     var container = newContainerBuilder();

4.  

5.     //ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

6.     ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container.Build()));

7.  

8.     if (ViewModelBase.IsInDesignModeStatic)

9.     {

10.         //SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();

11.         container.RegisterType<Design.DesignDataService>().As<IDataService>();

12.     }

13.     else

14.     {

15.         //SimpleIoc.Default.Register<IDataService, DataService>();

16.         container.RegisterType<DataService>().As<IDataService>();

17.     }

18.  

19.     //SimpleIoc.Default.Register<MainViewModel>();

20.     container.RegisterType<MainViewModel>();

21. }

我们声明了ContainerBuilder,把它设置为LocatorProvider。现在容器被用于注册我们需要的所有东西。通过注册创建实例的SimpleIoc重载,在AutoFac中呈现如下。


Code Snippet

1. container.RegisterInstance(newDataService()).As<IDataService>();

就这样,构造器注入能够像原来的SimpleIoc正确运行。


4. Built-in messages

MVVM Lightmessager能将类注册为Listeners并且能够将消息发送给这些类。这通常应用在viewmodel之间,一般我会创建各种类型的消息来发送,但是MVVM Light本身创建了很多messages供我们调用。


消息GenericMessage<T>(T content)能包含所有的类型。


Code Snippet

1. Messenger.Default.Send(newGenericMessage<string>("my message"));

消息NotificationMessage(string notification)包含notification,能被用于发送notification给notification工厂用以用合适的方式显示消息。

 


Code Snippet

1. try

2. {

3.     //try something

4. }

5. catch (Exception ex)

6. {

7.     Messenger.Default.Send(newNotificationMessage(ex.Message));

8. }

NotificationMessage<T>(T notification)可能同样是你需要的。

另外一个是 NotificationMessageAction(string notification, Action callback) 基于 NotificationMessage 但是你能添加回调行为,该行为会在消息被接收时启动。它也有类似于implementation的一般实现。

DialogMessage(string content, Action<MessageBoxResult> callback) 这个消息是让用户输入并在参数MessageBoxResult中返回结果。MessageBoxResult是System.Windows中的枚举。


Code Snippet

1. publicenumMessageBoxResult

2. {

3.   None = 0,

4.   OK = 1,

5.   Cancel = 2,

6.   Yes = 6,

7.   No = 7,

8. }

 

Code Snippet

1. Messenger.Default.Send(newDialogMessage("Are you sure?", result =>

2.     {

3.         if (result == MessageBoxResult.Yes)

4.         {

5.             //do something

6.         }

7.     }));

DialogMessage 继承于GenericMessage<string>。

PropertyChangedMessage 是使用在RaisePropertyChanged 的实现,多用于多个viewmodel需要响应属性变化时。


Code Snippet

1. publicstring WelcomeTitle

2. {

3.     get

4.     {

5.         return _welcomeTitle;

6.     }

7.  

8.     set

9.     {

10.         if (_welcomeTitle == value)

11.         {

12.             return;

13.         }

14.  

15.         Messenger.Default.Send(newPropertyChangedMessage<string>(_welcomeTitle, value"WelcomeTitle"));

16.  

17.         _welcomeTitle = value;

18.         RaisePropertyChanged(WelcomeTitlePropertyName);

19.     }

20. }

特别注意注册监听者,尝试使用尽可能多的不同消息类型是有意义的。你并不想让一个错误的监听者收到消息因为它可能恰好听到相同的信息。如下所示注册监听者。


Code Snippet

1. Messenger.Default.Register<PropertyChangedMessage<string>>(this, message =>

2.     {

3.         var a = message.NewValue;

4.         //do something

5.     } );

5. Portable libraries

MVVM Light在任何基于XAML平台都有效。并且目前迎来了便携版,便携版单独的类库在NuGet上。


Code Snippet

1. Install-Package Portable.MvvmLightLibs

如果你决定使用便携版,要确定你解决方案中每一个项目都要使用MVVM Light类库引用。它跟常用的MVVM Light类库并不共用。当你使用了PCL版本,你可把你的viewmodel在单独的、便携式的类库中和他们分享比如,Windows Store 或者Windows Phone app


6. Event to Command behavior

MVVM LightICommand的实现叫做RelayCommand,可用于绑定行为。比如XAML中的button有命令属性可用于绑定在ICommand类型的datacontext,因此当button被点击后ICommand会被启动。不幸的并非所有XAMLUI元素都有可绑定的属性用于他们能够触发的事件而且这是EventToCommand运行的地方。通过EventToCommand 你能绑定XAML UI元素所有事件到对应viewmodel中的ICommand实现中。


首先我们需要在XAML页面中添加两个namespaces

First we’ll need two namespaces in our XAML page

Code Snippet

1. xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

2. xmlns:command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP8"

下面介绍我们打算在stackpanel使用的Tap事件。


Code Snippet

1. <StackPanel Grid.Row="0" Orientation="Horizontal">

2.     <i:Interaction.Triggers>

3.         <i:EventTrigger EventName="Tap">

4.             <command:EventToCommand Command="{Binding GoToCommand}" CommandParameter="Edit" />

5.         </i:EventTrigger>

6.     </i:Interaction.Triggers>

第三行指出了我们想处理的事件,注意这是一个string所以要意识到(是否)错误。第四行绑定命令行为并且将参数传给了ICommand实现。


Code Snippet

1. privateRelayCommand<string> _goToCommand;

2. publicRelayCommand<string> GoToCommand

3. {

4.     get { return _goToCommand jQuery15206875578026641675_1366095632942 (_goToCommand = newRelayCommand<string>(NavigateAway)); }

5. }

方法NavigateAway 如下所示。


Code Snippet

1. privatevoid NavigateAway(string parameter)

参数以使用XAML中的单词“Edit”,我们甚至可以在第4行中从事件到命令直接传递事件参数。如下所示


Code Snippet

1. <command:EventToCommand PassEventArgsToCommand="True" Command="{Binding GoToCommand}" />

Windows Store applications并没有创造性的拥有这些行为,因此你不能使用EventToCommand除非从NuGet安装了Win8nl toolkitJoost Van SchaikWinRT中创建了他自己的行为实现,感谢他的成果(还有很多其他人在这个项目中给予帮助)我们现在能在WinRT中使用EventToCommand


7. DispatcherHelper

自从.net4.5我们就有了await/async关键字并且成了同步使用的良好的一员。也就意味着如果我们打算更新UI线程的活动我们需要使用Dispatcher类以在UI线程中调用我们的行为。一般情况下我们并不需要在自己的viewmodel类中使用DispatcherMVVM Light包含DispatcherHelper能在需要的时候执行UI 线程行为。


Code Snippet

1. DispatcherHelper.CheckBeginInvokeOnUI(() =>

2.     {

3.         //do something 

4.     });

DispatcherHelperApp.xaml.cs或者InitializePhoneApplication(WP8项目)方法中被初始化。

DispatcherHelper同样有个RunAsync ,跟CheckBeginInvokeOnUI的不同是CheckBeginInvokeOnUI首先检查是否已经在UI线程上了,如果在线程上就执行行为,如果不在UI线程上就调用RunAsync方法。


8. Blendable

MVVM Light有完整的Blend支持,也就是你可以从viewmodel拖放属性到view以生成binding,或者你能在设计时生成基于datacontext的数据。我并不数序Blend所以也不再详述,只要心中记着MVVM Light可以在Blend中构建就可以了。


9. Open Source

这点你可能知道但是MVVM Light是完全开源的。如果打算下载源代码查找地址http://mvvmlight.codeplex.com/ 。


10. Laurent is on Twitter and he’s a nice guy 

Laurent BugnionMVVM Light的创始者,也在Twitterhttps://twitter.com/LBugnion

Laurent Bugnion, the founder of MVVM Light, is on Twitter! 他很善聊也很渴望帮助任何需要帮助的人。


Conclusion

总结

MVVM Light是一个带有些许宝石伟大的库。在这篇文章中我讨论了8个可以让开发者生活简单的有趣话题,我保留了2个额外的项目,因为10这个数字比8要好。

 


原文地址: 点击打开链接
在 `MVVM Light` 中,可以使用 `Messenger` 类来实现两个窗体之间的通信。`Messenger` 是 `MVVM Light` 框架提供的一种消息传递机制,它能够让不同的对象之间进行通信,而不需要相互持有引用。以下是一个示例代码,用于在两个窗体之间传递数据: 首先,在发送窗体中定义一个 `Message` 类型,并在构造函数中初始化该类型的对象: ```csharp public class MyMessage { public string Data { get; set; } } public partial class SenderWindow : Window { public SenderWindow() { InitializeComponent(); DataContext = this; // 初始化 Message 对象 var message = new MyMessage { Data = "Hello, World!" }; // 发送消息 Messenger.Default.Send(message); } } ``` 然后,在接收窗体中注册 `Messenger`,并在回调方法中处理接收到的消息: ```csharp public partial class ReceiverWindow : Window { public ReceiverWindow() { InitializeComponent(); DataContext = this; // 注册消息接收器 Messenger.Default.Register<MyMessage>(this, OnMessageReceived); } private void OnMessageReceived(MyMessage message) { // 处理接收到的消息 MessageBox.Show(message.Data); } } ``` 在上面的示例中,我们在发送窗体中创建了一个 `MyMessage` 对象,并通过 `Messenger` 发送了该对象。在接收窗体中,我们注册了一个接收器,用于接收 `MyMessage` 类型的消息。当发送窗体发送消息时,接收窗体中的 `OnMessageReceived` 方法会被回调,然后我们可以在该方法中处理接收到的消息。 需要注意的是,在接收窗体中注册 `Messenger` 时,需要在窗体关闭时注销接收器,以避免内存泄漏: ```csharp protected override void OnClosed(EventArgs e) { Messenger.Default.Unregister<MyMessage>(this); base.OnClosed(e); } ``` 以上是使用 `MVVM Light` 实现两个窗体之间传值的示例代码,希望能够对你有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值