Mvvm Light Toolkit for wpfsilverlight系列之Messenger

Mvvm Light Toolkit for wpfsilverlight系列之Messenger,第1张

概述在开发Wpf/SL应用时,经常会遇到不同页面和窗体之间的参数传递的问题。对于这类问题,我们一般通过事件实现数据传递,也可以定义全局静态变量来进行数据共享。这里我们则使用了另外一种非常高效而优雅的方法来进行消息传递,这里我称之为Messenger,事实上,Messenger并非mvvm的专利,我们可以把它看作一种设计模式,你可以在其它.net程序中使用它。   一、Mvvm Light Messen

在开发Wpf/SL应用时,经常会遇到不同页面和窗体之间的参数传递的问题。对于这类问题,我们一般通过事件实现数据传递,也可以定义全局静态变量来进行数据共享。这里我们则使用了另外一种非常高效而优雅的方法来进行消息传递,这里我称之为Messenger,事实上,Messenger并非mvvm的专利,我们可以把它看作一种设计模式,你可以在其它.net程序中使用它。

 

一、Mvvm light Messenger是什么

 

通过Mvvm light源码我们可以知道Messenger的实现细节,如果你现在还不能理解这些代码也没关系,很多东西理解起来远比使用起来难,Messenger也是如此,它使用起来很简单,由于Messenger只公开了一些消息注册和发送方法,使用者一看便知方法的功能,而只需关注要发送的数据和接收的对象就可以了。

发送: 

[c-sharp]  view plain copy Messenger.Default.Send<bool?>(true);  

接收:

copy Messenger.Default.Register<this, m => this.DialogResult = m);  

这是最基本的用法,发送方发送了一个bool?类型的对象(值为true),这样任何只要注册了bool?类型消息的地方都可以接收到这个消息。

Send泛型方法很好理解,只是发送一个值为true的bool?类型的对象; Register泛型方法接受2个参数,第一个是接受者,也就是消息的载体,通常是对象本身(this),当然也可以是其他已实例化的对象,第二个参数是Action类型的对象,是接收到消息后执行的方法委托

Register方法实际上将对象和Action方法添加到全局的字典集合当中,只不过他们关系是弱引用的关系,在Send方法获取对象引用,同时执行Action方法,有关弱引用的介绍,参考弱引用。

 

Messenger通过全局的字典集合来保存弱引用关系,因此在对象不使用时,我们要养成清理的习惯,调用Unregister来从字典集合中移除引用关系。

copy Messenger.Default.Unregister(this);  

 

二、应用示例

 

下面我们会通过登录界面实现和简单的列表增删改的功能来演示Messenger的用法:

1、登录部分:

首先创建Loginviewmodel,类定义如下:

WPF:

copy #region ICommand            public RelayCommand<object> LoginCommand         {             get              {                 return new RelayCommand<object>(                     (p) =>                      {                         System.windows.Controls.PasswordBox pb = p as System.windows.Controls.PasswordBox;                            bool islogon = false;                         // 登录成功                         if (_username == "admin" && pb.Password == "123")                             islogon = true;   else                            // 发送消息                         Messenger.Default.Send<bool?>(islogon);                      }                     );              }         }   public RelayCommand CancelCommand   get   new RelayCommand(                     () =>    null);                      );                 #endregion           #region 公共属性         public const string UsernamePropertyname = "Username";   private string _username = "";   string Username         {             get             {                 return _username;             }   set   if (_username == value)                 {                     return;                 }                 _username = value;                 // Update bindings, no broadcast                 RaisePropertyChanged(UsernamePropertyname);         }         #endregion  

SL:

copy  string> LoginCommand    {              {            string>(                (p) =>                 {                                                        if (_username == "admin" && p == "123")                        islogon =                                       Messenger.Default.Send<bool>(islogon);                 }                );         }    }                () =>                 {                     System.windows.browser.HTMLPage.Window.Invoke("close");                 }                );        }    }       #endregion      #region 公共属性   string UsernamePropertyname = "Username";   string _username = "";   string Username   return _username;   set   if (_username == value)            {                return;            }            _username = value;                        RaisePropertyChanged(UsernamePropertyname);    #endregion  

接着创建Login窗体,将按钮命令绑定到Loginviewmodel对应的Command,注意WPF中不能绑定PasswordBox的Password属性,因此我们将PasswordBox作为参数传递给Loginviewmodel,这种写法不符合mvvm的思想,不过基本只有这里需要这么写,也无伤大雅,页面代码如下:

WPF:

[xhtml]  copy <StackPanel GrID.Row="1" GrID.ColumnSpan="2" OrIEntation="Horizontal"                HorizontalAlignment="Center">     TextBlock Text="用户名:" VerticalAlignment="Center"/>     TextBox Text="{Binding Username,Mode=TwoWay}"            WIDth="150" VerticalAlignment="Center"            margin="5,2,5,2"/>   </StackPanel>   StackPanel GrID.Row="2" GrID.ColumnSpan="2" OrIEntation="Horizontal"    TextBlock Text="密  码:" VerticalAlignment="Center"PasswordBox x:name="password" WIDth="150" VerticalAlignment="Center"                   margin="5,2" PasswordChar="*"  StackPanel GrID.Row="3" GrID.ColumnSpan="2"                OrIEntation="Horizontal"                HorizontalAlignment="Center" VerticalAlignment="Center"button Content="登录" Command="{Binding LoginCommand}"              CommandParameter="{Binding Elementname=password}"              WIDth="100" margin="5,0); background-color:inherit; Font-weight:bold">button Content="取消" Command="{Binding CancelCommand}"    >  

SL中只有传递参数不一样:

copy button Content="登录" Command="{Binding LoginCommand}"            CommandParameter="{Binding Password,Elementname=password}"            WIDth="100" margin="5,0); background-color:inherit; Font-weight:bold">/>  

 

最后在app.xaml.cs中添加登录逻辑

WPF(需要在xaml中去除StartupUri):

copy protected overrIDe voID OnStartup(StartupEventArgs e)   {       base.OnStartup(e);       // 首先显示登录控件       Login login = new Login();       Loginviewmodel loginviewmodel = new Loginviewmodel();       login.DataContext = loginviewmodel;       // 将Login设置为主窗体   this.MainWindow = login;       MainWindow.Show();   // 注册消息,接收bool类型的参数,true为登录成功       Messenger.Default.Register<bool?>(                   m =>           {               // 登录成功后,显示主页面               if (m.HasValue && m.Value)               {                   // 更改主窗体                   this.MainWindow = new MainWindow();                   // 关闭登录窗体                   login.Close();   // 清理释放Login资源                   loginviewmodel.Cleanup();                   login = null;                   MainWindow.Show();               }               else if (!m.HasValue)               {                   MainWindow.Close();           }           );   }   voID OnExit(ExitEventArgs e)       Messenger.Default.Unregister(this);       base.OnExit(e);   }  

SL:

copy voID ApplicationStartup(object sender, StartupEventArgs e)       GrID rootvisual = new GrID();        Login login =      rootvisual.Children.Add(login);       RootVisual = rootvisual;   // 注册消息,接收bool类型的参数,true为登录成功       Messenger.Default.Register<bool>(                   m =>            {               // 登录成功后,显示主页面   if (m)   // 移除登录控件                   rootvisual.Children.Clear();   // 添加主页面                   rootvisual.Children.Add(new MainPage());              dispatcherHelper.Initialize();   static voID ApplicationExit(    Messenger.Default.Unregister(sender);       viewmodelLocator.Cleanup();   }  

 

到这里登录功能就实现了,关键地方就是在添加登录逻辑的地方,通过匿名方法和Lamda表达式,注册一个消息的执行方法就像写方法代码一样简单,只不过消息里的方法要等到send命令发送后才会执行

 

2、通过ChilDWindow实现列表增删改

SL中模式对话框通过ChilDWindow来实现,WPF通过Window的ShowDialog方法实现,这里我通过模拟SL的ChilDWindow来实现WPF的模式对话框,有关如何在WPF中模拟SL的ChilDWindow,参考:在WPF中模拟SL的ChildWindow效果

代码比较多,这里就不贴代码了,我的示例代码中都有详细的注释,下面主要说说一些需要关键的地方,也算是我的一些心得:

 

首先是Messenger的一些方法重载:

voID Send<TMessage>(TMessage message);

发送值为message的TMessage类型的消息

 

voID Send<TMessage,TTarget>(TMessage message);

 

发送值为message的TMessage类型的消息,但是接收对象必须是TTarget类型的对象

 

public virtual voID Send<TMessage>(TMessage message,object token)

发送值为message的TMessage类型的消息,与前面不同的是接收对象注册的消息方法拥有相同的token值才能接收到消息值

 

voID Register<TMessage>(object recipIEnt,Action<TMessage> action);

 

注册接收TMessage类型消息的方法,recipIEnt是消息载体,也就是接收消息的对象,action是消息执行方法的委托,该委托接受TMessage类型的参数,也就是Send发送的值

 

voID Register<TMessage>(object recipIEnt,bool receiveDerivedMessagesToo,Action<TMessage> action);

注册接收TMessage类型消息的方法,与上面不同的是receiveDerivedMessagesToo指定是否能够接收TMessage派生类型的对象作为消息的值

 

public virtual voID Register<TMessage>(object recipIEnt,object token,Action<TMessage> action)

注册接收TMessage类型消息的方法,与前面不同的是必须与Send方法相匹配的token才能接收该Send的消息值

 

public virtual voID Register<TMessage>(object recipIEnt, bool receiveDerivedMessagesToo,Action<TMessage> action)

 

Mvvmlight中还封装了一种特殊的消息类型NotificationMessageAction<TMessage>,通过它可以发送一些复杂的对象,并且可以包含回调函数,此示例中在对话框中发送确定的消息给主界面,主界面调用子对象的方法执行数据库 *** 作,如果成功则关闭对话框,如果失败则执行回调函数,将错误信息返回给对话框并显示出来

 

最后需要主要注意的就是什么使用Messenger比较合适,例如在此示例中:

 

与d出的对话框进行交互,我会将主界面作为消息的载体,原因如下:

d出对话框的生命周期较短,因此将d出对话框作为发送方,总能发送到它的宿主页面

d出对话框一般需要重复打开,在d出对话框中注册消息方法会增加消息清理的成本,即在每次关闭对话框时要对消息进行清理,否则每打开一次对话框,消息执行次数会递增

 

 

本章节示例代码下载地址:示例下载 

总结

以上是内存溢出为你收集整理的Mvvm Light Toolkit for wpf/silverlight系列之Messenger全部内容,希望文章能够帮你解决Mvvm Light Toolkit for wpf/silverlight系列之Messenger所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1019112.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存