最近尝试了一下将 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. 安装ganglia

    安装ganglia 1.默认已经配置好相关的主机名和Ip地址映射关系 2.默认已经安装好ssh密码登陆 3.默认已经配置好yum源和相关网络配置(如hosts 可在墙外) 4.服务器端安装(除了yum ...

  2. git rebase 和 merge的区别

  3. EditText获取和失去焦点,软键盘的关闭,和软键盘的显示和隐藏的监听

    软键盘显示和隐藏的监听: 注: mReplayRelativeLayout是EditText的父布局 //监听软键盘是否显示或隐藏 mReplayRelativeLayout.getViewTreeO ...

  4. 希尔伯特矩阵(Hilbert matrix)

    例: [ 1 1/2 1/3  1/2 1/3 1/4  1/3 1/4 1/5 ]   矩阵的一种,其元素A(i,j)=1/(i+j-1),i,j分别为其行标和列标. 即: [1,1/2,1/3,- ...

  5. Java环境变量的简记

    1,安装版的jre或jdk.安装后如果不做开发用,则只是做Java运行时环境,则不需要手动配置任何Java环境变量. 2,绿色解压版(拷贝版)jdk或jre.运行和开发都需要配置环境变量. 运行Jav ...

  6. [R语言]R语言计算unix timestamp的坑

    R+mongo的组合真是各种坑等着踩 由于mongo中的时间戳普遍使用的是unix timestamp的格式,因此需要对每天的数据进行计算的时候,很容易就想到对timestamp + gap对方式来实 ...

  7. Hadoop各商业发行版之比较

    Hadoop的发行版除了社区的Apache hadoop外,cloudera,hortonworks,mapR,EMC,IBM,INTEL,华为等等都提供了自己的商业版本.商业版主要是提供了专业的技术 ...

  8. Mac osx 安装PIL出现Some externally hosted files were ignored (use --allow-external PIL to allow).

    出现这个问题Some externally hosted files were ignored (use --allow-external PIL to allow)的主要原因是PIL的一些依赖库还没 ...

  9. 如何在Windows上从源码编译Chromium (CEF3) 加入mp3支持

    一.什么是CEF CEF即Chromium Embeded Framework,由谷歌的开源浏览器项目Chromium扩展而来,可方便地嵌入其它程序中以得到浏览器功能. CEF包括CEF1和CEF3两 ...

  10. 【JBOSS】User not found SA

    启动JBOSS 发现报User not found: SA 错误, 找了老半天才找到处理方法,异常日志如下: java.sql.SQLException: User not found: SA at ...