zoukankan      html  css  js  c++  java
  • 搭建简易MVVM项目

      由于最近一直在学习Windows Phone相关知识,而伴随着WIN8的发布,新一代的编程使得很多语言使用唯一的核心库“Winmd”以及可以基于WINRT之上的AppStore环境设计。

      而MVVM是一种架构模式,主要在WPF、Silverlight和WP7开发里使用,它的目标是从视图层移除几乎所有代码隐藏(code-behind)。交互设计师可以专注于使用XAML表达用户体验需求,然后创建和视图模型的绑定,而视图模型则是由应用程序开发者开发和维护的,最大好处之一是分离关注点,以便用户体验设计师和应用程序开发者可以并行工作。

      稍微了解MVVM以后,我们就开始着手实现MVVM吧。实现的功能就挑选登录功能吧。

      项目结构如下:

      

      首先我们创建一个LoginUser类,由于需要绑定到相关的控件上,且在控件的值或实体类属性发生改变时,需要得到同步的更新,因此我们需要让实体类实现INotifyPropertyChange接口,代码如下:

    View Code
     1 public class BaseModel : INotifyPropertyChanged
     2 {
     3     public event PropertyChangedEventHandler PropertyChanged;
     4 
     5     protected void OnPropertyChanged(string propertyName)
     6     {
     7         if (null != PropertyChanged)
     8         {
     9             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    10         }
    11     }
    12 }
    13 
    14 
    15 public class LoginUser : BaseModel
    16 {
    17     private string m_Name;
    18     public string Name
    19     {
    20         set
    21         {
    22             if (null != value)
    23             {
    24                 m_Name = value;
    25                 OnPropertyChanged("Name");
    26             }
    27         }
    28         get { return m_Name; }
    29     }
    30 
    31     private string m_Password;
    32     public string Password
    33     {
    34         set
    35         {
    36             if (null != value)
    37             {
    38                 m_Password = value;
    39                 OnPropertyChanged("Password");
    40             }
    41         }
    42         get { return m_Password; }
    43     }
    44 }

      接着我们要使用xmal编码界面,具体的UI代码如下:

    View Code
     1 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"
     2       HorizontalAlignment="Center"
     3       VerticalAlignment="Center">
     4 
     5     <Grid.RowDefinitions>
     6         <RowDefinition Height="auto"/>
     7         <RowDefinition Height="auto"/>
     8         <RowDefinition Height="auto"/>
     9     </Grid.RowDefinitions>
    10     <Grid.ColumnDefinitions>
    11         <ColumnDefinition Width="60"/>
    12         <ColumnDefinition Width="120"/>
    13         <ColumnDefinition Width="180"/>
    14     </Grid.ColumnDefinitions>
    15 
    16     <TextBlock Name="txtblkName"
    17                Grid.Row="0"
    18                Grid.Column="0"
    19                VerticalAlignment="Center"
    20                Text="用户名"/>
    21     <TextBox Name="txtName"
    22              Grid.Row="0"
    23              Grid.Column="1"
    24              Grid.ColumnSpan="2"/>
    25 
    26     <TextBlock Name="txtblkPwd"
    27                Grid.Row="1"
    28                Grid.Column="0"
    29                VerticalAlignment="Center"
    30                Text="密码"/>
    31     <TextBox Name="txtPwd"
    32              Grid.Row="1"
    33              Grid.Column="1"
    34              Grid.ColumnSpan="2"/>
    35 
    36     <Button Grid.Row="2"
    37             Grid.Column="0"
    38             Grid.ColumnSpan="2"
    39             Content="登录"/>
    40     <Button Grid.Row="2"
    41             Grid.Column="2"
    42             Content="重置"/>
    43 </Grid>

      从XAML代码中,我们需要绑定2个文本的Text以及要绑定2个Button的事件,对于要绑定到控件的对象必须实现ICommand接口。

      登录的时候,我们需要判断用户名密码是否正确,如果正确的话,则执行登录并跳转到其他的页面,如果帐号密码不正确则要求提示,代码如下:

    View Code
     1 public class LoginCommand : ICommand                                                    
     2 {                                                                                       
     3     private LoginUser m_User = null;                                                    
     4     public event EventHandler CanExecuteChanged;                                        
     5                                                                                         
     6     public LoginCommand(LoginUser user)                                                 
     7     {                                                                                   
     8         m_User = user;                                                                  
     9     }                                                                                   
    10                                                                                         
    11     public bool CanExecute(object parameter)                                            
    12     {                                                                                   
    13         return true;                                                                    
    14     }                                                                                   
    15                                                                                         
    16     public void Execute(object parameter)                                               
    17     {                                                                                   
    18         if (m_User.Name != "ahl5esoft" || m_User.Password != "123456")                  
    19         {                                                                               
    20             MessageBox.Show("帐号或密码错误!");                                         
    21         }                                                                               
    22         else                                                                            
    23         {                                                                               
    24             MessageBox.Show("登录成功");                                                
    25         }                                                                               
    26     }                                                                                   
    27                                                                                         
    28     public void RaiseCanExecuteChanged()                                                
    29     {                                                                                   
    30         if (null != CanExecuteChanged)                                                  
    31         {                                                                               
    32             CanExecuteChanged(this, EventArgs.Empty);                                   
    33         }                                                                               
    34     }                                                                                   
    35 }                                                                                       

      重置按钮跟登录代码差不多,只是Execute对LoginUser重新实例化了,这里就省略了。有以上的Command实现,我们不难发现,其实大部分的实现代码都是差不多,只是实现的CanExecute、Execute方法有所不同,那么我们可以重构出一个基类,代码如下:

    View Code
     1 public class DelegateCommand : ICommand                                      
     2 {                                                                            
     3     private readonly Func<bool> m_canExecute;                                
     4     private readonly Action m_execute;                                       
     5     public event EventHandler CanExecuteChanged;                             
     6                                                                              
     7     public DelegateCommand(Action execute, Func<bool> canExecute = null)     
     8     {                                                                        
     9         if (null == execute)                                                 
    10             throw new ArgumentNullException("execute", "Cannot be null");    
    11                                                                              
    12         m_execute = execute;                                                 
    13         m_canExecute = canExecute;                                           
    14     }                                                                        
    15                                                                              
    16     [DebuggerStepThrough]                                                    
    17     public bool CanExecute(object parameter)                                 
    18     {                                                                        
    19         return null == m_canExecute || m_canExecute();                       
    20     }                                                                        
    21                                                                              
    22     public void Execute(object parameter)                                    
    23     {                                                                        
    24         m_execute();                                                         
    25     }                                                                        
    26                                                                              
    27     public void RaiseCanExecuteChanged()                                     
    28     {                                                                        
    29         if (null != CanExecuteChanged)                                       
    30         {                                                                    
    31             CanExecuteChanged(this, EventArgs.Empty);                        
    32         }                                                                    
    33     }                                                                        
    34 }                                                                            

      以上我们用一个Action替代了Execute内部的实现,然后用Func<bool>来替代CanExecute的实现,但是大家这时候会发现,如果我们在绑定Command的时候传入参数那该怎么办呢,因此我们这里还要提供一个泛型版本的基类,来处理传入参数的情况,代码如下:

    View Code
     1 public class DelegateCommand<T> : ICommand
     2 {
     3     private readonly Predicate<T> m_canExecute;
     4     private readonly Action<T> m_execute;
     5     public event EventHandler CanExecuteChanged;
     6 
     7     public DelegateCommand(Action<T> execute, Predicate<T> canExecute = null)
     8     {
     9         if (null == execute)
    10             throw new ArgumentNullException("execute", "Cannot be null");
    11 
    12         m_execute = execute;
    13         m_canExecute = canExecute;
    14     }
    15 
    16     public bool CanExecute(object parameter)
    17     {
    18         return null == m_canExecute || m_canExecute((T)parameter);
    19     }
    20 
    21     public void Execute(object parameter)
    22     {
    23         m_execute((T)parameter);
    24     }
    25 
    26     public void RaiseCanExecuteChanged()
    27     {
    28         if (null != CanExecuteChanged)
    29         {
    30             CanExecuteChanged(this, EventArgs.Empty);
    31         }
    32     }
    33 }

      完成了以上的Command基类以后,我们只要删除原有的LoginCommand,并相应的修改LoginUserViewModel内的代码,修改如下:

    View Code
     1 private ICommand m_LoginCommand = null;
     2 public ICommand LoginCommand
     3 {
     4     get
     5     {
     6         if (null == m_LoginCommand)
     7         {
     8             m_LoginCommand = new DelegateCommand(() =>
     9             {
    10                 if (m_User.Name != "ahl5esoft" || m_User.Password != "123456")
    11                 {
    12                     MessageBox.Show("帐号或密码错误!");
    13                 }
    14                 else
    15                 {
    16                     MessageBox.Show("登录成功");
    17                 }
    18             });
    19         }
    20         return m_LoginCommand;
    21     }
    22 }

      完成了ViewModel,我们只要将ViewModel赋值给Page的DataContext,并调整XAML内的绑定就将这个简易的MVVM完成啦,XAML代码如下:

    View Code
     1 <TextBlock Name="txtblkName"
     2            Grid.Row="0"
     3            Grid.Column="0"
     4            VerticalAlignment="Center"
     5            Text="用户名"/>
     6 <TextBox Name="txtName"
     7          Grid.Row="0"
     8          Grid.Column="1"
     9          Grid.ColumnSpan="2"
    10          Text="{Binding Path=User.Name, Mode=TwoWay}"/>
    11 
    12 <TextBlock Name="txtblkPwd"
    13            Grid.Row="1"
    14            Grid.Column="0"
    15            VerticalAlignment="Center"
    16            Text="密码"/>
    17 <PasswordBox Name="txtPwd"
    18              Grid.Row="1"
    19              Grid.Column="1"
    20              Grid.ColumnSpan="2"
    21              Password="{Binding Path=User.Password, Mode=TwoWay}" />

      以上在绑定TextBox以及PasswordBox的时候,我们选择了TwoWay的方式,是为了在控件的值或LoginUser的值发生改变时,都能收到通知,使控件与LoginUser的值能达到同步。

      另一方面,对于MVVM不足之处在于它对于UI操作比较简单的情况有点杀鸡用牛刀的感觉,数据绑定有点难以调试,以及大量使用数据绑定可能带来性能问题等等,虽然有着众多的不足,但是能理解它也是有好处的。

      那么今天的简易MVVM就到这里啦,由于大部分的代码已经贴出,因此就不再提供源代码了,如有什么误解或者错误的观点还望指出,谢谢。

  • 相关阅读:
    模态视图-多视图应用
    linux下查看文件夹的大小
    CentOS的字符集locale的设置
    CentOS 7 设置中文环境
    GCC中文错误提示
    ubuntu的交换分区和系统休眠
    CentOS使用EPEL YUM源
    git将远程仓库最新版本拉到本地仓库
    git的全局变量
    ssh:Permissions 0644 for ‘/root/.ssh/id_rsa’ are too open
  • 原文地址:https://www.cnblogs.com/ahl5esoft/p/2684527.html
Copyright © 2011-2022 走看看