搭建简易MVVM项目

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

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

  稍微了解MVVM以后,我们就开始着手实现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就到这里啦,由于大部分的代码已经贴出,因此就不再提供源代码了,如有什么误解或者错误的观点还望指出,谢谢。

你可能感兴趣的:(项目)