【我们一起写框架】MVVM的WPF框架(四)—DataGrid
前言
这个框架写到这里,应该有很多同学发现,框架很多地方的细节,其实是违背了MVVM的设计逻辑的。
没错,它的确是违背了。
但为什么明知道违背设计逻辑,还要这样编写框架呢?
那是因为,我们编写的是框架,是使用MVVM的概念编写框架,而并不是要完美的实现MVVM设计。
两者有什么区别呢?区别就是前者是实战,后者只是个理念。
在实战架构中,并不是UI的东西都一定要放在UI层写,逻辑的东西放在逻辑层写的。因为,架构的目的是让程序员更好的写代码,而不是让代码死死的固定在某一层。
所以,我们在编写框架时,设计模式中该切割的东西,就不要犹豫的切割。因为,架构师是设计模式的使用者,而不是被使用者。
举个例子,当你的逻辑全部提取到某一层中以后,你突然发现,该逻辑执行过程中要弹出提示框,但提示框又是属于UI层的,此时你犹豫了,把提示框移动到逻辑层,不符合设计理念,但不在逻辑层做,开发又很难受。
遇到这样的情况,我们该怎么做呢?
很简单,让设计理念去死吧,不要犹豫,直接把弹出提示框封装到逻辑层中即可。
现实中,设计逻辑永远是要向开发逻辑低头的,因为实战永远高于理论。
框架是什么?
框架就是规则,规则在人类社会被称之为法律;换言之,框架是代码界的法律。
人类社会建立法律之初,是抱着人人守法,秩序稳定的理想的。
可现实是残酷的,总有人,因为各种原因,践踏法律。
事实上,代码界也一样,总是会那不守规矩的程序员触犯法律,他们会让代码跨边界引用类库,或者拒绝使用接口声明对象等等。
为什么不能准守规则呢?
因为他们想更快速的完成任务,所以他们不惜触犯法律,也要拼一次一夜暴富。。。
所以,架构师作为代码界的人民警察,一定要做好惩治工作。。。
因为,当一个坏代码出现后,马上就会有若干个类似的坏代码出现,犹如劣币逐良币一样,时间一长,框架就会被破坏。
接着好代码就得依赖着坏代码写。
当坏代码多了到一定程度,好代码就会变成Bug了。。。
所以,任重道远,人民警察还需警惕。。。
为什么要编写数据控件
我们之前编写的数据控件功能相对单一;完全可以用属性和事件代替,所以有些同学会觉得,数据控件好像没什么用。
其实不然,现实中我们要处理的逻辑,并不是简单的对象属性一对一绑定就能处理解决的。
我们需要做很多操作,其中也包括UI操作。而数据控件就是用来应对这种复杂的UI操作的。
因为数据控件通过绑定UI控件后,已经将复杂的UI操作,变成了简单的数据逻辑操作了。
如果没有数据控件,那当我们实现一个控件联动时,就得在Xaml.cs文件中处理了。
如果该控件联动还要触发数据变化,那我们就又得从Xaml.cs文件中,穿越回ViewModel中处理逻辑了;亦或者,我们直接在Xaml.cs文件中处理数据逻辑。
不论哪种模式,都会将我们好容易做的逻辑层与UI层混淆到一起。而这个问题,并不是一个弹出框那么简单的UI越界问题,因为它包含了更多复杂的业务逻辑。
数据控件解决这个烦恼。
我们通过数据控件,实现了控件是控件,数据是数据,清晰的,层次分离;并且通过简洁的绑定,实现了数据变化与控件变化同步。
DataGrid数据控件
DataGrid数据控件可以说是数据控件的精髓了,因为DataGrid相对复杂,不像其他的数据控件那样功能单一。
所以,当然我们学习了DataGrid数据控件后,就可以更好的理解,数据控件的意义了。
下面我们先看下DataGrid数据控件的代码:
public class DataGrid<T> : Control<T>
{
private Action<T> LoadAction = null;
public Action<T> SelectCallBack = null;
private Func<object, bool> DataFilter = null; #region 分页
private volatile int _CurrentPage = 1;
public int CurrentPage
{
get { return _CurrentPage; }
set
{
_CurrentPage = value;
if (_CurrentPage > PageCount)
{
_CurrentPage = PageCount;
}
if (_CurrentPage < 1)
{
_CurrentPage = 1;
}
OnPropertyChanged();
}
}
private int _PageCount = 1;
public int PageCount { get { return _PageCount; } set { _PageCount = value; OnPropertyChanged(); } }
private int _RecordCount = 0;
public int RecordCount
{
get { return _RecordCount; }
set
{
_RecordCount = value;
if (_RecordCount <= SkipNumber)
{
PageCount = 1;
}
else
{
PageCount = int.Parse(Math.Ceiling((double)RecordCount / (double)SkipNumber).ToString());
}
if (_CurrentPage > PageCount)
{
_CurrentPage = PageCount;
}
OnPropertyChanged();
}
}
private int _SkipNumber = 30;
public int SkipNumber { get { return _SkipNumber; } set { _SkipNumber = value; OnPropertyChanged(); } }
private TextBox<string> _JumpTextBox = new TextBox<string>();
public TextBox<string> JumpTextBox
{
get { return _JumpTextBox; }
set { _JumpTextBox = value; OnPropertyChanged(); }
}
#region 跳页
public BaseCommand JumpCommand
{
get
{
return new BaseCommand(JumpCommand_Executed);
}
}
void JumpCommand_Executed(object send)
{
int pagenum = 0; if (int.TryParse(JumpTextBox.Text, out pagenum))
{
if (pagenum <= PageCount && pagenum > 0)
{
CurrentPage = pagenum; if (LoadAction != null)
{
LoadAction(Condition);
}
}
else
{
MessageBox.Show("请正确填写跳转页数。", "提示信息");
}
}
else
{
MessageBox.Show("请正确填写跳转页数。", "提示信息");
}
}
#endregion
#region 上一页
public BaseCommand PreviousCommand
{
get
{
return new BaseCommand(PreviousCommand_Executed);
}
}
void PreviousCommand_Executed(object send)
{
if (CurrentPage > 1)
{
CurrentPage -= 1;
if (LoadAction != null)
{
LoadAction(Condition);
}
}
else
{
MessageBox.Show("已至首页。", "提示信息");
}
}
#endregion #region 下一页
public BaseCommand NextCommand
{
get
{
return new BaseCommand(NextCommand_Executed);
}
}
void NextCommand_Executed(object send)
{
if (CurrentPage < PageCount)
{
CurrentPage += 1; if (LoadAction != null)
{
LoadAction(Condition);
}
}
else
{
MessageBox.Show("已至末页。", "提示信息");
}
}
#endregion
#endregion private ObservableCollection<T> _ItemsSource = new ObservableCollection<T>();
public ObservableCollection<T> ItemsSource
{
get { return _ItemsSource; }
set
{
_ItemsSource = value;
if (_ItemsSource != null && _ItemsSource.Count > 0 && SelectedItem == null)
{
SelectedItem = _ItemsSource.First();
}
OnPropertyChanged();
}
}
public void SetItemsSource(List<T> itemSource)
{
ItemsSource = new ObservableCollection<T>(itemSource);
}
public T _SelectedItem;
public T SelectedItem
{
get { return _SelectedItem; }
set
{
_SelectedItem = value;
if (SelectCallBack != null)
{
SelectCallBack(_SelectedItem);
}
OnPropertyChanged();
}
}
private ICollectionView _ItemsSourceView;
public ICollectionView ItemsSourceView
{
get
{
_ItemsSourceView = CollectionViewSource.GetDefaultView(_ItemsSource);
return _ItemsSourceView;
}
set
{
_ItemsSourceView = value;
OnPropertyChanged();
}
}
private T _Condition = (T)Activator.CreateInstance(typeof(T));
public T Condition { get { return _Condition; } set { _Condition = value; OnPropertyChanged(); } } #region 方法
public DataGrid()
{
}
public void BindSource(Action<T> loadAction, T conditionRow = default(T))
{
LoadAction = loadAction;
if (LoadAction != null)
{
CurrentPage = 1;
LoadAction(conditionRow);
}
}
public void BindSource(Action loadAction)
{
LoadAction = new Action<T>((obj) => {
loadAction();
}); ;
if (LoadAction != null)
{
CurrentPage = 1;
LoadAction(default(T));
}
}
public void ItemsSourceReBind()
{
BindSource(LoadAction);
}
public void SelectedItemReBind()
{
T newitem = (T)Activator.CreateInstance(typeof(T));
List<System.Reflection.PropertyInfo> plist = typeof(T).GetProperties().ToList(); foreach (var propertyInfo in plist)
{
propertyInfo.SetValue(newitem, propertyInfo.GetValue(SelectedItem));
}
SelectedItem = newitem;
}
public void SetFilter(Func<object, bool> dataFilter)
{
try
{
DataFilter = dataFilter;
_ItemsSourceView = CollectionViewSource.GetDefaultView(_ItemsSource);
_ItemsSourceView.Filter = new Predicate<object>(DataFilter);
}
catch(Exception ex)
{ }
}
public void Refresh()
{
if (_ItemsSourceView == null)
{
_ItemsSourceView = CollectionViewSource.GetDefaultView(this.ItemsSource);
}
_ItemsSourceView.Refresh();
}
#endregion
}
从代码中我们可以看到,DataGrid控件不仅包含了基础属性,还包含了上一页,下一页,刷新,甚至过滤的功能。
下面,我们看下一下DataGrid控件的基础应用。
Xaml页面代码如下:
<DataGrid Margin="5" FontSize="12" ItemsSource="{Binding TestDataGrid.ItemsSource}" AutoGenerateColumns="True"
SelectedItem="{Binding TestDataGrid.SelectedItem}" > </DataGrid>
ViewModel页面代码如下:
public DataGrid<User> TestDataGrid { get; set; }
TestDataProxy proxy = new TestDataProxy();
public VM_PageDataGrid()
{
TestDataGrid = new DataGrid<User>();
int currentPage = TestDataGrid.CurrentPage;
int skipNumber = TestDataGrid.SkipNumber;
proxy.GeDataGridData(null, currentPage, skipNumber, (list, count, msg) =>
{
TestDataGrid.SetItemsSource(list);
TestDataGrid.RecordCount = count;
});
TestDataGrid.SelectCallBack = (user) =>
{
MessageBox(user.Name);
};
}
我们可以看到,基础的DataGrid应用很简单,只要设置好绑定,然后将读取的数据赋值给数据控件的ItemSource属性即可。(这里我们使用SetItemSource方法为ItemSource赋值)
然后我们会发现,只要我们操作数据控件的ItemSource,不论是增加数据,删除数据,变更数据,页面都会自动的同步刷新。
DataGrid的中级应用
我们在上面的代码中可以看到,DataGrid数据控件还包含了分页功能。那么如何实现分页功能呢。
很简单,我们只需要在Xaml页面多绑定几个属性即可实现。
Xaml代码如下:
<StackPanel DataContext="{Binding TestDataGrid}" Orientation="Horizontal" DockPanel.Dock="Bottom" HorizontalAlignment="Right" Margin="0,10,0,0">
<Button Content="上一页" Width="60" Command="{Binding PreviousCommand}" Height="20" Margin="20,0,0,0" VerticalAlignment="Top" />
<Button Content="下一页" Width="60" Command="{Binding NextCommand}" Height="20" Margin="20,0,0,0" VerticalAlignment="Top" />
<TextBlock VerticalAlignment="Center" Text="每页" Margin="20,0,0,0"></TextBlock>
<TextBlock VerticalAlignment="Center" Text="{Binding SkipNumber}" Margin="0,0,0,0"></TextBlock>
<TextBlock VerticalAlignment="Center" Text="条"></TextBlock>
<TextBlock VerticalAlignment="Center" Text="{Binding CurrentPage}" Margin="20,0,0,0"></TextBlock>
<TextBlock VerticalAlignment="Center" Text="/"></TextBlock>
<TextBlock VerticalAlignment="Center" Text="{Binding PageCount}"></TextBlock>
<TextBlock VerticalAlignment="Center" Text="总记录数:" Margin="20,0,0,0"></TextBlock>
<TextBlock VerticalAlignment="Center" Text="{Binding RecordCount}"></TextBlock>
<TextBox VerticalAlignment="Center" Width="40" Height="20" Margin="40,0,0,0" Text="{Binding JumpTextBox.Text}" ></TextBox>
<Button Content="GO" Command="{Binding JumpCommand}" Width="40" Height="20" Margin="5,0,0,0" VerticalAlignment="Top" />
</StackPanel>
<GroupBox DockPanel.Dock="Top" Header="DataGrid" Margin="10,0,0,0" >
<DataGrid Margin="5" FontSize="12" ItemsSource="{Binding TestDataGrid.ItemsSource}" AutoGenerateColumns="True"
SelectedItem="{Binding TestDataGrid.SelectedItem}" >
</DataGrid>
</GroupBox>
这样我们就实现了分页功能,代码很简单,并且彻底分割了UI和ViewModel。
但是那么复杂的UI,就这样简单的被彻底搞定了吗?
当然是不可能的!UI很复杂,仅仅靠数据控件是无法彻底搞定的。
那么我们应该怎么办呢?
很简单,我们去编写UI控件就好啦。
当然,我们要编写的UI控件不是普通的UI控件,而是配合数据控件应用的UI控件。
这种定制UI控件在功能上与其他自定义控件是一样,但好处就在于,编写方便,易于理解和二次开发。
----------------------------------------------------------------------------------------------------
本篇文章就先讲到这了,下一篇文章我们将一起为框架编写UI控件。
框架代码已经传到Github上了,并且会持续更新。
相关文章:
To be continued——DataGrid
Github地址:https://github.com/kiba518/KibaFramework
----------------------------------------------------------------------------------------------------
注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

【我们一起写框架】MVVM的WPF框架(四)—DataGrid的更多相关文章
- 【我们一起写框架】MVVM的WPF框架(五)—完结篇
前言 这篇文章是WPF框架系列的最后一篇,在这里我想阐述一下我对框架设计的理解. 我对框架设计的理解是这样的: 框架设计不应该局限于任何一种设计模式,我们在设计框架时,应该将设计模式揉碎,再重组:这样 ...
- 【我们一起写框架】MVVM的WPF框架(一)—序篇
前言 我想,有一部分程序员应该是在二三线城市的,虽然不知道占比,但想来应该不在少数. 我是这部分人群中的一份子. 我们这群人,面对的客户,大多是国内中小企业,或者政府的小部门.这类客户的特点是,资金有 ...
- 【我们一起写框架】MVVM的WPF框架(二)—绑定
MVVM的特点之一是实现数据同步,即,前台页面修改了数据,后台的数据会同步更新. 上一篇我们已经一起编写了框架的基础结构,并且实现了ViewModel反向控制Xaml窗体. 那么现在就要开始实现数据同 ...
- 【我们一起写框架】MVVM的WPF框架(三)—数据控件
这世上,没人能一次性写出完美无缺的框架:因为,任何一个框架都需要项目的淬炼,然后才能升华,趋近完美. 所以,框架是个反复修改的东西,最终形成的东西. 如果你学了一点技术,觉得自己可以写出框架了,觉得自 ...
- Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)
jquery提供的serialize方法能够实现. $("#searchForm").serialize();但是,观察输出的信息,发现serialize()方法做的是将表单中的数 ...
- 剖析手写Vue,你也可以手写一个MVVM框架
剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...
- win10 uwp MVVM 轻量框架
如果在开发过程,遇到多个页面之间,需要传输信息,那么可能遇到设计的问题.如果因为一个页面内包含多个子页面和多个子页面之间的通信问题找不到一个好的解决方法,那么请看本文.如果因为ViewModel代码越 ...
- 前端框架MVVM是什么(整理)
前端框架MVVM是什么(整理) 一.总结 一句话总结:vm层(视图模型层)通过接口从后台m层(model层)请求数据,vm层继而和v(view层)实现数据的双向绑定. 1.我大前端应该不应该做复杂的数 ...
- MVVM、MVC框架的认识
推荐博客: https://blog.csdn.net/jia12216/article/details/55520426 https://www.cnblogs.com/sunny_z/p/7093 ...
随机推荐
- B20J_3231_[SDOI2014]旅行_树链剖分+线段树
B20J_3231_[SDOI2014]旅行_树链剖分+线段树 题意: S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,城市信仰不同的宗教,为了方便,我们用不同的正整数代表各种宗教. S国 ...
- Android缓存机制——LruCache
概述 LruCache的核心原理就是对LinkedHashMap的有效利用,它的内部存在一个LinkedHashMap成员变量,值得注意的4个方法:构造方法.get.put.trimToSize LR ...
- Python基础面试,看这篇文章画重点吧,Python面试题No1
为什么有这个系列的文章 一直想写一些更加基础的文章,但是总是想不到好的点子,最近到了就业季,一大堆学生面临就业了,正好,从Python的面试题出发,分析和解答一些常见的面试题,并且总结一些文字. 每一 ...
- Python爬虫入门教程 57-100 python爬虫高级技术之验证码篇3-滑动验证码识别技术
滑动验证码介绍 本篇博客涉及到的验证码为滑动验证码,不同于极验证,本验证码难度略低,需要的将滑块拖动到矩形区域右侧即可完成. 这类验证码不常见了,官方介绍地址为:https://promotion.a ...
- WebView,我已经长大了,知道自己区分是否安全了!
一.前言 如果你在用 Android 原生系统(Google Play 服务),在使用 WebView 加载某些网页时,一定遇到过以下的安全警告红屏. 这是 WebView 的安全浏览保护策略,在 A ...
- BeetleX快速构建多平台的TCP和SSL TCP应用
对于普通开发者而言编写TCP应用通讯是一件相对复杂的工作,毕竟需要一系列的bytes操作:如果再针对SSL的安全性处理相信会把很多普通开发者拒之门外.为了简化这一问题BeetleX引入了Stream操 ...
- C#机器学习之判断日报是否合格
简单来说机器学习的核心步骤在于“获取学习数据:选择机器算法:定型模型:评估模型,预测模型结果”,下面本人就以判断日报内容是否合格为例为大家简单的阐述一下C#的机器学习. 第一步:问题分析 根据需求可以 ...
- 第13章 Base64 URL编码 - IdentityModel 中文文档(v1.0.0)
JWT令牌使用Base64 URL编码进行序列化. IdentityModel包括Base64Url帮助编码/解码的类: var text = "hello"; var b64ur ...
- Python 中@property的用法
在绑定属性时,如果我们直接把属性赋值给对象,比如: p = Person() p.name= 'Mary' 我们先看个详细的例子(注意双下划线name和age定义为私有变量): class Perso ...
- SQL Server的Descending Indexes
SQL Server的Descending Indexes 测试环境:SQL Server 2012 表结构如下 USE [test] GO CREATE TABLE [dbo].[tt8]( ,) ...