微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Silverlight MVVM 贴近实战(三)

我没有大牛搞懂原理源代码的本事,我只有一颗帮助别人的心。

今天我们来通过修改代码模块来介绍一下Command绑定以及viewmodel的作用。先看一张图,调一下胃口,看看修改密码页面

首先我们点击修改密码,弹出修改密码界面,那么我们肯定用的是双击,但是Silverlight无双击事件,只有左右键单击事件。怎么办呢,我们只能根据点击的时间间隔去做判断,如果小于200ms,则属于双击事件。看看代码

 
 
  1. dispatcherTimer doubleClickTimer;  
  2.         public MainPage()  
  3.         {  
  4.             InitializeComponent();  
  5.             doubleClickTimer = new dispatcherTimer();  
  6.             doubleClickTimer.Interval = new TimeSpan(0, 0, 200);  
  7.             doubleClickTimer.Tick += new EventHandler(DoubleClick_Timer);  
  8.         } 

在主页面的构造函数中,我们定义一个DoubleClickTimer,设置时间间隔为200ms,开始计时调用DoubleClick_Timer方法,如下

 
 
  1. private void DoubleClick_Timer(object sender, EventArgs e)  
  2.        {  
  3.            doubleClickTimer.Stop();  
  4.        } 

也就是200ms停止一次,如果你没有在这200ms内双击图标,则不会代开界面。看下面一段代码

 
 
  1. private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)  
  2.         {  
  3.             if (doubleClickTimer.IsEnabled)  
  4.             {  
  5.                 doubleClickTimer.Stop();  
  6.                 ModifyPassWordView modifyPassWordWindow = new ModifyPassWordView(userNo);  
  7.                 modifyPassWordWindow.Show();  
  8.             }  
  9.             else 
  10.             {  
  11.                 doubleClickTimer.Start();  
  12.             }  
  13.         } 

在点击图标的时候,判断如果doubleClickTimer目前可用,也就是还在200ms内的话,就打开页面,并且停止doubleClickTimer的tick事件。否则重新启动doubleClickTimer开始计时,这个功能其实就是对双击的一个模拟。好了,重点我们看看修改密码

先看设计界面UI代码

 
 
  1. <controls:ChildWindow x:Class="MISInfoManage.ModifyPassWordView" 
  2.            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
  3.            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
  4.            xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 
  5.            xmlns:LocalResource="clr-namespace:MISInfoManage.Resources" 
  6.            xmlns:viewmodel="clr-namespace:viewmodel;assembly=viewmodel" 
  7.            Width="400" Height="240"   
  8.            Title="修改密码"> 
  9.     <controls:ChildWindow.Resources> 
  10.         <LocalResource:MainPageResource x:Key="LocalResource"></LocalResource:MainPageResource> 
  11.     </controls:ChildWindow.Resources> 
  12.     <Grid x:Name="LayoutRoot" HorizontalAlignment="Center" Margin="0,15,0"> 
  13.         <Grid.ColumnDeFinitions> 
  14.             <ColumnDeFinition Width="Auto"/> 
  15.             <ColumnDeFinition Width="*"/> 
  16.         </Grid.ColumnDeFinitions> 
  17.         <Grid.RowDeFinitions> 
  18.             <RowDeFinition Height="Auto"/> 
  19.             <RowDeFinition Height="Auto"/> 
  20.             <RowDeFinition Height="Auto"/> 
  21.             <RowDeFinition Height="Auto"/> 
  22.             <RowDeFinition Height="Auto"/> 
  23.         </Grid.RowDeFinitions> 
  24.         <TextBlock Text="{Binding Tb_UserNo,Source={StaticResource LocalResource}}" HorizontalAlignment="Right" Grid.Row="0" Grid.Column="0" Margin="5,10,10" Style="{StaticResource PwdPageTextStyle}"/> 
  25.         <TextBlock Text="{Binding UserID,Mode=OneTime}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Left" Margin="0,0"/> 
  26.         <TextBlock Text="{Binding Tb_OldPwd,Source={StaticResource LocalResource}}" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" Style="{StaticResource PwdPageTextStyle}"/> 
  27.         <TextBox Text="{Binding OldPwd,Mode=TwoWay}" Grid.Row="1" Grid.Column="1" Width="200" HorizontalAlignment="Left" FontSize="12"/> 
  28.         <TextBlock Text="{Binding Tb_NewPwd,Source={StaticResource LocalResource}}" HorizontalAlignment="Right" Grid.Row="2" Grid.Column="0" Margin="5,10" Style="{StaticResource PwdPageTextStyle}"/> 
  29.         <TextBox Text="{Binding NewPwd,Mode=TwoWay}" Grid.Row="2" Grid.Column="2" Width="200" Margin="0,10" HorizontalAlignment="Left" FontSize="12"/> 
  30.         <TextBlock Text="{Binding Tb_ReNewPwd,Source={StaticResource LocalResource}}" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="0" Style="{StaticResource PwdPageTextStyle}"/> 
  31.         <TextBox Text="{Binding ReNewPwd,Mode=TwoWay}" Width="200" Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" FontSize="12"/> 
  32.         <StackPanel Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20,0"> 
  33.             <Button Content="{Binding Tb_Modify,Source={StaticResource LocalResource}}" Command="{Binding UpdatePwdCommands}" CommandParameter="1"  Width="80" Margin="0,0"></Button> 
  34.             <Button Content="{Binding Tb_Cancel,Source={StaticResource LocalResource}}" Command="{Binding CancelCommands}" Width="80"></Button> 
  35.         </StackPanel> 
  36.     </Grid> 
  37. </controls:ChildWindow> 

我们看到了,在界面上绑定了三个文本框,其Text属性均绑定viewmodel中的属性值。都是双向的(Two Way),UserID为一次性绑定(OneTime)。在这里我要说的是一次性绑定就是绑定一次,直到页面关闭其值也不会再有变化。单项绑定(OneWay)就是只要viewmodel的值变化,就会反映在UI上,而UI上值的变化不会反映到viewmodel。双向绑定则不管是UI上的变化还是viewmodel的变化,都会互相体现在对方的属性值上,相信我说的已经是通俗易懂。OK,那么现在我们看看修改密码界面的cs代码

 
 
  1. public partial class ModifyPassWordView:ChildWindow  
  2.     {  
  3.         private UserPwdInfo userPwdInfo;  
  4.         public ModifyPassWordView()  
  5.         {  
  6.             InitializeComponent();  
  7.         }  
  8.  
  9.         public ModifyPassWordView(string userNo)  
  10.             : this()  
  11.         {  
  12.             userPwdInfo = new UserPwdInfo();  
  13.             userPwdInfo.UserID = userNo;  
  14.             this.LayoutRoot.DataContext = userPwdInfo;  
  15.         }  
  16.     } 

在这里我们设置页面的数据源为userPwdInfo。那么页面的文本框什么的就可以绑定这个对象的属性。为什么只有这么点代码呢?不要忘了本节主要说的是viewmodel,大量的处理逻辑已经放在了viewmodel中,viewmodel其实是一个充血模型,界面所牵涉的所有业务逻辑都可以在viewmodel中完成,从而实现了UI与业务的分离。看上面的这段代码,我们声明了一个UserPwdInfo,其实这个是我定义的一个viewmodel类,代码如下

 
 
  1. namespace viewmodel  
  2. {  
  3.     public class UserPwdInfo : INotifyPropertyChanged  
  4.     {  
  5.         public event PropertyChangedEventHandler PropertyChanged;  
  6.         private string oldPwd;  
  7.         public string OldPwd  
  8.         {  
  9.             get 
  10.             {  
  11.                 return oldPwd;  
  12.             }  
  13.             set 
  14.             {  
  15.                 oldPwd = value;  
  16.                 NotifyPropertyChange("OldPwd");  
  17.             }  
  18.         }  
  19.  
  20.         private string newPwd;  
  21.         public string NewPwd  
  22.         {  
  23.             get 
  24.             {  
  25.                 return newPwd;  
  26.             }  
  27.             set 
  28.             {  
  29.                 newPwd = value;  
  30.                 NotifyPropertyChange("NewPwd");  
  31.             }  
  32.         }  
  33.  
  34.         private string reNewPwd;  
  35.         public string ReNewPwd  
  36.         {  
  37.             get 
  38.             {  
  39.                 return reNewPwd;  
  40.             }  
  41.             set 
  42.             {  
  43.                 reNewPwd = value;  
  44.                 NotifyPropertyChange("ReNewPwd");  
  45.             }  
  46.         }  
  47.  
  48.         private void NotifyPropertyChange(string property)  
  49.         {  
  50.             if (PropertyChanged != null)  
  51.             {  
  52.                 PropertyChanged(thisnew PropertyChangedEventArgs(property));  
  53.             }  
  54.         }  
  55.  
  56.         public string userID;  
  57.         public string UserID  
  58.         {  
  59.             get 
  60.             {  
  61.                 return userID;  
  62.             }  
  63.             set 
  64.             {  
  65.                 userID = value;  
  66.                 NotifyPropertyChange("UserID");  
  67.             }  
  68.         }  
  69.  
  70.         public ICommand UpdatePwdCommands  
  71.         {  
  72.             get 
  73.             {  
  74.                 UpdatePwdCommand updatePwdCommand = new UpdatePwdCommand(delegate()  
  75.                 {  
  76.                     if (string.IsNullOrEmpty(OldPwd))  
  77.                     {  
  78.                         CommonMessage.ShowInfo("旧密码不能为空!");  
  79.                         return;  
  80.                     }  
  81.                     if (string.IsNullOrEmpty(NewPwd))  
  82.                     {  
  83.                         CommonMessage.ShowInfo("请输入新密码!");  
  84.                         return;  
  85.                     }  
  86.                     if (string.IsNullOrEmpty(ReNewPwd))  
  87.                     {  
  88.                         CommonMessage.ShowInfo("请重复输入新密码!");  
  89.                         return;  
  90.                     }  
  91.                     if (!Validate.IsCorrectPwd(this.NewPwd))  
  92.                     {  
  93.                         CommonMessage.ShowInfo("新密码格式不正确!");  
  94.                         return;  
  95.                     }  
  96.                     if (!NewPwd.Equals(ReNewPwd))  
  97.                     {  
  98.                         CommonMessage.ShowInfo("新密码和重复密码不一致!");  
  99.                         return;  
  100.                     }  
  101.                     LoginDAL.Instance.GetUser(this.UserID, (obj, args) =>  
  102.                     {  
  103.                         if (args.Error == null)  
  104.                         {  
  105.                             Stream stream = args.Result;  
  106.                             User loginUser = SeriealizeHelper<User>.JsonDeserialize<User>(stream);  
  107.                             if (loginUser != null)  
  108.                             {  
  109.                                 string passWordEncrypt = loginUser.user_password;  
  110.                                 Cryptor cryptor = new Cryptor();  
  111.                                 string passWord = cryptor.Decrypt(passWordEncrypt.tochararray());  
  112.                                 if (!passWord.Equals(OldPwd))  
  113.                                 {  
  114.                                     CommonMessage.ShowInfo("旧密码不正确!");  
  115.                                     return;  
  116.                                 }  
  117.                                 ModifyPwd();  
  118.                             }  
  119.                         }  
  120.                     });  
  121.                 }, delegate(string userId) { return !string.IsNullOrEmpty(userId); });  
  122.                 return updatePwdCommand;  
  123.             }  
  124.         }  
  125.  
  126.         public ICommand CancelCommands  
  127.         {  
  128.             get 
  129.             {  
  130.                 CancelCommand cancelCommand = new CancelCommand(delegate()  
  131.                 {  
  132.                     this.OldPwd = string.Empty;  
  133.                     this.NewPwd = string.Empty;  
  134.                     this.ReNewPwd = string.Empty;  
  135.                 });  
  136.                 return cancelCommand;  
  137.             }  
  138.         }  
  139.  
  140.         public static ManualResetEvent allDone = new ManualResetEvent(false);  
  141.         private void ModifyPwd()  
  142.         {  
  143.             WebRequest myWebRequest = WebRequest.Create("Http://localhost:37206/MISInfoService/ModifyPwd");  
  144.             RequestState myRequestState = new RequestState();  
  145.             myRequestState.request = myWebRequest;  
  146.             myWebRequest.ContentType = "application/json";  
  147.             myRequestState.request.Method = "POST";  
  148.             IAsyncResult r = (IAsyncResult)myWebRequest.BeginGetRequestStream(  
  149.                 new AsyncCallback(ReadCallback), myRequestState);  
  150.             allDone.WaitOne();  
  151.             IAsyncResult myWebResponse = myWebRequest.BeginGetResponse(new AsyncCallback(ResponseCallBack), myRequestState);  
  152.         }  
  153.         private void ReadCallback(IAsyncResult asynchronousResult)  
  154.         {  
  155.             RequestState myRequestState = (RequestState)asynchronousResult.AsyncState;  
  156.             WebRequest myWebRequest = myRequestState.request;  
  157.             Stream requestStream = myWebRequest.EndGetRequestStream(asynchronousResult);  
  158.             string jsonString = SeriealizeHelper<ModifyPwdRequest>.JsonSerialize<ModifyPwdRequest>(new ModifyPwdRequest() { UserID = UserID, Pwd = NewPwd });  
  159.             byte[] buffer = System.Text.Encoding.Unicode.GetBytes(jsonString);  
  160.             requestStream.Write(buffer, buffer.Length);  
  161.             requestStream.Close();  
  162.             allDone.Set();  
  163.         }  
  164.  
  165.         private void ResponseCallBack(IAsyncResult asynchronousResult)  
  166.         {  
  167.             RequestState myRequestState = (RequestState)asynchronousResult.AsyncState;  
  168.             WebRequest myWebRequest = myRequestState.request;  
  169.             WebResponse webResponse = myWebRequest.EndGetResponse(asynchronousResult);  
  170.             using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))  
  171.             {  
  172.                 string result = reader.ReadToEnd();  
  173.                 result = result.Replace("\"""");  
  174.                 JsonModel jsonModel = new JsonModel(result);  
  175.                 string suc = jsonModel.GetValue("suc");  
  176.                 if (suc == "1")  
  177.                 {  
  178.                     new Thread(() =>  
  179.                     {  
  180.                         UisynchronizationContext.Context.Post((state) =>  
  181.                         {   
  182.                             MessageBox.Show(jsonModel.GetValue("msg"));  
  183.                         }, null);  
  184.                     }).Start();  
  185.                     //MessageBox.Show(jsonModel.GetValue("msg"));  
  186.                 }  
  187.             }  
  188.         }  
  189.     }  
  190.     public class RequestState  
  191.     {  
  192.         public WebRequest request;  
  193.         public RequestState()  
  194.         {  
  195.             request = null;  
  196.         }  
  197.     }  

看到了吧,这里面包含了很多逻辑。首先我要说的是Command绑定,我们在viewmodel中定义了两个Command,一个针对修改按(UpdatePwdCommands ),一个针对取消按钮(CancelCommands)。UpdatePwd构造函数中的第一个参数delegate()是一个匿名委托,用来实现修改密码功能,第二个参数delegate(string userId)用来判断是否可以执行Command命令,我们看看UpdatePwdCommands 类的代码你就明白了

 
 
  1. public class UpdatePwdCommand : ICommand  
  2.     {  
  3.         public event EventHandler CanExecuteChanged;  
  4.         public delegate void UpdatePwdhandler();  
  5.         public delegate bool CanExecuteHandler(string UserID);  
  6.         public CanExecuteHandler canExecuteHandler;   
  7.         public event UpdatePwdhandler UpdatePwdChange;  
  8.         public UpdatePwdCommand(UpdatePwdhandler handler,CanExecuteHandler canExecuteHandler)  
  9.         {  
  10.             this.UpdatePwdChange = handler;  
  11.             this.canExecuteHandler = canExecuteHandler;  
  12.         }  
  13.  
  14.         public bool CanExecute(object parameter)  
  15.         {  
  16.             if (canExecuteHandler != null)  
  17.             {  
  18.                 return canExecuteHandler(parameter.ToString());  
  19.             }  
  20.             return true;  
  21.         }  
  22.  
  23.         public void Execute(object parameter)  
  24.         {  
  25.             if (CanExecute(parameter))  
  26.             {  
  27.                 UpdatePwdChange();  
  28.             }  
  29.         }  
  30.     } 

我声明了一个委托public delegate bool CanExecuteHandler(string UserID);在CanExecute方法中判断如果委托指向了某个方法,那么久执行这个方法来判断Command是否可以执行。在viewmodel中我们通过指定UpdatePwdCommand构造函数的第二个参数,来确定Command是否可以执行:delegate(string userId) { return !string.IsNullOrEmpty(userId); }如果不能执行,则修改按钮为disable状态。同样的取消按钮也是这样的一个逻辑。我们看到在页面上我们给修改按钮和取消按钮绑定了Command,如下

 
 
  1. <Button Content="{Binding Tb_Modify,0"></Button> 
  2. <Button Content="{Binding Tb_Cancel,Source={StaticResource LocalResource}}" Command="{Binding CancelCommands}" Width="80"></Button> 

 这样当我们点击修改按钮的时候,就会调用UpdatePwdCommand类的Execute方法,在Execute方法触发UpdatePwdChange事件,而这个UpdatePwdChange的原型是我们UserPwdInfo中UpdatePWdCommand构造函数的第一个参数,如下

 
 
  1. public ICommand UpdatePwdCommands  
  2.         {  
  3.             get 
  4.             {  
  5.                 UpdatePwdCommand updatePwdCommand = new UpdatePwdCommand(delegate()  
  6.                 { ......}
  7. }
  8. }

ok,Command看完了,我们看看调用WCF的部分,WCF部分代码如下,在这里我的WCF服务代码具有如下特点,无SVC文件,无ABC配置。看看代码

 
 
  1. namespace WCFService  
  2. {  
  3.     [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]  
  4.     [ServiceContract]  
  5.     [ServiceBehavior(IncludeExceptionDetailInFaults = true)]  
  6.     public class UserService  
  7.     {  
  8.         MISInfoEntities misInfoEntities;  
  9.         public UserService()  
  10.         {  
  11.             misInfoEntities = new MISInfoEntities();  
  12.         }  
  13.  
  14.         [OperationContract]  
  15.         [WebInvoke(UriTemplate = "ModifyPwd",   
  16.             Method = "POST"
  17.             RequestFormat=Webmessageformat.Json, 
  18.             ResponseFormat=Webmessageformat.Json)]  
  19.         public string ModifyPwd(ModifyPwdRequest request)  
  20.         {  
  21.             Users user = misInfoEntities.users.Find(request.UserID);  
  22.             user.user_password = Cryptor.Encrypt(request.Pwd);  
  23.             misInfoEntities.SaveChanges();  
  24.             return "{suc:1,msg:'修改成功!'}";  
  25.         }  
  26.     }  

在这里需要注意的是    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]这个Attribute设置为Allowed,并且webConfig也要设置。

 
 
  1. <system.serviceModel> 
  2.     <serviceHostingEnvironment aspNetCompatibilityEnabled="true"></serviceHostingEnvironment> 
  3.     <standardEndpoints> 
  4.       <webHttpEndpoint> 
  5.         <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/> 
  6.       </webHttpEndpoint> 
  7.     </standardEndpoints> 

如果有什么不明白,请看我的文章WCF目录。大家看到了我的WCF是Rest风格的,那么参数ModifyPwdRequest 就无需标记DataContract。我们只需要告诉客户端它的序列化方式为RequestFormat=Webmessageformat.Json,请求的时候使用json序列化方式,同理ResponseFormat=Webmessageformat.Json,响应客户端的时候也是json格式。

所以在客户端调用的时候,我们采用如上面viewmodel中ModifyPwd方法调用方式

 myWebRequest.ContentType = "application/json";  myRequestState.request.Method = "POST";

设置请求发送数据的格式以及httpMethod。在ReadCallBack方法中,

 
 
  1. Stream requestStream = myWebRequest.EndGetRequestStream(asynchronousResult);  
  2.             string jsonString = SeriealizeHelper<ModifyPwdRequest>.JsonSerialize<ModifyPwdRequest>(new ModifyPwdRequest() { UserID = UserID, buffer.Length);  
  3.             requestStream.Close(); 

我们序列化一个和服务端一模一样的对象,将它写进请求流中,服务端接收到这个流以后,根据格式序列化为ModifyPwdRequest对象,调用修改密码方法。OK,当调用完以后进入ResponseCallBack方法,重点看这段代码

 
 
  1. using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))  
  2.             {  
  3.                 string result = reader.ReadToEnd();  
  4.                 result = result.Replace("\"""");  
  5.                 JsonModel jsonModel = new JsonModel(result);  
  6.                 string suc = jsonModel.GetValue("suc");  
  7.                 if (suc == "1")  
  8.                 {  
  9.                     new Thread(() =>  
  10.                     {  
  11.                         UisynchronizationContext.Context.Post((state) =>  
  12.                         {   
  13.                             MessageBox.Show(jsonModel.GetValue("msg"));  
  14.                         }, null);  
  15.                     }).Start();  
  16.                     //MessageBox.Show(jsonModel.GetValue("msg"));  
  17.                 }  
  18.             } 

获取到服务端响应流以后,我们读成字符串,然后通过json解析,读取json对象的值。最后显示修改成功,在显示修改成功的时候,因为viewmodel和界面UI不在一个线程上,如果直接调用MessageBox.Show将会抛出异常。所以我们采取如下的办法,首先在App启动类里面

 
 
  1. public App()  
  2.        {  
  3.            this.Startup += this.Application_Startup;  
  4.            this.UnhandledException += this.Application_UnhandledException;  
  5.            UisynchronizationContext.Context = SynchronizationContext.Current;  
  6.            InitializeComponent();  
  7.        } 

我们获取当前线程的实例,UisynchronizationContext是我定义的一个类,如下

 
 
  1. namespace Client.Common  
  2. {  
  3.     public static class UisynchronizationContext    
  4.     {  
  5.         public static SynchronizationContext Context { getset; }  
  6.     }  

 所以当修改密码成功以后,我们获取当前UI线程,并弹出提示信息。OK,今天就讲到这里,如果有什么不明白,可以找我要源代码。看看执行效果

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐