最近尝试了一下将 XtraGrid 的初始化工作封装成内部 DSL,例如一个普通的基础数据的增删改查操作的代码会像下面这样:

public partial class UserForm : XtraForm
{
private readonly UserRepository UserRepository;
private readonly UserService UserService;
private readonly GridManager<UserDto> _gridManager; public UserForm(UserRepository UserRepository,
UserService UserService)
{
InitializeComponent(); this.UserRepository = UserRepository;
this.UserService = UserService; _gridManager = new GridManager<UserDto>(userGrid);
_gridManager.CommonInlineEditable()
.Caption("系统用户") .Column("登录名", t => t.LoginName)
.Column("用户名", t => t.Text)
.Column("工号", t => t.Code)
.Column("是否停用", t => t.IsStop)
.Column("录入码1", t => t.InputCode1)
.Column("录入码2", t => t.InputCode2)
.Column("录入码3", t => t.InputCode3)
.Column("排序", t => t.OrderField) .Validate(
v => v.Varify(t => t.LoginName, spec => spec.NotNull()
.Unique(UserRepository.IsLoginNameUnique)
.Msg("登录名不能为空或重复。").End())
.Varify(t => t.Text, spec => spec.NotNull().Msg("用户名不能为空。").End())
.Varify(t => t.Code, spec => spec.NotNull()
.Unique(UserRepository.IsCodeUnique)
.Msg("工号不能为空或重复。").End())) .DataSource(() => UserRepository.GetAllOrderBy(t => t.Text).ToDtoList<User, UserDto>())
.ById(id => new UserDto().FromEntity(UserRepository.GetById(id)))
.SaveAction(dto =>
{
User newEntity = new User();
dto.AssignToEntity(newEntity);
UserService.Save(newEntity);
})
.UpdateAction(dto =>
{
User entity = UserRepository.GetFromCache(dto.Id);
dto.AssignToEntity(entity);
UserService.Update(entity);
})
.DeleteAction(id => UserService.Delete(id));
} private void SaveButton_Click(object sender, EventArgs e)
{
_gridManager.SaveAll();
} private void AddButton_Click(object sender, EventArgs e)
{
_gridManager.AddNew();
} private void DeleteButton_Click(object sender, EventArgs e)
{
_gridManager.Delete();
} private void RefreshButton_Click(object sender, EventArgs e)
{
_gridManager.ReLoadData();
} private void UserForm_Load(object sender, EventArgs e)
{
_gridManager.ReLoadData();
}
}

运行起来像这样:

DevExpress 这套控件功能非常强大,自带的可视化设计器也非常方便。如果控件非常多,或者希望进行复杂的交互,配合 DataLayoutControl 拖拖拽拽就可生成表单窗体。感觉照比宝蓝的 RAD 辉煌时代有过之而无不及。那么为什么还要放弃方便的可视化设计器,费力封装成代码呢?主要考虑到直接设置控件的属性和事件有如下几个缺点
  1. 不便于重构。每个数据列的 FeildName 属性都是将实体或DTO的属性名以字符串的方式赋值,将来如果想改名、移除或移动一个属性、移除/合并/拆分实体,无法直接用重构工具精确地找到所有引用到该属性/实体的控件,只能用字符串查找的方法,低效而且容易有遗漏,这也是项目后期进行实体级别的重构让人望而却步的原因之一。虽然使用DTO能缓解此问题,但终究让人不爽。 而使用类似 Column("登录名", t => t.LoginName) 这样的语句进行配置就不会有这种问题了。
  2. 导致大量的重复代码。像基础数据维护这种功能,大部分都是简单的增删改查,往往只使用了 Grid 控件功能的一个子集而且大部分都比较相像。例如上图所示,需要显示查找栏,不需要显示分组栏,要显示Grid的标题栏,自动列宽就可以等等。这些属性一个一个地进行设置的话,也挺费力气。当需要增加一个“部门管理”的时候,感觉跟“用户管理” 差不多,就会把“用户管理”这个 Grid 复制过来,甚至可能把 “用户管理”整个窗体的代码完全复制过来,再进行修改。这其实也是一种重复代码。对于把 DRY(don't repeat yourself) 作为一项基本原则的人来说这是无法忍受的。
  3. 代码意图不明显,可读性差。虽然只是简单的增删改查,但是麻雀虽小五脏俱全——需要进行前端验证提升用户体验,验证不通过时要以友好的方式显示错误信息,按ESC键能够撤销修改;修改过的数据行希望以粗体显示,按保存按钮保存之后要恢复成正常字体;保存时要判断是修改还是新增数据;对于引用了其它的实体的属性或者枚举类型的属性要做特殊处理。如果前端验证失败,而后用户又修改成可以通过验证的数据之后直接按了保存按钮,虽然能保存成功,但是如果不做特殊处理的话那个红色的叉叉并不会自动消失。。。要把这些功能和细节全处理好的话往往要在好几个事件里面写代码配合着一起完成。这样单独看某一块代码的话往往不知道它是做什么的。不但新人掌握起来比较费劲,过一段时间我们自己要想读懂都要费一番力气。我们希望能把相关的功能集中起来,并且隐藏实现细节。

所以这次设计这组操作 XtraGrid 的内部 DSL 的目标就是:
  1. 可读性第一。不追求读起来像自然语言,但希望相关的内容能在一处,读起来自然流畅,尽量隐藏实现细节。从实际效果来看,由于有 lambda 表达式和一些语法糖可用,虽然有一些杂音和少量实现细节,基本上还挺让人满意。
  2. 不使用字符串硬编码。不太地道地借助了 FluentNHibernate 的帮助类库轻松实现,以后再彻底把它的代码偷过来。
  3. DSL 实现代价最好小一点,作为一个薄的封装层,并且可以随需演进。出于希望DSL的实现越简单越好的想法,并没有使用大量自己的对象模型,而是使用DSL的API直接操作Grid控件的属性。这样一开始确实挺省事,但是往往出于实现上的困难而把实现代码越搞越乱,这个挺让人纠结。
  4. 减少冗余代码。冗余代码还是有一点,例如 t=>t.Code 在 Column 和 Validate 中就出现了 2 次,出于实现上的困难暂时只能这样了。

总之基本还算不错,由于实现代码还很不成熟就先不贴出来了。

完全使用一组 DSL 来操作 Grid 控件的更多相关文章

  1. 扩展BindingList,防止增加、删除项时自动更新界面而不出现“跨线程操作界面控件 corss thread operation”异常

    在做界面程序时,常常需要一些数据类,界面元素通过绑定等方式显示出数据,然而由于UI线程不是线程安全的,一般都需要通过Invoke等方式来调用界面控件.但对于数据绑定bindingList而言,没法响应 ...

  2. WPF平台Grid控件性能比较

    WPF官方发布第一个版本至今已经有10年了, 我们几乎在同时也开始了XAML开发.即使经过多年打造,我们依旧尝试提高:我们真的成功打造了高效灵活的控件吗?我没有在其他地方找到任何关于优秀的WPF表格性 ...

  3. C#多线程操作界面控件的解决方案(转)

    C#中利用委托实现多线程跨线程操作 - 张小鱼 2010-10-22 08:38 在使用VS2005的时候,如果你从非创建这个控件的线程中访问这个控件或者操作这个控件的话就会抛出这个异常.这是微软为了 ...

  4. Grid控件

    Grid控件是WPF布局容器中功能最强大.最灵活的控件.Grid控件基本上能够完成其他WPF容器控件所能完成的功能,Microsoft建议大多数界面的布局都使用Grid控件来实现,因此默认情况下.vs ...

  5. [WinForm]WinForm跨线程UI操作常用控件类大全

    前言 在C#开发的WinForm窗体程序开发的时候,经常会使用多线程处理一些比较耗时之类的操作.不过会有一个问题:就是涉及到跨线程操作UI元素. 相信才开始接触的人一定会遇上这个问题. 为了解决这个问 ...

  6. FineUI Grid控件高度自适应

    引言 页面里使用f:Grid控件,添加分页功能,然后高度填充整个页面. 如何使用 使用FineUI 控件的每个页面都有一个f:PageManager控件,它包含属性:AutoSizePanelID,设 ...

  7. Jquery 操作Html 控件 CheckBox、Radio、Select 控件 【转】http://www.cnblogs.com/lxblog/archive/2013/01/09/2853056.html

    Jquery 操作Html 控件 CheckBox.Radio.Select 控件   在使用 Javascript 编写前台脚本的时候,经常会操作 Html 控件,比如 checkbox.radio ...

  8. FineUI Grid控件右键菜单的实现

    FineUI官方Demo上一直没有Grid右键菜单的实现,其实从4.1.x的版本开始,允许添加自定义的事件监听(Listeners),所以要实现这个功能已经相当容易了. ExtJs右键菜单有很多种,对 ...

  9. 实现控件WPF(4)----Grid控件实现六方格

    PS:今天上午,非常郁闷,有很多简单基础的问题搞得我有些迷茫,哎,代码几天不写就忘.目前又不当COO,还是得用心记代码哦! 利用Grid控件能很轻松帮助我们实现各种布局.上面就是一个通过Grid单元格 ...

随机推荐

  1. 学习微信小程序之css8

    实战画一个圣诞树 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  2. 连接Mysql提示Can’t connect to local MySQL server through socket的解决方法

    mysql,mysqldump,Mysqladmin,php连接mysql服务常会提示下面错误: ERROR 2002 (HY000): Can't connect to local MySQL se ...

  3. [原创]Centos7 从零编译配置Memcached

    序言 Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度. Memca ...

  4. tecplot 脚本文件

    一个简单的C++代码,可以生成tecplot的脚本文件,打开文件就能自动绘图.绘图过程是先把文件导入,然后镜像,最后生成一个动画,但是导入的文件名称要求是有规律的. /* ** This progra ...

  5. JS简单解决并发量

    经常在写代码的时候碰到这样的场景:页面初始化时显示loading页,同时启动多个ajax并发请求获取数据,当每个ajax请求返回时结束loading. 举个例子,一个下订单的页面,要查询常用地址信息. ...

  6. delphi 步长不等于 1 的方法

    i:=; do begin ... INC(i,); end; 不能用for循环,只能用像上面的方法折衷一下

  7. c#教程之通过数据绑定修改数据

    通过数据绑定修改数据 "实体框架"提供了与数据库的双向通信通道.前面已经讲述了如何使用"实体框架"获 取数据,现在来看看如何修改获取的信息,并将改动发送回数据库 ...

  8. MVC5项目中添加Wep API

    一.查看MVC版本,决定你有没有必要看这篇文章 打开web.config,看到以下内容 <dependentAssembly> <assemblyIdentity name=&quo ...

  9. HDU4787 GRE Words Revenge(AC自动机 分块 合并)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4787 Description Now Coach Pang is preparing for ...

  10. 学习android 官方文档

    9.29 1. 今天,FQ,看到android studio中文网上有一个FQ工具openVPN,我就使用了. 之前用过一个FQ工具开眼,但由于网速慢,我就弃用了. 2. 现在,我就可以FQ去andr ...