MVVM模式构架分为视图层V、视图模型层VM、模型层M,View主要用于界面呈现,ViewModel用于逻辑实现,Model用于数据的构造,而这三者能够进行通信,对于简单的应用大规模运用MVVM模式开始可能会降低开发效率,但Silverlight提供了强大的数据绑定机制,将View和ViewModel有效的联系起来,会大大提高开发效率,特别较为复杂的应用,MVVM模式并有助于应用程序的测试、维护和升级,使得设计人员专注于界面设计,开发人员专注于代码,他们并能很好的合作。
Model 是应用程序的核心,代表着最大、最重要的业务资产,因为它记录了所有复杂的业务实体、它们之间的关系以及它们的功能。Model 之上是 ViewModel。ViewModel 的两个主要目标分别是:使 Model 能够轻松被 WPF/XAML View 使用;将 Model 从 View 分离并对 Model 进行封装。这些目标当然非常好,但是由于一些现实的原因,有时并不能达到这些目标。
您构建的 ViewModel 知道用户在高层上将如何与应用程序交互。但是,ViewModel 对 View 一无所知,这是 MVVM 设计模式的重要部分。这使得交互设计师和图形设计师能够在 ViewModel 的基础上创建优美、有效的 UI,同时与开发人员密切配合,设计适当的 ViewModel 来支持其工作。此外,View 与 ViewModel 的分离还使得 ViewModel 更有利于单元测试和重用。
MVVM 模式中存在三个核心组件:模型、视图和视图模型。除了了解这三个组件的作用,还要了解这些组件相互之间如何交互,这也很重要。从最高层面上来说,视图“了解”视图模型,视图模型“了解”模型,但模型不了解视图模型,视图模型不了解视图,下图 演示了这三个组件之间的关系
Model 是应用程序的核心,代表着最大、最重要的业务资产,因为它记录了所有复杂的业务实体、它们之间的关系以及它们的功能。Model 之上是 ViewModel。ViewModel 的两个主要目标分别是:使 Model 能够轻松被 WPF/XAML View 使用;将 Model 从 View 分离并对 Model 进行封装。这些目标当然非常好,但是由于一些现实的原因,有时并不能达到这些目标。
您构建的 ViewModel 知道用户在高层上将如何与应用程序交互。但是,ViewModel 对 View 一无所知,这是 MVVM 设计模式的重要部分。这使得交互设计师和图形设计师能够在 ViewModel 的基础上创建优美、有效的 UI,同时与开发人员密切配合,设计适当的 ViewModel 来支持其工作。此外,View 与 ViewModel 的分离还使得 ViewModel 更有利于单元测试和重用。
MVVM 模式中存在三个核心组件:模型、视图和视图模型。除了了解这三个组件的作用,还要了解这些组件相互之间如何交互,这也很重要。从最高层面上来说,视图“了解”视图模型,视图模型“了解”模型,但模型不了解视图模型,视图模型不了解视图,下图 演示了这三个组件之间的关系
![](https://i-blog.csdnimg.cn/blog_migrate/7b73240001e183167347f0df435db0a0.gif)
是在 MVVM 模式中清楚地划分职责的一些好处:
• 在开发过程中,开发人员和设计人员可以更好地独立和并行处理各自的组件。设计人员可将精力集中在视图上,如果他们使用的是 Expression Blend,则可以轻松生成要处理的示例数据,而开发人员可处理视图模型和模型组件。
• 开发人员可为视图模型和模型创建单元测试而不使用视图。视图模型的单元测试可准确地执行视图使用的相同功能。
• 由于视图完全在 XAML 中实现,因此可以轻松地重新设计应用程序 UI 而不必改动代码。新版本的视图应可以与现有视图模型一起使用。
• 如果现有的模型实现封装了现有业务逻辑,则更改起来可能存在难度或风险。在这种情况下,视图模型将充当模型类的适配器,您可以通过它避免对模型代码做出重大更改。
一Model模型层
Model模型层是一个面向对象的实体类。如:
首先在Student.cs中简单声明了一个类
public class Student
{
public string name { get; set;}
public int age { get; set; }
public string class { get; set;}
}
类型定义好后,我们在Students.cs中得到一个Student的集合
public class Students
{
public List<Student> listStudent;
public List<Student> GetStudentS()
{
listStudent = new List<Student>()
{
new Student {name = "Tom", age = 21,Class=”一年级二班” },
new Student {name = "Jack", age = 22 ,Class=”一年级二班”},
new Student {name = "Rose", age = 23 ,Class=”一年级二班”},
};
return listStudent;
}
}
Model模型层是一个面向对象的实体类。如:
首先在Student.cs中简单声明了一个类
public class Student
{
public string name { get; set;}
public int age { get; set; }
public string class { get; set;}
}
类型定义好后,我们在Students.cs中得到一个Student的集合
public class Students
{
public List<Student> listStudent;
public List<Student> GetStudentS()
{
listStudent = new List<Student>()
{
new Student {name = "Tom", age = 21,Class=”一年级二班” },
new Student {name = "Jack", age = 22 ,Class=”一年级二班”},
new Student {name = "Rose", age = 23 ,Class=”一年级二班”},
};
return listStudent;
}
}
二、ViewModel视图模型层
ViewModel是视图模型层 这一层是对View视图层展现、数据的读取以及各种事件的处理。如:
public class ViewModel
{
public List<Students> vStudents { get; set; }
public ViewModel()
{
vStudents = new Students ().GetStudentS ();
}
}
ViewModel是视图模型层 这一层是对View视图层展现、数据的读取以及各种事件的处理。如:
public class ViewModel
{
public List<Students> vStudents { get; set; }
public ViewModel()
{
vStudents = new Students ().GetStudentS ();
}
}
Command命令一般都需要定义成独立的类来实现,然后再ViewModel上实例化
Command命令类的实现的方法可以继承ICommand 使用第三方组件的Command命令的类
继承ICommand 的语法如下
public class MyCommand<T> : ICommand
{
Action<T> Excuted;
Func<bool> canExcute;
public ShowMessageCommand(Action<T> excuted)
{
this.Excuted = excuted;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
//你的需要执行的代码
}
}
Command命令类的实现的方法可以继承ICommand 使用第三方组件的Command命令的类
继承ICommand 的语法如下
public class MyCommand<T> : ICommand
{
Action<T> Excuted;
Func<bool> canExcute;
public ShowMessageCommand(Action<T> excuted)
{
this.Excuted = excuted;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
//你的需要执行的代码
}
}
三、View视图层
View视图层是界面的设计,文件表现为xaml文件。
先在应用程序中定义为资源:
<Application.Resources>
<vmmodel: ViewModel x:Key=" vStudents "></vmmodel: ViewModel>
</ Application.Resources>
也可在具体页中定义。
<UserControl.Resources>
<vmmodel: ViewModel x:Key=" vStudents "></vmmodel: ViewModel>
<UserControl.Resources>
View视图层是界面的设计,文件表现为xaml文件。
先在应用程序中定义为资源:
<Application.Resources>
<vmmodel: ViewModel x:Key=" vStudents "></vmmodel: ViewModel>
</ Application.Resources>
也可在具体页中定义。
<UserControl.Resources>
<vmmodel: ViewModel x:Key=" vStudents "></vmmodel: ViewModel>
<UserControl.Resources>
页面引用:
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource vStudents }" >
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource vStudents }" >
实现绑定:
<DataTemplate>
<StackPanel>
<TextBlock HorizontalAlignment="Left" Name="textBlock1" Text="{Binding name}" VerticalAlignment="Top" />
<TextBlock HorizontalAlignment="Left" Name="textBlock2" Text="{Binding age}" VerticalAlignment="Top" />
<TextBlock HorizontalAlignment="Left" Name="textBlock2" Text="{Binding Class}" VerticalAlignment="Top" />
</StackPanel>
</DataTemplate>
案例6:MVVM模式
这是一个MVVM模式,在前面的讲解的基础上,实现更多的功能, MVVM模式重点是实现绑定,而其中的难点是命令绑定,下面举例说明命令绑定的实现。该例实现一个矩形的放大缩小。
(1) 新建一个Windows Phone Project。创建Command、ViewModel二个文件夹,用于创建对应于的文件,此类功能简单Model层不必创建、View层就直接用MainPage.xaml。
(2)在Model目录下创建类ExecuteCommandAction.cs,以便在首页实现绑定,主要代码如下:
public class ExecuteCommandAction:TriggerAction<FrameworkElement>
{
public static readonly DependencyProperty CommandNameProperty =
DependencyProperty.Register("CommandName", typeof(string), typeof(ExecuteCommandAction), null);
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExecuteCommandAction), null);
protected override void Invoke(object parameter)
{
if (AssociatedObject == null)
return;
ICommand command = null;
var dataContext = AssociatedObject.DataContext;//调用视图的上下文Content
foreach (var info in dataContext.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (IsCommandProperty(info) && String.Equals(info.Name, CommandName, StringComparison.Ordinal))//找到命为"AddRadius"的ICommand:AddRadius
{
command = (ICommand)info.GetValue(dataContext, null);
break;
}
}
if ((command != null) && command.CanExecute(CommandParameter))
{
command.Execute(CommandParameter);//运行AddRadius,AddRadius命令内容就是增加半径
}
}
private static bool IsCommandProperty(PropertyInfo property)
{
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExecuteCommandAction), null);
protected override void Invoke(object parameter)
{
if (AssociatedObject == null)
return;
ICommand command = null;
var dataContext = AssociatedObject.DataContext;//调用视图的上下文Content
foreach (var info in dataContext.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (IsCommandProperty(info) && String.Equals(info.Name, CommandName, StringComparison.Ordinal))//找到命为"AddRadius"的ICommand:AddRadius
{
command = (ICommand)info.GetValue(dataContext, null);
break;
}
}
if ((command != null) && command.CanExecute(CommandParameter))
{
command.Execute(CommandParameter);//运行AddRadius,AddRadius命令内容就是增加半径
}
}
private static bool IsCommandProperty(PropertyInfo property)
{
return typeof(ICommand).IsAssignableFrom(property.PropertyType);
}
(3)在ViewModel目录下创建视图模型类:
public class RadiusViewModel : INotifyPropertyChanged
{
private Double radius;
public RadiusViewModel()
{
Radius = 0;
AddRadius = new ActionCommand(p => Radius += 50);
ShuRadius = new ActionCommand(p => Radius -= 10);
}
public event PropertyChangedEventHandler PropertyChanged;
public ICommand AddRadius
{
get;
private set;
}
public ICommand ShuRadius
{
get;
private set;
}
public Double Radius
{
get
{
return radius;
}
set
{
{
private Double radius;
public RadiusViewModel()
{
Radius = 0;
AddRadius = new ActionCommand(p => Radius += 50);
ShuRadius = new ActionCommand(p => Radius -= 10);
}
public event PropertyChangedEventHandler PropertyChanged;
public ICommand AddRadius
{
get;
private set;
}
public ICommand ShuRadius
{
get;
private set;
}
public Double Radius
{
get
{
return radius;
}
set
{
radius = value;
OnPropertyChanged("Radius");
}
}
(5)在首页MainPage创建RadiusViewModel实体,声明为资源,在页面长方形的宽、高绑定其中的属性Radius:
<!--设置整个页面DataContext为视图模型类RadiusViewModel-->
(5)在首页MainPage创建RadiusViewModel实体,声明为资源,在页面长方形的宽、高绑定其中的属性Radius:
<!--设置整个页面DataContext为视图模型类RadiusViewModel-->
<phone:PhoneApplicationPage.DataContext>
<my:RadiusViewModel/>
</phone:PhoneApplicationPage.DataContext>
<Rectangle Fill="Blue"
Height="{Binding Radius}" Width="{Binding Radius}"
<my:RadiusViewModel/>
</phone:PhoneApplicationPage.DataContext>
<Rectangle Fill="Blue"
Height="{Binding Radius}" Width="{Binding Radius}"
HorizontalAlignment="Left" Margin="71,78,0,0" Name="ellipse1" Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" />
(6) 绑定命令,实现放大、缩小的功能
<Button Content="放大" Height="72" HorizontalAlignment="Left" Margin="71,468,0,0" Name="button2" VerticalAlignment="Top" Width="160" >
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="Click">
<my_Interactivity:ExecuteCommandAction CommandName="AddRadius"/>
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
</Button>
<Button Content="缩小" Height="72" HorizontalAlignment="Left" Margin="253,468,0,0" Name="button3" VerticalAlignment="Top" Width="160" >
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="Click">
<my_Interactivity:ExecuteCommandAction CommandName="ShuRadius"/>
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
</Button>
运行,可看到如下效果:
![](https://i-blog.csdnimg.cn/blog_migrate/04af42eb01ab3968b280bdde4a150e03.gif)
这是一个简单案例,主要实现了一个命令绑定,实际应用中可能更复杂,按照MVVM模式的思想编写的程序应该抛弃Xaml文件的code behind(即xaml.cs)文件,这样才能让开发和设计各尽其能,MVVM模式的View与ViewModel有三大通讯方式:Binding Data(实现数据的传递)、Command(实现操作的调用)和Attached Behavior(实现控件加载过程中的操作)。
在 www.CodePlex.com上有许多不错的第三方MVVM框架可供我们借鉴和使用,较常用的有下面两个:
MVVM Light Toolkit是帮助人们在Silverlight和WPF中使用MVVM设计模式的一套组件。它是一个轻量级的、务实的框架,只包含所需的必要组成部分。
下载地址: http://mvvmlight.codeplex.com/ 。
Simple MVVM Toolkit使开发应用MVVM设计模式Widnows Phone的应用程序变得更容易,为基于 MVVM设计模式的应用程序提供一个简单的框架和工具集。Simple MVVM Toolkit的特点是简单,但它包含执行 MVVM 设计模式的应用程序所需的一切。
下载地址: http://simplemvvmtoolkit.codeplex.com/ 。
转载于:https://blog.51cto.com/wanxl/670183