完全使用一组 DSL 来操作 Grid 控件
最近尝试了一下将 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 控件的更多相关文章
- 扩展BindingList,防止增加、删除项时自动更新界面而不出现“跨线程操作界面控件 corss thread operation”异常
在做界面程序时,常常需要一些数据类,界面元素通过绑定等方式显示出数据,然而由于UI线程不是线程安全的,一般都需要通过Invoke等方式来调用界面控件.但对于数据绑定bindingList而言,没法响应 ...
- WPF平台Grid控件性能比较
WPF官方发布第一个版本至今已经有10年了, 我们几乎在同时也开始了XAML开发.即使经过多年打造,我们依旧尝试提高:我们真的成功打造了高效灵活的控件吗?我没有在其他地方找到任何关于优秀的WPF表格性 ...
- C#多线程操作界面控件的解决方案(转)
C#中利用委托实现多线程跨线程操作 - 张小鱼 2010-10-22 08:38 在使用VS2005的时候,如果你从非创建这个控件的线程中访问这个控件或者操作这个控件的话就会抛出这个异常.这是微软为了 ...
- Grid控件
Grid控件是WPF布局容器中功能最强大.最灵活的控件.Grid控件基本上能够完成其他WPF容器控件所能完成的功能,Microsoft建议大多数界面的布局都使用Grid控件来实现,因此默认情况下.vs ...
- [WinForm]WinForm跨线程UI操作常用控件类大全
前言 在C#开发的WinForm窗体程序开发的时候,经常会使用多线程处理一些比较耗时之类的操作.不过会有一个问题:就是涉及到跨线程操作UI元素. 相信才开始接触的人一定会遇上这个问题. 为了解决这个问 ...
- FineUI Grid控件高度自适应
引言 页面里使用f:Grid控件,添加分页功能,然后高度填充整个页面. 如何使用 使用FineUI 控件的每个页面都有一个f:PageManager控件,它包含属性:AutoSizePanelID,设 ...
- 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 ...
- FineUI Grid控件右键菜单的实现
FineUI官方Demo上一直没有Grid右键菜单的实现,其实从4.1.x的版本开始,允许添加自定义的事件监听(Listeners),所以要实现这个功能已经相当容易了. ExtJs右键菜单有很多种,对 ...
- 实现控件WPF(4)----Grid控件实现六方格
PS:今天上午,非常郁闷,有很多简单基础的问题搞得我有些迷茫,哎,代码几天不写就忘.目前又不当COO,还是得用心记代码哦! 利用Grid控件能很轻松帮助我们实现各种布局.上面就是一个通过Grid单元格 ...
随机推荐
- BZOJ 1305: [CQOI2009]dance跳舞 二分+最大流
1305: [CQOI2009]dance跳舞 Description 一次舞会有n个男孩和n个女孩.每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞.每个男孩都不会和同一个女孩跳两首(或更多)舞曲 ...
- html学习第二天—— 第九、十章——CSS的继承、层叠和特殊性+CSS格式化排版
继承CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代.比如下面代码:如某种颜色应用于p标签,这个颜色设置不仅应用p标签, ...
- 压力测试相关之ab命令
1. 短时压力测试工具 ab 命令(apache的工具) 关键指标: Requests per second: 98.52 [#/sec] (mean) ###平均每秒的请求数 Tim ...
- mac linux 删除一个文件下边所有文件和文件夹
sudo rm -r -f 目录
- 利用Simple-RTMP-Server(SRS)来进行直播
1.下载SRS 官方地址:http://www.ossrs.net/srs.release/releases/ 百度地址:http://pan.baidu.com/s/1kV8WQpx 2.编译安装S ...
- linux学习第一阶段
最近比较盲目的生活,翻来覆去,总归是为了自己,还是静下心来看看东西吧.好好学习.天天向上
- IDEA+Tomcat+JRebel热部署
在完成idea工程简单应用后,接下来实现热部署. 简单应用地址:http://wibiline.iteye.com/admin/blogs/2072454 一.安装JRebel插件 1. 在线安装 F ...
- Html5+Css3制作下拉菜单的三种方式
一.渐变式改变ol的高度 1.外部为ul标签,在每个li里嵌套一个ol列表2.设置外部li左浮动,内部ol标签绝对定位,外部li标签相对定位3.设置ol的高为0,溢出隐藏4.外部li标签:hover ...
- HTML5 使用application cache 接口实现离线数据缓存
1.配置缓存文件 cache manifest MIME TYPE:text/cache-manifest文件名称:name.appcache作用:用于配置需要缓存的文件 2.使用方法 在服务器上添加 ...
- Spring的通知(Advice)
Spring提供了5种Advice类型: Interception Around:JointPoint前后调用 Before:JointPoint前调用 After Returning:JointPo ...