之前写过一篇文章,地址  http://www.cnblogs.com/Bond/p/3469798.html   大概说了下怎么通过反射来自动生成对应EasyUi datagrid的模板,然后贴了很多代码,看起来很乱,当时没用过easyui,没啥经验。 这次经过了项目的实际考验,我把它做了一些改动,在此分享下,并且附上源码,源码需要用vs2012打开,打开即可运行不要做任何设置。源码地址在 https://github.com/LittleBearBond/GenerateEasyUiDataGridTemplate 。都说现在程序员要玩GitHub,我一直都想用用,不过没时间,经过N久的加班,最近终于有时间学习了下,把源码也放到上面。

我为什么要去折腾写这个东西,之前博客也大概提了下,现在请看下图,如果我们用easyui的datagrid来展示这样的数据

页面必然对应这样一个datagrid模板

<table id="dt" class="easyui-datagrid" data-options="title:'商品列表'">   <thead>     <tr><th data-options="field:'Name',align:'center',formatter:format.formatVal"> 名称</th>      <th data-options="field:'Category',align:'center',formatter:format.formatVal"> 类别</th>      <th data-options="field:'Price',align:'center',sortable:true,formatter:format.formatVal"> 价格</th>      <th data-options="field:'CreateTime',align:'center',formatter:format.formatTime" sortable="true"> 创建时间</th></tr>  </thead></table>

其实这个模板他要展示的数据对象必然对应一个后台的类,比如这个模板对应的后台类是Product

public class Product{/// <summary>/// /// </summary>public int Id { get; set; }/// <summary>/// 名称/// </summary>public string Name { get; set; }/// <summary>/// /// </summary>public string Category { get; set; }/// <summary>/// 价格/// </summary>public decimal Price { get; set; }/// <summary>/// 创建时间/// </summary>public DateTime CreateTime { get; set; }  }

而从后台返回的数据是这样的

通过以上观察我们会发现一些问题,datagrid要展示的数据后台必然是返回一个json对象,而单个对象里面的 Id 、CreateTime 、Name、 Price 其实是对应着Product实体对象的字段名,而datagrid模板里面要设置哪行显示哪个字段,比如Name 在datagrid模板里面对应着一行th,并设置Name这行的相关属性。

datagrid模板里面每个字段都是和后台的Product实体对象的字段名称一一对应的,而我们每次在设置模板的时候都要去拷贝字段名称,如果不小心弄错了数据就不会显示出来。如果我们能通过后台的实体对象Product来直接生成这个模板那就不会出错了。这个功能我已经做好了,用到的知识点很少,只是自定义属性加反射再加点字符串拼接就搞定啦。不过生成模板也是有很多问题需要考虑,大致有以下一些问题需要解决。

1:Product对象里面不是每个字段都要显示出来,比如Id不需要显示,我们希望能够动态设置需要显示的字段。

2:在设置单行th的时候fileid是Name,但是在thead表头上需要显示 “名称” 两个汉字而不是英文的Name

3:我们可以动态设置这个字段的data-options属性  以及这行的其他属性 如style  class之类的

4:显示有先后顺序,Name显示在前面还是Price显示在前面我们可以做动态设置

5:可以额外添加和Product无关的字段,然后显示到页面上

上面说在很抽象来看具体事例:

现在我的类是这样的,标记了display属性,和DataOptions自定属性

 [DataOptions(Options = "title:'商品列表'")]public class Product{/// <summary>/// /// </summary>public int Id { get; set; }/// <summary>/// 名称/// </summary>[Display(Name = "名称")][DataOptions(Order = 1)]public string Name { get; set; }/// <summary>/// /// </summary>[Display(Name = "类别")][DataOptions(Order = 2)]public string Category { get; set; }/// <summary>/// 价格/// </summary>[Display(Name = "价格")][DataOptions(Order = 3, Options = "sortable:true")]public decimal Price { get; set; }/// <summary>/// 创建时间/// </summary>[Display(Name = "创建时间")][DataOptions(Order = 4, Property = "sortable=true")]public DateTime CreateTime { get; set; }

现在我的cshtml页面代码是这样的

@using GenerateDataGridDemo.EasyUi
@using GenerateDataGridDemo.Extends
@using GenerateDataGridDemo.Models
@{ViewBag.Title = "Test1";Layout = "~/Views/Shared/_EasyUiLayout.cshtml";var loadUrl = Url.Action("LoadTest1", "Home");
}
@section searchs
{<div>@EasyUiPageControls.SearchTimeInput()@EasyUiPageControls.SearchKeyWordInput("产品名称:", "KeyWord", "init=\"请输入产品名称\"")@EasyUiPageControls.SearchButton()</div>
}
@Html.CreateDataGridTemplate(typeof(Product))
@section scripts{<script type="text/javascript">$(function () {easyui.dg.LoadData('@loadUrl');WebJs.SearchCreateTime();$('#searchLoadList').click(function () {easyui.dg.Search('@loadUrl');});});</script>
}

在页面上得到的效果就是以下这样的,经过简单封装cshtml的页面代码基本上就只有几行,就搞定了数据显示、加载、搜索。

有时我们页面不会这么简单,我们的页面可能是这样的,前面有多复选框和后面的操作列。

此时我还是用的以前的Product,只是页面代拿略有改变,如下

@using GenerateDataGridDemo.EasyUi
@using GenerateDataGridDemo.Extends
@using GenerateDataGridDemo.Models
@{ViewBag.Title = "Test2";Layout = "~/Views/Shared/_EasyUiLayout.cshtml";var loadUrl = Url.Action("LoadTest1", "Home");
}
@section searchs
{<div>@EasyUiPageControls.SearchTimeInput()@EasyUiPageControls.SearchKeyWordInput("产品名称:", "KeyWord", "init=\"请输入产品名称\"")@EasyUiPageControls.SearchButton()<a class="easyui-linkbutton" id="GetAll">获取选择的ID</a></div>
}
@{var end = EasyUiPageHtml.FormateOperate();var start = EasyUiPageHtml.FirstCheckBox();
}
@Html.CreateDataGridTemplate(typeof(Product), end, start)
@section scripts{<script type="text/javascript">function formatOperate(val, row) {return '<a href="###" οnclick="Modify(' + row.Id + ',\'' + row.Name + '\')">编辑</a>  ';}function Modify(id, name) {WebJs.Dialog.Alert(utils.str.formatString('Id:{0},Name{1}', id, name));}$(function () {easyui.dg.LoadData('@loadUrl');WebJs.SearchCreateTime();$('#searchLoadList').click(function () {easyui.dg.Search('@loadUrl');});$('#GetAll').on('click', function () {var ids = easyui.dg.GetSelectionIds();if (utils.str.isNullOrWhiteSpace(ids)) {WebJs.Dialog.Tip('请先选择!');return;}WebJs.Dialog.Content(ids);});});</script>
}

大家会发现其实就多了end和start,其他其实是没变动的,只是曾加了一个GetALL按钮。

真正用于生成面datagrid模板的代码只有@Html.CreateDataGridTemplate(typeof(Product))这一行,然后加上Product上面标记的一些属性,就完成了datagrid模板的自动生成,具体实现上篇博客有说明,这次把代码抽取出来然后奉献给大家做个参考。

demo代码是这样的,一切从简,只有一个程序集,页面只有两个页面,但是功能还是比较齐全的。

点击左边导航,就会添加一个页面,如果已经存在就刷新存在的页面,页面有右键菜单。

扩展datagrid的view 在没有数据的时候显示提示信息

对Jq这个日期控件做了点改动,起始日期级联验证

页面搜索和加载数据做了相应封装,调用的时候只需一句话,在项目中做了很多公共方法的提取和封装,这里提出来的是部分,多了影响大家看这个代码

支持排序,只有价格和创建时间支持排序,支持单个字段排序,多个字段也是可以的,后台ExtendClass.cs有相关代码只是我没具体做这个功能。

最后贴一下整个核心的代码,代码就一百多行,在项目源码中可以去看看,然后根据自己的需求去扩展和改进。

public class GenerateDataGrid{
        public static IList<PropertyInfo> GetAllPropertyInfoList(Type entity){return entity.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);}public static string GetDataGridTemplate(Type entity, string appendEnd, string appendStart){var sb = new StringBuilder();//先获取类的Attributevar entityCustomAttr = entity.GetCustomAttributes(typeof(DataOptionsAttribute), false).FirstOrDefault() as DataOptionsAttribute;#region 对实体的Attibute属性进行处理//是否显示没有 标记dataoptions的字段var isShowNotAttr = false;//默认不显示var options = string.Empty;var tableId = string.Empty;var tableProperty = string.Empty;//并没有处理可能发生的异常情况, 比如在Property 指定了id="xxx"  而又指定了id的值if (entityCustomAttr != null){isShowNotAttr = entityCustomAttr.IsShowNotAttr;options = string.IsNullOrWhiteSpace(entityCustomAttr.Options) ? string.Empty : entityCustomAttr.Options;//默认ID为dt ,  假设在Property 中没有设置了Id,如果设置了这里没做处理tableId = string.IsNullOrWhiteSpace(entityCustomAttr.Id) ? "dt" : entityCustomAttr.Id;tableProperty = string.IsNullOrWhiteSpace(entityCustomAttr.Property) ? string.Empty : entityCustomAttr.Property;}#endregion//获取所有的Propertyvar properties = GetAllPropertyInfoList(entity);//如果设置有不显示没有dataoptions标记的,值取出标记有dataoptions的字段 if (!isShowNotAttr){properties = properties.Where(n => n.CustomAttributes.Any(a => a.AttributeType == typeof(DataOptionsAttribute))).ToList();}//没有打标记的也要取出来, 这里得到以字段name为key List<Attribute>为值的集合对象Dictionary<string, List<Attribute>> colDicOpts = properties.ToDictionary(property => property.Name,property =>{var list = new List<Attribute>{property.GetCustomAttributes(typeof (DataOptionsAttribute), false).FirstOrDefault() as DataOptionsAttribute,property.GetCustomAttributes(typeof (DisplayAttribute), false).FirstOrDefault() as DisplayAttribute};return list;});//在table上拼接 id    data-options  和 Propertysb.AppendLine(string.Format("<table id=\"{0}\" class=\"easyui-datagrid\"  data-options=\"{1}\"  {2} > <thead> <tr>", tableId, options, tableProperty));//没有直接遍历加入数据  这里先取得所有数据,然后进行排序,得到th 列表var listThs = (from pro in propertieslet custAttrs = colDicOpts.SingleOrDefault(n => n.Key == pro.Name)select AppenedTemplate(Template.DataGridTh, custAttrs, pro)).ToList();//1、添加到开始部分的 add startif (!string.IsNullOrWhiteSpace(appendStart)) { sb.AppendLine(appendStart); }//2、添加中间部分,先排序,得到显示顺序 add centerlistThs = listThs.OrderBy(n => n.Key).Select(n => n.Value).ToList();sb.AppendLine(string.Join("", listThs));//3、追加后面的字符串 add endif (!string.IsNullOrWhiteSpace(appendEnd)) { sb.AppendLine(appendEnd); }sb.AppendLine(@"</tr></thead></table>");return sb.ToString();}//dynamic 可用 KeyValuePairprivate static dynamic AppenedTemplate(string template, KeyValuePair<string, List<Attribute>> attributes, PropertyInfo proinfo = null){var displayName = attributes.Value.SingleOrDefault(n => n is DisplayAttribute) as DisplayAttribute;//设置字段显示的名称,直接设置 DisplayAttribute,这个大家肯定很熟悉的属性var str = Template.RegV.Replace(template, displayName != null ? displayName.Name : attributes.Key);//设置显示的字段field ,即是当前th显示哪个字段,例如field:'Id'str = Template.RegF.Replace(str, attributes.Key);//从该字段的CustomAttributes中取得DataOptionsAttributevar dataOptions = attributes.Value.SingleOrDefault(n => n is DataOptionsAttribute) as DataOptionsAttribute;//设置Property, 如果property和data-options有设置相同的对象 这里没做异常处理str = Template.RegP.Replace(str, dataOptions == null ? string.Empty : dataOptions.Property ?? "");//没有设置排序的这里默认设置一个值var order = dataOptions == null ? 100 : dataOptions.Order;//由于我自己的需要,我要对DateTime类型进行特殊处理if (proinfo != null && proinfo.PropertyType == typeof(DateTime)){//没有自定义属性的值if (dataOptions == null){//WebJs.Format.formatTime 自己的js时间格式化函数 这个一定程度上导致前后台耦合了str = Template.RegD.Replace(str, "formatter:format.formatTime");//默认时间格式
                }else{str = dataOptions.Options != null && dataOptions.Options.IndexOf("formatter", StringComparison.CurrentCultureIgnoreCase) >= 0 ?//已经设置formatter
                        Template.RegD.Replace(str, dataOptions.Options) ://默认设置formatterTemplate.RegD.Replace(str, ((dataOptions.Options ?? "").TrimEnd(',') + ",formatter:format.formatTime").TrimStart(','));}}else{//替换data-option 的值, 如果为空就直接替换为空 if (dataOptions == null){str = Template.RegDi.Replace(str, string.Empty);}else{var opt = (dataOptions.Options ?? "");//默认设置起格式化var replaceStr = opt.IndexOf("formatter", StringComparison.CurrentCultureIgnoreCase) >= 0 ? opt : opt.TrimEnd(',') + ",formatter:format.formatVal";str = Template.RegD.Replace(str, replaceStr.TrimStart(','));}}return new { Value = str, Key = order };}}

PS:为了追逐爱情,准备离开成都,辞职北漂,忘大神收留。

转载于:https://www.cnblogs.com/Bond/p/3583526.html

反射实体自动生成EasyUi DataGrid模板 第二版--附项目源码相关推荐

  1. 有道云笔记 - Markdown模板(文首附markdown源码,即.md文件)

    有道云笔记 - Markdown模板 附 本文的Markdown源码镜像: https://github.com/yanglr/AlgoSolutions/blob/master/Youdao_Not ...

  2. 基于qt+halcon实现视觉定位模板匹配【附部分源码】

    文章目录 前言 演示视频 一.项目文件目录讲解 二.Qt Designer设置ui界面 1.CtuImageMatching.ui界面详解 2.CameraSetting.ui的设置 3.Calibr ...

  3. 李沐d2l《动手学深度学习》第二版——风格迁移源码详解

    本文是对李沐Dive to DL<动手学深度学习>第二版13.12节风格迁移的源码详解,整体由Jupyter+VSCode完成,几乎所有重要代码均给出了注释,一看就懂.需要的同学可以在文末 ...

  4. 忧心文案小程序第二版前端后端源码

    简介: 大家好我是刚哥 前几天扒的两套小程序源码然后,然后整合了一下,修改了下,现在反正是能正常使用,后端是青木的后端, 后端安装: 上传后端解压修改config.php的数据库信息 上传数据库 前端 ...

  5. IDEA中修改自动生成的Servlet模板,提高编码效率

    IDEA中修改自动生成的Servlet模板,提高编码效率 一.修改idea中生成的servlet模板原因 自动生成的servlet模块代码,不够智能,还需要手动进行修改 二.修改Servlet模板 三 ...

  6. chatgpt如何自动生成角色prompt模板

    chatgpt如何自动生成角色prompt模板 作者:虚坏叔叔 博客:https://xuhss.com 早餐店不会开到晚上,想吃的人早就来了!

  7. 自动生成代码工具 模板工具类

    自动生成代码工具 模板工具类 import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.Output ...

  8. 【C#】最全单据打印(打印模板、条形码二维码、字体样式、项目源码)

    系列文章 [C#]编号生成器(定义单号规则.固定字符.流水号.业务单号) 本文链接:https://blog.csdn.net/youcheng_ge/article/details/12912978 ...

  9. 最新云核泛目录自带MIP模板开源站群系统源码

    最新云核泛目录自带MIP模板开源站群系统源码 最新云核泛目录自带MIP模板开源站群系统源码,注意:程序会对网站权重有一定影响,如果您的网站已经比较稳定,内容质量不错建议不要放在你网站上,老实的做好网站 ...

最新文章

  1. 技术图文:02 创建型设计模式(上)
  2. Python集成网络诊断小工具(含有ping,tracert,tcping等小工具)
  3. python是什么编程教程-编程零基础应当如何开始学习 Python?
  4. verilog case语句_浅谈Design Compiler -- Verilog语言结构到门级的映射
  5. [云炬创业基础笔记]第二章创业者测试8
  6. 细胞培养中出现黑胶虫污染处理方法
  7. 石子合并(GarsiaWachs算法)
  8. 技术回顾系列:最新最热门的技术大事-第一周
  9. Tp框架中模板中if条件如何使用?
  10. 软件安全测试的几个原则
  11. python画猴子_Python学习笔记(1)
  12. form表单中的enctype=multipart/form-data什么意思?
  13. qt 对话框位置如何确定_便利店如何确定收银台位置?
  14. MyBatis_Study_003(字段名与属性名称不一致,resultMap)
  15. 徐思 201771010132
  16. 仿“当当网”首页-Flash图片轮换
  17. 项目实训--Unity多人游戏开发(八、3D音效融合AudioMixer、统一的音频播放系统)
  18. Graph Neural Network-Based Anomaly Detection in Multivariate Time Series 代码配置及解析
  19. Lyx使用对中文进行编译
  20. C# 提取Word中插入的多媒体文件(视频、音频)

热门文章

  1. Flutter 项目开发指导 从基础入门到精通使用目录
  2. Flutter轮播图
  3. 03 ansible核心模块 之 文件类型模块
  4. 谷歌身份认证 Python实现
  5. 神奇的applycall
  6. web.xml中的contextConfigLocation在spring中的作用
  7. apache域名本地映射
  8. 自己常用的wireshark过滤条件
  9. Oracle 获取每月最后一天的函数
  10. 微软解释:关于Outlook 2007的争议