博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在UWP中实现自己的MVVM设计模式
阅读量:5065 次
发布时间:2019-06-12

本文共 16430 字,大约阅读时间需要 54 分钟。

原文:

其实写这篇博文的时候我是拒绝的,因为这牵扯到一个高大上的东西——"框架"。一说起这个东西,很多朋友就感觉有点蒙了,尤其是编程新手。因为它不像在代码里面定义一个变量那么显而易见,它是需要在你的整个程序架构上体现出来的,并且对于框架来说,并没有什么固定的代码格式,你可以这样写,当然也可以那样写。只要最终可以达到同样的效果,各个模块之间能够体现这种框架的思想就OK。所以当你都是用MVVM框得到两份架写的相同需求的Demo看时,发现里面的很多代码都不一样,请不要惊讶,因为你正在接触一个很抽象的东西,这种东西有的时候还真得你需要自己挖空心思去琢磨一下,光靠别人给你讲还是不行的!

--------------------------------切入正题--------------------------------

在进行搭建自己的MVVM框架的时候你需要提起掌握一下知识(至少要熟悉,如果未达标,建议先自行脑补一下,我可能不会做到面面俱到):

1、熟练掌握数据绑定;

2、熟练使用委托;

3、对MVVM框架有一定的了解;

 --------------------------------在你决定继续要往下看的时候我会默认你已经对上述知识有所了解------------------------------

一:为页面绑定数据

 按照规范的MVVM框架来说,一个项目中至少要有要有三个文件夹:View、ViewModel、Model;这三个文件夹分别对应该框架的三个组成部分,这一点没什么好说的。针对Model中的一些属性而言,如果想具有属性通知的功能的话就需要继承INotifyPropertyChanged接口,并需要自定义一个函数用于触发对应的PropertyChanged事件,一般情况下我们都会把这一部分封装到一个类中,供其它类来继承它。这样就避免出现代码冗余的问题。示例代码如下所示:

1                public                class ObservableObject : INotifyPropertyChanged 2                    { 3                public                event PropertyChangedEventHandler PropertyChanged; 4                public                void RaisePropertyChanged(string propertyName) 5                        { 6                if (PropertyChanged != null) 7                            { 8                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 9                            }10                        }11     }
View Code

接下来,我们就需要让对应的Model类来继承我们写的这个具有属性通知的类,示例代码如下所示:

 

1                public                class User:ObservableObject 2                    { 3                private                string _name; 4                public                string Name 5                        { 6                get { return _name; } 7                set             8                            { 9                 _name = value;10                 RaisePropertyChanged("Name");11                            }12                        }13            14                private                int _age;15            16                public                int Age17                        {18                get { return _age; }19                set            20                            {21                 _age = value;22                 RaisePropertyChanged("Age");23                            }24                        }25            26                public User(string name,int age)27                        {28                this.Name = name;29                this.Age = age;30                        }31            32                ///                            33                /// 给ViewModel提供一个获取Model中集合对象的接口34                ///                            35                ///                
36 public static ObservableCollection
GetUsers()37 {38 return new ObservableCollection
()39 {40 new User("hippieZhou",23),41 new User("小明",12),42 new User("老王",50)43 };44 }45 }
View Code

Model已经搭建完成,接着我们就开始搭建ViewModel,示例代码如下所示: 

 

1                public                class MainPageViewModel : ObservableObject 2                    { 3                private ObservableCollection
_users; 4 public ObservableCollection
Users 5 { 6 get { return _users; } 7 set 8 { 9 _users = value;10 RaisePropertyChanged("Users");11 }12 }13 14 public MainPageViewModel()15 {16 this.Users = User.GetUsers();17 }18 }
View Code

OK,是不是很简单,接下来就是View中的数据绑定,示例代码如下所示:

 

1                
2
3
4 5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
View Code

 

二:为页面绑定Command

写到这算是完成了1/3,界面是展示了数据,但是我们不能对它进行任何的操作,因此我们还需要让数据能够动态的增加和删除,接下来我们需要使用有关Command的相关知识了,首先,Command属于ButtonBase的一个字段,如果我们想为对应的Command进行绑定的话,那就需要这个绑定源对应的委托继承自ICommand接口,需要重写ICommand对应的两个接口函数。其次,由于ICommand提供了两个接口函数CanExecute和Execute,因此当CanExecute为false时候Execute是不被执行,此时绑定的Command是失效的,那么对应的控件应该自动处于禁用状态的,但是在WindowsStore类型的应用不再像WPF那样具有CommandManager的功能,不能自动触发CanExecuteChanged,这样就导致控件的颜色仍然不是禁用状态的颜色(尽管没有执行对应的函数),因此我们需要手动触发这个事件,来保证前台的控件的显示状态在指定的条件下发生改变。

在此,我们一般会采取封装的思想来处理这种情况,因此我选择封装一个类DelegateCommand,继承至ICommand,示例代码如下所示:

1                public sealed class DelegateCommand : ICommand 2                    { 3                        public event EventHandler CanExecuteChanged; 4         ///                      5                        /// 需要手动触发属性改变事件 6         ///                      7                        public void RaiseCanExecuteChanged() 8                        { 9                            if (CanExecuteChanged != null)10                            {11                                CanExecuteChanged(this, EventArgs.Empty);12                            }13                        }14            15         ///                     16                        /// 决定当前绑定的Command能否被执行17                        /// true:可以被执行18                        /// false:不能被执行19         ///                     20         /// 不是必须的,可以依据情况来决定,或者重写一个对应的无参函数                                21         /// 
22 public bool CanExecute(object parameter)23 {24 return this.MyCanExecute == null ? true : this.MyCanExecute(parameter);25 }26 27 /// 28 /// 用于执行对应的命令,只有在CanExecute可以返回true的情况下才可以被执行29 /// 30 /// 31 public void Execute(object parameter)32 {33 try34 {35 this.MyExecute(parameter);36 }37 catch (Exception ex)38 {39 #if DEBUG40 41 Debug.WriteLine(ex.Message);42 43 #endif44 }45 }46 47 /// 48 /// 49 /// 50 public Action MyExecute { get; set; }51 public Func
MyCanExecute { get; set; }52 53 ///
54 /// 构造函数,用于初始化55 /// 56 ///
57 ///
58 public DelegateCommand(Action
execute, Func
canExecute)59 {60 this.MyExecute = execute;61 this.MyCanExecute = canExecute;62 }63 }
View Code

 然后在我们的ViewModel中创建对应的Command就可以了,我们可以将ViewModel改造成下面这个样子:

 

1                public class MainPageViewModel : ObservableObject 2                    { 3         private ObservableCollection
_users; 4 public ObservableCollection
Users 5 { 6 get { return _users; } 7 set 8 { 9 _users = value;10 RaisePropertyChanged("Users");11 }12 }13 14 private DelegateCommand _addCommand;15 16 ///
17 /// 当当前集合项的个数小于5时允许用户继续添加,否则就不允许用户添加18 /// 19 public DelegateCommand AddCommand20 {21 get22 {23 return _addCommand ?? (_addCommand = new DelegateCommand24 ((Object obj) =>25 {26 //添加一条记录27 this.Users.Add(new User(DateTime.Now.ToString(),DateTime.Now.Hour));28 //手动触发CanExecuteChanged事件来改变对应控件的显示状态29 this._addCommand.RaiseCanExecuteChanged();30 this._delCommand.RaiseCanExecuteChanged();31 },32 (Object obj) => this.Users.Count < 5));33 }34 }35 36 ///
37 /// 当当前集合项的个数大于1时允许用户继续删除,否则就不允许用户删除38 /// 39 private DelegateCommand _delCommand;40 public DelegateCommand DelCommand41 {42 get43 {44 return _delCommand ?? (_delCommand =45 new DelegateCommand((Object obj) =>46 {47 //删除一条记录48 this.Users.RemoveAt(0);49 //手动触发CanExecuteChanged事件来改变对应控件的显示状态50 this._addCommand.RaiseCanExecuteChanged();51 this._delCommand.RaiseCanExecuteChanged();52 },53 (Object obj) => this.Users.Count > 1));54 }55 }56 57 public MainPageViewModel()58 {59 this.Users = User.GetUsers();60 }61 }
View Code

 并将对应的View改造成下面这个样子:

 

1                
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
View Code

 这个地方提醒新手朋友要注意的一个问题,如果你希望你的控件在指定条件下显示的状态不一样就需要手动触发CanExecuteChanged事件。

推荐链接:

             

三:Event To Command

接下来算是一个重点内容吧,如何将一个事件绑定到Command上? 这个问题很现实,并不是所有的控件都有Command属性,当一个控件只有Event而没有Command我们该怎么办?

我们现在需求是选中一项后弹出一个对话框,显示你选中项的相关信息(通过EventToCommand来实现)

这个微软为我们提供了一个解决方案,如果你安装了Blend工具,你可以把 目录C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1\ExtensionSDKs\BehaviorsXamlSDKManaged\12.0打开,你会发现有两个动态库很有用:Microsoft.Xaml.Interactions.dllMicrosoft.Xaml.Interactivity.dll;没错,就是它俩可以达成你的心愿,迅速将这两个动态库加入到工程中,然后你可以在你的XAML页面中进行绑定的,我把完整的代码罗列出来供大家参考:

1                
13
16
17
18
19 20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
View Code

这里面用到了一个非MVVM的知识:值转换器(只是为了弹出框能够显示我想要的数据而已,没什么其他的作用),示例代码如下所示:

 

1                ///                             2                /// 定义一个值转换器,用于将绑定的数据格式化为指定的格式 3                ///                             4                public                class ItemConverter : IValueConverter 5                    { 6                public                object Convert(object value, Type targetType, object parameter, string language) 7                        { 8             User user = value as User; 9                if (user != null)10                            {11                return user.Name;12                            }13                else            14                            {15                return                "you have not select!";16                            }17                        }18            19                public                object ConvertBack(object value, Type targetType, object parameter, string language)20                        {21                throw                new NotImplementedException();22                        }23     }
View Code

然后对应的命令写法和之前的是一样的,如下所示:

 

1                private DelegateCommand _showDialog; 2                public DelegateCommand ShowDialog 3                        { 4                get             5                            { 6                return _showDialog ?? (_showDialog= new DelegateCommand( 7                async (Object obj) => 8                                    { 9                await                new Windows.UI.Popups.MessageDialog(obj.ToString()).ShowAsync();10                                    },11                     (Object obj) => true));12                            }13         }
View Code

写到这,我们的MVVM框架已经搭建的差不多了,还算满意,我运行的效果是这样的(你的也是这样的吗?):

我不知道我用我的这种方式理解和设计应用程序的MVVM框架在诸位眼中是否规范,合法,还请高手不吝赐教呀:)!!!!

四:写在最后

 如果你能够熟练理解并能够将MVVM运用到自己的项目中,并计划使用第三方MVVM框架的话,我建议你使用MVVMLight,简单易用上手快,并且它已经支持UWP的项目模板了。我真的很佩服作者()的编码能力,我的很多思路都是从他的博客中获得灵感的,希望你也是如此!

GitHub  :

posted on
2018-08-03 00:03 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/lonelyxmas/p/9410965.html

你可能感兴趣的文章
设计器 和后台代码的转换 快捷键
查看>>
Monkey测试结果分析
查看>>
STL——配接器、常用算法使用
查看>>
STL容器之vector
查看>>
无法向会话状态服务器发出会话状态请求
查看>>
数据中心虚拟化技术
查看>>
01入门
查看>>
复习文件操作
查看>>
SQL Server 使用作业设置定时任务之一(转载)
查看>>
第二阶段冲刺-01
查看>>
BZOJ1045 HAOI2008 糖果传递
查看>>
发送请求时params和data的区别
查看>>
JavaScript 克隆数组
查看>>
eggs
查看>>
一步步学习微软InfoPath2010和SP2010--第七章节--从SP列表和业务数据连接接收数据(4)--外部项目选取器和业务数据连接...
查看>>
如何增强你的SharePoint 团队网站首页
查看>>
FZU 1914 Funny Positive Sequence(线性算法)
查看>>
oracle 报错ORA-12514: TNS:listener does not currently know of service requested in connec
查看>>
基于grunt构建的前端集成开发环境
查看>>
MySQL服务读取参数文件my.cnf的规律研究探索
查看>>