背景:
需求:实现从数据库读取级联表指定字段数据,并展示到前台界面。
VM层做业务逻辑层,每页最多获取2条数据。
View层只有数据表格,上一页与下一页按钮,且上一页与下一页在特定条件下不可用。
(转载请注明来源:cnblogs coder-fang)
解决方案结构如下:
-
-
- 项目结构:
- WPFTest:主要是界面显示数据(V层),ViewModel是wpftest的VM层,unittest做vm及数据的测试项目。
- 示例用的数据结构如图:
- 创建类库VM项目,为了简化,这里将M层与VM层放到了同一项目中,首先在VM中使用EF框架生成相关数据实体与context(EF自行查阅,这里不多介绍),即M层,项目目录如下:
- 为了使V层(展示层)与核心业务控制解耦,需要在VM层实现对业务的控制,建通用Command类,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace ViewModels
{
public class Command :ICommand
{
private Action methodToExecute = null;
private Func<bool> methodCanExecute = null;
public Command(Action methodToExecute, Func<bool> methodCanExecute)
{
this.methodToExecute = methodToExecute;
this.methodCanExecute = methodCanExecute;
}
public void Execute(object parameter)
{
this.methodToExecute();
}
public bool CanExecute(object parameter)
{
if (this.methodCanExecute == null)
{
return true;
}
else
{
return this.methodCanExecute();
}
}
public event EventHandler CanExecuteChanged;
public void RaseCanExecuteChangedEvent()
{
if (this.CanExecuteChanged != null)
{
this.CanExecuteChanged(this, EventArgs.Empty);
}
}
}
}
View Code
这里的command主要参数为命令调用的函数委托,是否可执行的函数委托
- 创建DatagridVM,是显示层主要的数据提供者,与业务控制者,代码如下:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Input;
using ViewModels;
namespace ViewModels
{
public class GridMember
{
public string Name { get; set; }
public string Role { get; set; }
public string Depart { get; set; }
}
public class DatagridVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private List<GridMember> _griddata;
public Command preCmd {get;private set;}
public Command nextCmd { get; private set; }
public Action<String> errCallback { get; set; }
private int _curpage = 1;
private int _total = 0;
public void getData()
{
using (dbEntities ctx = new dbEntities())
{
try
{
Total = ctx.user.Count();
var users = (from c in ctx.user orderby c.id select new GridMember{ Name = c.username, Role = c.Role1.rolename, Depart = c.deprtment.departname }).Skip((CurPage - 1) * 2).Take(2);
foreach (var item in users)
{
Console.WriteLine(item.Name);
}
this.GridData = users.ToList();
}
catch (Exception e)
{
if (errCallback != null)
errCallback(e.Message+"\r\n"+e.StackTrace);
}
}
}
public List<GridMember> GridData
{
get { return _griddata; }
set { _griddata = value; OnPropertyChanged("GridData"); }
}
public int CurPage
{
get { return _curpage; }
set { _curpage = value;
OnPropertyChanged("CurPage");
preCmd.RaseCanExecuteChangedEvent();
nextCmd.RaseCanExecuteChangedEvent();
}
}
public int Total
{
get { return _total; }
set { _total = value; OnPropertyChanged("Total"); }
}
public DatagridVM()
{
preCmd = new Command(() =>
{
CurPage--;
getData();
}, () => { return (bool)(CurPage > 1); });
nextCmd = new Command(() =>
{
CurPage++;
getData();
}, () => { return (bool)(CurPage * 2 < Total); });
getData();
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
}
}
View Code
注:其中GridMember为VM需要从数据库中查询的(多表联合后)数据字段,也是显示层要显示的字段,且创建了两个命令,用来实现上一页与下一页的业务逻辑,在CurPage改变时,需要发出一个事件,即相关命令更新自己的可执行状态。
- 至此,VM层已完成,下面创建显示层,创建简单的WPF窗口项目:
-
此项目需引用ViewModels,创建新窗口,Datagrid.xaml,界面代码如下:
<Window x:Class="WPFTest.Datagrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ViewModels;assembly=ViewModels"
Title="Datagrid" Height="311.417" Width="528.358">
<Window.Resources>
<local:DatagridVM x:Key="VM"/>
</Window.Resources>
<Grid Name="Grid" DataContext="{StaticResource VM}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="10" Orientation="Horizontal" >
<Button Name="Pre" Command="{Binding Path=preCmd}" >上一页</Button>
<Button Name="Next" Command="{Binding Path=nextCmd}" >下一页</Button>
</StackPanel>
<DataGrid Name="usersGrid" Grid.Row="1" ItemsSource="{Binding Path=GridData}"></DataGrid>
</Grid>
</Window>
View Code
注意这里的两个button,并没有实现click,反而使用命令绑定,自动调用了VM的执行函数,自动更新可执行状态,这就解耦了界面与业务。
- 整个界面已完成,是的,UI只需要编辑这个文件即可,已经将业务与UI分离了出来。
-
运行效果:首页:最后一页:
下面进行对VM层的单元测试
-
创建C#的单元测试项目,并引用viewmodel:
-
创建DataGridVMTest,代码如下:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ViewModels;
using System.Collections.Generic;
using System.Linq;
namespace UnitTest
{
[TestClass]
public class DataGridVMTest
{
[TestMethod]
public void testFunc()
{
DatagridVM vm = new DatagridVM();
vm.errCallback = (e) => { Console.WriteLine("出现异常:"+e); };
Assert.AreEqual(vm.CurPage, 1);
Assert.AreEqual(vm.Total, 7);
Assert.IsFalse(vm.preCmd.CanExecute(null));
Assert.IsTrue(vm.nextCmd.CanExecute(null));
vm.CurPage = 3;
Assert.IsTrue(vm.preCmd.CanExecute(null));
Assert.IsTrue(vm.nextCmd.CanExecute(null));
vm.nextCmd.Execute(null);
Assert.IsTrue(vm.preCmd.CanExecute(null));
Assert.IsFalse(vm.nextCmd.CanExecute(null));
Assert.AreEqual(vm.GridData.Count, 1);
}
}
}
View Code
注:数据库中有7条记录,每页显示2条,所以在testFunc中,分别测试不同页码时,preCmd与nextCmd的可执行状态,且在最后一页时测试获取数据的Count
-
执行结果:
本次实践已完成。
总结:MVVM前期需要花费一定的工作量,但带来的效果是显而易见的,当然,是否使用MVVM进行开发还需要很多其它因素的参考,希望大家灵活运用。
|
请发表评论