
非正常前端|.net|java开发人员,最近再学产品
一,问题 资产主表字段不固定,有些客户字段多,有的字段少;或者一个字段叫法不一样,但是作用一样;不同资产分类有不同字段;用户和实施人员希望资产字段可以自定义,搜索条件可以动态配置; 所以要满足功能资产字段可配可扩展,满足前台资产【增】【删】【改】【查】,Excel导入,第三方导入(webservice|webapi接口) 二,解决思路 其实如果使用CMDB系统就可以解决,这里只考虑一些传统小系统的设计思路。所以原标题叫小型CMDB数据机构及原型。以下想法没有落地,只作为个人的记录和分享,如果有毛病,请各位大佬在评论区提出,我会及时删除评论。首先资产表中,不是所有字段都可以自定义,增删改, 如果参与系统业务逻辑的字段被用户删了那系统就废了;要考虑资产分类所带来的增改查影响,所以字段应该满足以下原则: 1.参与系统业务逻辑字段,只能修改显示名称,不能删除,不能新增,简称固定字段;2.其他资产扩展字段,只显示,不参与系统业务逻辑,可以新增、修改、删除;3.所有资产,无论属于哪中资产分类,都要有通用字段,在资产分类未知时可以作为查询条件;4.通用字段被所有子分类继承,子分类可以修改显示名称,是否显示,不能删除; 三,功能细化 1.编辑(改)资产的通用字段,资产通用字段为资产主表中字段,只能修改字段显示名称 1.1编辑项: 1.1.1字段显示中文名1.1.2字段显示英文名1.1.3列表中字段显示顺序 2.按资产分类编辑(增删改)自定义字段 2.1编辑项: 2.1.1字段显示中文名称,不同分类显示名称不一样【?】2.1.2字段显示英文名称2.1.3列表中字段显示顺序2.1.4字段类型: 文本多行文本数字日期关联表【?】枚举固定值隐藏控件 2.1.5表单中字段分组名称2.1.6表单中字段显示顺序2.1.7表单中是否必填2.1.8一对多扩展字段【?】 3.资产导入Excel时自动导入对应的自定义字段数据 3.1导入模板,【某些扩展字段,与分类不匹配,数据将丢失】 4.可以通过通用字段和自定义字段查询资产 4.1显示的查询字段名称需要读取配置项 5.列表页面表格的通用字段和自定义字段混合显示 5.1可以设置显示哪些列5.2列名通过配置项动态读取对应字段的对应名称 6.编辑页面表单通用字段和自定义字段混合显示 6.1字段名通过配置项动态读取对应字段的对应名称6.2字段分组读取配置的分组 四,界面原型 配置页面 预览 资产卡片 高级查询 五,部分数据结构 1.属性配置主表 AutoID 分类ID 字段名称 中文名称 英文名称 字段说明 列表是否显示 列表显示顺序号 表单是否显示 表单分组AutoID 表单显示顺序 数据类型 表单是否必填 数据(默认)值 关联表名 关联字段 是否为系统字段 唯一 资产分类 分类为-1时为通用字段(维度二) 数据库中字段名称 唯一 唯一 唯一 唯一 唯一 系统字段不能删除(维度一) 表单分组表 AutoID 表单分组名称 唯一 唯一 角色-字段关系表 2.数据值字段关系表 AutoID 资产ID 属性配置主表AutoID 值 【?JSON方式存储】 资产主表加入一个字段列ExField,存JSON对象 优点:不用关联表,直接取值前台处理,新增修改方便 缺点:查询性能可能受印象; 3.Excel导入模板 Excel导入模板主表 AutoID 模板名称 Excel模板字段对照配置 AutoID Excel导入模板主表AutoID Excel表头名称 属性配置主表AutoID 4.高级查询模板 AutoID 模板名称 AutoID Excel导入模板主表AutoID 属性配置主表AutoID 运算符 连接符 值 六,核心数据关系 云析的博客编辑真的很恶心
因为实在不知道写啥,所以迟迟没有相关的介绍。但是必须要积累过程资产,所以还是介绍一下,不定哪天就有人用了。 首先还是介绍遇到的问题,我是做传统后台管理系统的,公司赶时髦,要用bootstrap。公司其他团队已经用bootstrap作为前端做出一套系统了,算是当前系统的升级,我没参与,进入测试阶段后,我偷偷看了一眼——嗯,可能是我眼光高,可能是测试版,我可以忍住不吐槽,我又不上项目。抱着这样的心理,我被通知开发其中的一个模块,这时我又看到了jsp代码——嗯,可能是我开发经验少,可能是……。反正要让我开发我忍不了,一个表单,输入框能不一样长,一个保存按钮就挨在输入框旁边,换个行也行啊,这是我认识的bootstrap么,表格头和分页条挨着是什么鬼,没数据?有了数据要滚动整个屏幕是哪样,我在滚动到顶上看表头么;HTML你倒是缩进啊,倒是写注释啊,还有内部样式啊,以后不改了么,我新建个页面,要把写好的整个页面都粘贴过去,然后在一层一层的找就为修改个标题,然后拉到最下面修改数据源,还得靠搜索找到事件到底在哪。我百度了一下,发现bootstrap没有面向后台开发人员的框架,最多是组件控件,html和css还是要写一堆。 1.由于后台管理程序界面大同小异,页面的大多数功能就是对数据的增删改查,复杂的逻辑由后台程序执行;所以当使用Bootstrap时有很多HTML是需要反复重复的粘贴复制,使用模板页又不能保证灵活性如果做了很多页面后需要统一修改,就会相当麻烦,对于专注后台代码人员是一个很大的工作量; 2.后台代码人员对前端不是很熟悉,而且不会花大量时间优化前端代码,导致不同程序员开发的页面样式不统一,整个软件看着就不是一个风格; 3.原生Bootstrap部分控件并不能满足客户需求,需要引用其他第三方插件,当多个第三方插件组合在一起的时候程序员的噩梦又开始了; 所以如果公司没有专门的前端人员,也没有成熟的Bootstrap模板框架,将会遇到三个问题:重复的工作量,影响开发周期;页面风格不统一;前台代码逻辑复杂很难维护; 当我发现这三个问题后着手用jquery封装原生Bootstrap和第三方插件,使页面引用统一的js和css,在尽量保证开发灵活的同时统一页面布局和样式。 再来分享下具体的实现思路, 1.首先作为控件要有一个统一的类,我命名为bsEx,随便起的,类包括 控件,基础属性和方法,事件也要走统一接口,要支持多语言,如果是第三方组件,要支持其所有属性和方法,这个类包含在一个js包里,组件要尽量减少前台html,保证一个控件一行或直接没有; 2.要有一个css文件,调整组件样式;要为每个组件写出示例及API方便学习参考; 3.要开源,以为只有我用到项目中了(jq22那上也有人用了但是没有跟踪不知道使用情况),源码要注释的明确方便修改; 4.要有更新日志; 实现的话就很简单了,只不过把属性提出来,输出html罢了。 这是我的第一个开源项目,大家多多支持,有不好的地方一定要通知我,我会不要脸的接受的。 项目源码 https://gitee.com/shixixiyue/MyBootstrapEx 可以通过右上角 [查看本页面源码] 进行在线查看,方便学习 v2.0版本 https://shixixiyue.gitee.io/mybootstrapex/
第一篇博客只是粗略说明了一下,其实这个工具真正用话可能大家还要细看下,我今天(连夜)写个例子,截几个图,做一下自定义模板的实例教程,因为代码生成本身是个工具,动画效果都是次要的,主要是工具本身,其中自带的模板并不适合所有开发环境,所以还是侧重模板编写和接口。 前面说到我要写个MVC前台的模板按表生成 Index.cshtml 和 Controller.cs,所以就那这两个做个例子。说到模板就是一个制式的文件,我要先做错一个标准文件作为模板,然后根据不同表生成自己的文件, 其实大家都是程序员说这都是废话,博客比较长,说明的比较细,截图多,最下面有源码 环境准备,我先搭了一个FineUI的空项目 CodeFDemo 用生成工具生成了 asset_a2 表的后台三层 添加到项目,生成,该表是一个资产分类表 第一步 做一个模板的真实页面 准备工作做完,再做一个前台展示页面,以此为标准制作模板文件 Index.cshtml @{ ViewBag.Title = "Index"; var F = Html.F(); } @section head { } @section body { @( F.Panel() .IsViewPort(true) .ShowBorder(false) .ShowHeader(false) .Layout(LayoutType.Region) .Items( //中间Panel 查询和列表 F.Panel() //.IsViewPort(true) .BoxFlex(4) .Layout(LayoutType.VBox) .BodyPadding(0)//5 .BoxConfigChildMargin("0 0 0 0")//0 0 5 0 .ShowBorder(false) .ShowHeader(false) .Items( //查询表单 F.Form() .ID("searchForm").Title("查询条件") .BodyPadding(5).BoxConfigChildMargin("0 5 0 5").BoxFlex(1) .Layout(LayoutType.VBox) .BoxConfigAlign(BoxLayoutAlign.Stretch).BoxConfigPosition(BoxLayoutPosition.Center) .EnableCollapse(true) .LabelWidth(100) .RowsEx(4, F.TextBox() .ID("txtstr20") .Attribute("data", "SYS_ASSET_A1_160") .Label("分类名称") .EmptyText("分类名称"), F.TextBox() .ID("txtstr20") .Attribute("data", "SYS_ASSET_A1_160") .Label("分类编号") .EmptyText("分类编号") ) , //列表 F.Grid() .Title("列表") .ID("Grid1") .BoxFlex(6) .EnableCheckBoxSelect(true) .DataIDField("ASSET_A2_AUTOID") .AllowPaging(true) .EnableHeaderMenu(false) .PageSize(15) .Toolbars( F.Toolbar().Items( F.DefaultGridBtn("Grid1") ) ) .Columns( F.RowNumberField(), F.RenderField() .HeaderText("ID") .Hidden(true) .DataField("ASSET_A2_AUTOID"), F.RenderField() .HeaderText("分类名称") .ExpandUnusedSpace(true) .DataField("ASSET_A2_10"), F.RenderField() .HeaderText("分类编号") .ExpandUnusedSpace(true) .DataField("ASSET_A2_20"), F.RenderField() .HeaderText("新增时间") .ExpandUnusedSpace(true) .DataField("ASSET_A2_MAKETIME") ) .DataSource(ViewBag.griddata) //.Listener("rowclick", "OnGrid1RowClick") ) ) ) } @section script { <script> F.ready(function () { }) //列表行点击事件 function OnGrid1RowClick(e, rowid) { F.doPostBack('@Url.Action("Grid1_RowClick")', { rowid: rowid, fields: F.ui.Grid1.fields }); } //列表新增 function Grid1new_Click() { F.ui.Grid1.showEdit();//新增 } //列表修改 得到列表选中项 getSelectedRows 注意DataIDField属性 function Grid1edit_Click(g,rowid) { F.ui.Grid1.showEdit(true);//修改 } //删除 删除此方法自动回发Grid1Del_Click 方法 参数为 id fields function Grid1delete_Click(g,ids) { } //刷新列表 function lodeGrid() { F.doPostBack('@Url.Action("lodeGrid")', { fields: F.ui.Grid1.fields }); } </script> } asset_a2Controller.cs using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using CodeFDemo.BP; namespace CodeFDemo.Controllers.sysApp { public class asset_a2Controller : Controller { // GET: sysA1 public ActionResult Index() { BLLasset_a2 bll = new BLLasset_a2(); ViewBag.griddata = bll.Getasset_a2MsByQuery(new Message.MsQuery()).ToArray(); return View(); } } } OK,浏览下(本实例只做列表展示) 第二步 将标准文件改写为模板文件 再看下代码,其中Grid的Columns是列的循环,查询条件也是列的循环,数据库表格的列一循环就可以了 所以就有了两个模板 @{ ViewBag.Title = "Index"; var F = Html.F(); } @section head { } @section body { @( F.Panel() .IsViewPort(true) .ShowBorder(false) .ShowHeader(false) .Layout(LayoutType.Region) .Items( //中间Panel 查询和列表 F.Panel() //.IsViewPort(true) .BoxFlex(4) .Layout(LayoutType.VBox) .BodyPadding(0)//5 .BoxConfigChildMargin("0 0 0 0")//0 0 5 0 .ShowBorder(false) .ShowHeader(false) .Items( //查询表单 F.Form() .ID("searchForm").Title("查询条件") .BodyPadding(5).BoxConfigChildMargin("0 5 0 5").BoxFlex(1) .Layout(LayoutType.VBox) .BoxConfigAlign(BoxLayoutAlign.Stretch).BoxConfigPosition(BoxLayoutPosition.Center) .EnableCollapse(true) .LabelWidth(100) .RowsEx(4 $$TextBoxForSearch$$ ) , //列表 F.Grid() .Title("列表") .ID("Grid1") .BoxFlex(6) .EnableCheckBoxSelect(true) .DataIDField("$$KEY$$") .AllowPaging(true) .EnableHeaderMenu(false) .PageSize(15) .Toolbars( F.Toolbar().Items( F.DefaultGridBtn("Grid1") ) ) .Columns( F.RowNumberField(), F.RenderField() .HeaderText("ID") .Hidden(true) .DataField("$$KEY$$") $$GridColumns$$ ) .DataSource(ViewBag.griddata) //.Listener("rowclick", "OnGrid1RowClick") ) ) ) } @section script { <script> F.ready(function () { }) //列表行点击事件 function OnGrid1RowClick(e, rowid) { F.doPostBack('@Url.Action("Grid1_RowClick")', { rowid: rowid, fields: F.ui.Grid1.fields }); } //列表新增 function Grid1new_Click() { F.ui.Grid1.showEdit();//新增 } //列表修改 得到列表选中项 getSelectedRows 注意DataIDField属性 function Grid1edit_Click(g,rowid) { F.ui.Grid1.showEdit(true);//修改 } //删除 删除此方法自动回发Grid1Del_Click 方法 参数为 id fields function Grid1delete_Click(g,ids) { } //刷新列表 function lodeGrid() { F.doPostBack('@Url.Action("lodeGrid")', { fields: F.ui.Grid1.fields }); } </script> } ,F.TextBox() .ID("txt$$ColunName$$") .Attribute("data", "$$ColunName$$") .Label("$$ColunNotes$$") .EmptyText("$$ColunNotes$$") using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using $$namespace$$.BP; namespace $$namespace$$.Controllers.sysApp { public class $$tablename$$Controller : Controller { // GET: sysA1 public ActionResult Index() { BLL$$tablename$$ bll = new BLL$$tablename$$(); ViewBag.griddata = bll.Get$$tablename$$MsByQuery(new Message.MsQuery()).ToArray(); return View(); } } } 其中$$namespace$$ $$tablename$$ $$KEY$$ $$ColunName$$ $$ColunNotes$$ 都是系统默认字典。 多了两个自定义字典$$TextBoxForSearch$$和$$GridColumns$$,其中$$TextBoxForSearch$$单独建了一个模板,为了让大家看看接口实现的另一种写法。 将保存好的模板放到模板目录里 第三步 编写模板翻译类 新建模板扩展类,引用CodeFactoryMVC.Main.dll,列实现接口ICodeFactory 注意第43行: 生成CodeFEx.dll,考到bin文件夹下,在web上新增接口 OK完成,刷新页面选择其余表,填写项目名称,点击生成,拷贝到原项目,随便找个表预览下 一个list页面就做好了。CodeFDemo的项目源码会放在 知识星球 提供下载, 下载地址 http://fineui.com/bbs/forum.php?mod=viewthread&tid=21482 没有加入星球的赶紧加入(越来越贵!)
三层我就不说了,主要是看框架思路可扩展。以前用FineUI开源版写过一版,修修改改自己用了,没有特意的整理,FineUIMVC开发还是比较快,移植了一下两天就弄完了,算是一个对新手有用的工具,先放出第一版发布版,没有源码但是有接口,也搭着好久没更新博客了(主要是MVC挺简单的没啥可写的...)所以在这详细说明下。 1.使用 加入了本地数据库,sqlite,主要记录下连接地址和接口地址,我本地Win10 64 环境 IIS发布后需要设置下32位兼容性: 启动后就可以连接数据库了,目前支持SqlServer 和MySQL,配置地址和密码,测试下会有连接成功提示,连不上的话,就不知了。 单击数据库名,可以加载数据库表,选择要生成的表,点击选择按钮移动到右边,左侧表有个搜索功能,两个表交互是纯前端的,有用到的可以查看下源码 选择表的主键,主键影响增删改,处理的是int型,框架会自增长,到框架时再说。 填写项目名称,框架默认了两个数据库帮助类,默认数据库访问类是项目只能有一个数据库类型的意思,生成模式传统和自定义,目前这个分类还没啥作用,就是自定义的时候除了主键都是string类型罢了(传统是按数据库类型), 这时点击生成就可以生成项目了,项目地址在发布文件夹下,项目文件,时间加项目名称文件夹 2.框架 生成的代码框架是三成的小架子,没有前台,当然可以自己见模板,一会再说。拷贝到项目中就能直接用,用起来还是很方便的 BP,文件夹,按表生成,一个表一个三层,MySql和SQLserver的路由在Base/BPBase.cs中,通过Config/DataConfig.ini配置,代码看一眼注释应该都能看懂,不行打个断点走一遍。 3.模板和接口 为了保证灵活开放了模板和接口。详细介绍 模板文件就在发布文件的mould中,后缀名.codemod的是模板文件,可以直接用记事本打开,修改其中$$***$$就是要替换的动态字符了,所以生成代码一点不神秘很偷懒,后续我会把程序默认的字典说明加上,其实看模板也能看懂,没啥神奇的,不喜欢模板可以随便改。 标识符 说明 对应属性/来源 备注 $$namespace$$ 项目名称/命名空间 ModelHelp.MsCodeSetup.namespacestr 前台填写 $$connectionString$$ 数据库连接地址 ModelHelp.MsCodeSetup.conStr 前台生成 $$DbHelperMySQL$$ MySQL帮助类 ModelHelp.MsCodeSetup.mysqlclassname 前台填写 $$DataAccessor$$ SQL Server帮助类 ModelHelp.MsCodeSetup.sqlclassname 前台填写 $$ColunName$$ Message字段(默认列名) MsTableColumns.ColunMs 数据库读取 $$ColunNotes$$ 列字段说明 MsTableColumns.ColunNotes 数据库读取 $$tablename$$ 表名 MsTableColumns.TableName 数据库读取 $$KEY$$ 主键字段 作为参数传入 MsTableColumns 数据库读取 $$KEYname$$ 主键字段列名 作为参数传入 MsTableColumns.ColunName 数据库读取 $$type$$ 字段类型 根据列字段类型动态生成 数据库读取 $$MsContent$$ 生成数据表实体时格式标记 见 MsTable.codemod 模板 $$_ForMsToGetMs$$ 生成selectSQL语句时的特殊标记 见 SQLTable.codemod 模板 $$_ForCmToAddMs$$ 生成insertSQL语句时的特殊标记 见 SQLTable.codemod 模板 $$_ForMsToAddMs$$ 生成insertSQL语句时的特殊标记 见 SQLTable.codemod 模板 $$_ForMsToUpMs$$ 生成updateSQL语句时的特殊标记 见 SQLTable.codemod 模板 再动态的生成可以使用接口配置 比如我新建个类库,引用CodeFactoryMVC.Main.dll Class1实现接口ICodeFactory,这样我就参与了生成过程,并继承所有配置参数,表名,数据库名,字段,字段备注...感动的我要掉眼泪了 生成该项目后,需要把dll放到模板生成的bin文件夹下, 在web上配置一下就可以用了,如果没效果的话,发给我、我调试... 下载地址 http://fineui.com/bbs/forum.php?mod=viewthread&tid=21482 其他扩展 发布文件中还包括一个FineUIMVC的扩展,FineUIMVCEx.dll FineUIOvereide.js FineUIOvereide.css animate-3.5.2.min.css 前台的东西比较多,也藏不住,干脆就说明一下,如果觉得一些效果很合口味可以联系我 /阴险 另外这个项目还没做完,下载没有做,如果喜欢的话可以关注,发两个赞助二维码,也不知道会不会有人扫描(奈何我发什么都是企业级应用)
Grid编辑下垃级联 看了看专业版的例子,分为以下几步,都是前端的 1.编辑父下拉框后,重置子下拉框 2.编辑子下垃框前,通过父下垃框数据得到下垃项,然后绑定数据 所以这里要截取Grid的两个事件,编辑前事件和编辑后事件 我以前博客里有编辑前事件 即beforeedit 编辑后事件为 edit 所以为第一步 Grid 加如下代码 1 <Listeners> 2 <f:Listener Event="beforeedit" Handler="Gbeforeedit" /> 3 <f:Listener Event="edit" Handler="Gedited" /> 4 </Listeners> 第二步 实现 注意注释 应该都能看明白 1 //编辑事件 在编辑专业前判断是否是男女 进行数据绑定 2 function Gbeforeedit(editor, e, eop) { 3 if (e.field == "Major") { 4 //得到选择器控件 5 var edcmp = e.column.getEditor(); 6 //根据不同值绑定数据 得到值的过程略 7 var data = GetMajorData(e.record.data.Gender); 8 edcmp.f_loadData(data); 9 } 10 } 11 //编辑后事件 编辑完 性别后 重置专业 12 function Gedited(editor, e, eop) { 13 if (e.field == "Gender") { 14 e.grid.f_updateCellValue(e.record.getId(), 'Major', ''); 15 } 16 } 17 //根据不同值绑定数据 得到值的过程略 可以直接用Ajax获取 18 function GetMajorData(Gender) { 19 if (Gender == "1") { 20 return [["材料科学与工程系", "材料科学与工程系", 1], ["化学系", "化学系", 1]]; 21 } else { 22 return [["数学系", "数学系", 1], ["物理系", "物理系", 1], ["自动化系", "自动化系", 1]]; 23 } 24 } 测试页面为grid_editor_cell_databind.aspx 版本:开源版v6.0.1 惯例 来个截图
Appcan IDE为4.0+; appcan提供了导出文件的方法 appcan.file.write 文件会自动创建,要解决的事情是Excel用字符串输出,可以考虑 csv(逗号间隔)、HTML、Xml,这些都可以通过拼接字符串导出Excel 下面给出HTML格式的模板,作为记录备份 1 function writeExcel(data) { 2 if (!appcan.isArray(data)) { 3 return; 4 } 5 var tmpl = '<html xmlns:v="urn:schemas-microsoft-com:vml"'; 6 tmpl += 'xmlns:o="urn:schemas-microsoft-com:office:office"'; 7 tmpl += ' xmlns:x="urn:schemas-microsoft-com:office:excel"'; 8 tmpl += ' xmlns="http://www.w3.org/TR/REC-html40">'; 9 tmpl += ' <head>'; 10 tmpl += ' <meta charset="UTF-8" />'; 11 tmpl += '<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>'; 12 tmpl += '<x:Name>Worksheet</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions>'; 13 tmpl += '</x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head>'; 14 tmpl += '<body>'; 15 tmpl += '<table>'; 16 tmpl += '<thead>'; 17 tmpl += '<tr>'; 18 tmpl += ' <th>标题1</th>'; 19 tmpl += ' <th>标题2</th>' 20 tmpl += '</tr>'; 21 tmpl += '</thead>'; 22 tmpl += '<tbody>'; 23 for (var i = 0; i < data.length; i++) { 24 tmpl += '<tr><td>' + data[i].字段1 + '</td>'; 25 tmpl += '<td>' + data[i].字段2 + '</td></tr>'; 26 } 27 tmpl += '</tbody>'; 28 tmpl += '</table>'; 29 tmpl += '</body>'; 30 tmpl += '</html>'; 31 32 console.log("开始导出数据"); 33 34 var myDate = new Date().format("yyyyMMddhhmm"); 35 appcan.file.write({ 36 filePath : '/storage/emulated/0/' + myDate + '.xlsx', 37 content : tmpl, 38 callback : function(err) { 39 if (err) { 40 appcan.window.openToast('导出不成功', 5000, '5', '0'); 41 //写入出错了 42 return; 43 } 44 appcan.window.openToast('导出成功' + myDate + '.xlsx', 5000, '5', '0'); 45 //写入成功了 46 } 47 }); 48 }
appcan的 uexXmlHttpMgr.send 或者 appcan.ajax无法同步请求(没有找到这个属性),只能异步,造成循环多次提交时由于延迟或网络堵塞等原因无法同步响应,导致提交顺序混乱,执行完后回调错误或丢数据,如传统方法(这里已经引用的JQ包) 1 var data=[]; 2 var d=[1,2,3,4,5,6]; 3 $.each(d, function(i, v) { 4 var req = uexXmlHttpMgr.create({ 5 method : "GET", 6 url :myurl 7 }) 8 uexXmlHttpMgr.send(req, 0, function(status, resStr, resCode, resInfo) { 9 if (status == 1) { 10 data.push(i+"OK"); 11 } 12 }); 13 }); 14 alert(JSON.stringify(data)) 输出结果为[],因为调用了多次回发,输出data时each和请求都没有执行完毕所以data中肯定是没有值的。 这时引用$.Deferred(),例子如下 1 var dtd = $.Deferred(); // 新建一个Deferred对象 2 var wait = function(dtd){ 3 var tasks = function(){ 4 alert("执行完毕!"); 5 dtd.resolve(); // 改变Deferred对象的执行状态 6 }; 7 setTimeout(tasks,5000); 8 return dtd; 9 }; 10 $.when(wait(dtd)) 11 .done(function(){ alert("哈哈,成功了!"); }) 12 .fail(function(){ alert("出错啦!"); }); 这里不做过多说明,$.Deferred()的用法详解: http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html http://api.jquery.com/category/deferred-object/ 结合appcan请求 得到如下代码 1 var d = [1, 2, 3, 4, 5, 6]; 2 var data = []; 3 var sendalldata = function() { 4 var dtdall = $.Deferred(); 5 $.each(d, function(i, v) { 6 var io = i; 7 var req = uexXmlHttpMgr.create({ 8 method : "GET", 9 url : myurl 10 }) 11 uexXmlHttpMgr.send(req, 0, function(status, resStr, resCode, resInfo) { 12 if (status == 1) { 13 console.log("结果" + i + ":" + resStr); 14 resStr = eval('(' + resStr + ')'); 15 data.push(resStr.toString()) 16 if (d.length == count) { 17 dtdall.resolve(JSON.stringify(data)); 18 } 19 } 20 }); 21 }) 22 return dtdall.promise(); 23 } 24 $.when(sendalldata()).done(function(v1) { 25 console.log(v1) 26 v1 = eval('(' + v1 + ')'); 27 console.log("结果v1" + ":" + JSON.stringify(v1)); 28 }); 输出结果为预期(运行环境为ID4.0)。 另外还有两种解决办法,第一种是网上比较常见的无限循环,但是如果还有复杂的回调代码就会很混乱,以上代码中 if (d.length == count) {... 其实也有点无限循环的意思; 第二种是使用js原生方法promise,但是appcan内核还没有到ES6(Array.prototype.filter和Array.prototype.map都没有还是自己扩展的),所以这个方法也抛弃了。
我又不用FineUI开发,所以FineUI项目经验等于0,最近在忙别的,所以也没工夫研究。积累了论坛和群里的问题,写下来留个备份 1.在grid可编辑单元格中,如果需要在点击该单元格时,单元格中所有文字内容全部选中的功能如何实现 引用地址 http://fineui.com/bbs/forum.php?mod=viewthread&tid=7730&page=1#pid36686 解决办法,利用了grid编辑前事件,现在FineUI开源版写事件很方便,所以没啥新鲜的,添加事件 Event 就是事件名称,比如 click dbclick等,Handler就是调用的方法函数,这里就写函数名称即可,参数是自动传的 1 </Columns> 2 <Listeners> 3 <f:Listener Event="beforeedit" Handler="Gbeforeedit" /> 4 </Listeners> 5 </f:Grid> 实现: 看注释应该能看懂,第一个是...第二个...第三个,反正就是参数... 1 function Gbeforeedit(editor, e, eop) { 2 //得到选择器控件 3 var edcmp = e.column.getEditor(); 4 //如果是个text 5 if (edcmp.getXType() == "textfield") { 6 //选中文字,注意延迟, 7 window.setTimeout(function () { 8 edcmp.selectText(); 9 }, 100); 10 } 11 } 2.封了一下上级回发的方法,以前写过,都是前台的,给封了一下下,改了源码 1 /// <summary> 2 /// 触发上层方法 方法名称,参数,参数,参数 3 /// </summary> 4 /// <param name="funname">方法名称</param> 5 /// <param name="eves">参数</param> 6 public void ParentsCustomEvent(string funname, params string[] eves) 7 { 8 string eve = funname + '|' + string.Join("$", eves); 9 //string js = "(function(){var aw=F.wnd.getActiveWindow();if(aw){aw.window.F.customEvent('" + eve + "');}})();"; 10 string js = "(function(){parent.F.customEvent('" + eve + "');})();"; 11 FineUI.PageContext.RegisterStartupScript(js); 12 } 13 /// <summary> 14 /// 触发上层方法 是否关闭窗体,方法名称,参数,参数,参数 15 /// </summary> 16 /// <param name="bhide">是否关闭窗体</param> 17 /// <param name="funname">方法名称</param> 18 /// <param name="eves">参数</param> 19 public void ParentsCustomEvent(bool bhide, string funname, params string[] eves) 20 { 21 string eve = funname + '|' + string.Join("$", eves); 22 string hide = ""; 23 if (bhide) 24 { 25 hide += "aw.f_hide();"; 26 } 27 string js = "(function(){var aw=F.wnd.getActiveWindow();if(aw){aw.window.F.customEvent('" + eve + "');" + hide + "}})();"; 28 //string js = "(function(){parent.F.customEvent('" + eve + "');})();"; 29 FineUI.PageContext.RegisterStartupScript(js); 30 } 这个方法就是把得到上级给得到然后回传方法,触发上级的PageManager1_CustomEvent事件第二个是个重载,给改了,实现的目的就是可以传自己想传的参数,比如下面这个方法 1 protected void btnSaveContinue_Click(object sender, EventArgs e) 2 { 3 // 1. 这里放置保存窗体中数据的逻辑 4 //方法名称,参数 5 ParentsCustomEvent("show", "就是这么封", "就是这么封1", "就是这么封2", "就是这么封3", "就是这么封4"); 6 // 2. 关闭本窗体,然后回发父窗体 7 //PageContext.RegisterStartupScript(ActiveWindow.GetHidePostBackReference()); 8 } 父页面就可以写成 1 protected void PageManager1_CustomEvent(object sender, CustomEventArgs e) 2 { 3 //方法名称 4 if (e.funname == "show") 5 { 6 string s = ""; 7 //循环参数集合 8 foreach (string item in e.evelist) 9 { 10 s += item + ";"; 11 } 12 //事件参数 13 Alert.Show(s); 14 } 15 } CustomEventArgs扩展了两个参数,就是定义了一个规则罢了 既然叫自定义事件参数那就自定义好了 1 /// <summary> 2 /// 自定义事件参数 3 /// </summary> 4 public class CustomEventArgs : EventArgs 5 { 6 private string _eventArgument; 7 /*扩展参数*/ 8 private List<string> _evelist;//事件参数集合 9 private string _funname;//触发方法名称 10 /// <summary> 11 /// 触发方法名称 12 /// </summary> 13 public string funname 14 { 15 get { return _funname; } 16 set { _funname = value; } 17 } 18 /// <summary> 19 /// 事件参数集合 20 /// </summary> 21 public List<string> evelist 22 { 23 get { return _evelist; } 24 set { _evelist = value; } 25 } 26 /// <summary> 27 /// 事件参数 28 /// </summary> 29 public string EventArgument 30 { 31 get { return _eventArgument; } 32 set { _eventArgument = value; } 33 } 34 /// <summary> 35 /// 构造函数 36 /// </summary> 37 /// <param name="eventArgument">事件参数</param> 38 public CustomEventArgs(string eventArgument) 39 { 40 _eventArgument = eventArgument; 41 /*扩展解析方法*/ 42 _evelist = new List<string>(); 43 if (eventArgument.IndexOf('|') > 0) 44 { 45 _funname = eventArgument.Split('|')[0]; 46 string[] s = eventArgument.Split('|')[1].Split('$'); 47 for (int i = 0; i < s.Length; i++) 48 { 49 _evelist.Add(s[i]); 50 } 51 } 52 } 53 } 没必要向我这么写,只是给了灵感吧 3.也是在论坛上看见的,我新想最起码攒三个在写博客,今天就看见了,查了查,封个方法就可以了 请问FineUI开源版中如何实现Grid保持行选中状态?就好像专业版中的 KeepCurrentSelection ,例子在这里 http://fineui.com/demo_pro/#/dem ... rrentselection.aspx。感觉这个很有用,现在开原版多选行只能按住Ctrl键实现点击每一行的任意位置多选,而且很容易误操作。 引用页面 http://fineui.com/bbs/forum.php?mod=viewthread&tid=7861 extjs 是有这个参数的,就是给写到源码里了 Grid.cs 文件新增属性 1 /// <summary> 2 /// 启用保存多行选择 3 /// </summary> 4 [Category(CategoryName.OPTIONS)] 5 [DefaultValue(true)] 6 [Description("启用多行选择")] 7 public bool KeepCurrentSelection 8 { 9 get 10 { 11 object obj = FState["KeepCurrentSelection"]; 12 return obj == null ? false : (bool)obj; 13 } 14 set 15 { 16 FState["KeepCurrentSelection"] = value; 17 } 18 } 3534行改下 1 if (EnableMultiSelect) 2 { 3 if (KeepCurrentSelection) 4 { 5 selectOB.AddProperty("mode", "SIMPLE"); 6 } 7 else 8 { 9 selectOB.AddProperty("mode", "MULTI"); 10 } 11 } 就可以了,环境是4.2.2 源码啥的都考走吧,也不截图了,没啥可玩的,就是好久不写,凑个一章算是 2015 最后一炮,大家跨年吉祥
实现那还差点,在事件参数里我传了一个boolall选中状态参数,这个参数由前台给的,RowSelect 传的是index 行号,就是改这,通过$符号来分开的, if (commandArgs.Length == 2) { OnRowAllSelect(new GridRowAllSelectEventArgs(bool.Parse(commandArgs[1].ToString()))); } 就是这个段代码干的事。 再写成功之前,来用js试一下,打开grid_rowcommand_download.aspx文件,因为这有个全选,把js加进去 <script src="../res/js/jquery.min.js"></script> <script> F.ready(function () { $('#Grid1').find('.x-column-header.x-column-header-checkbox').on('click', function () { __doPostBack('Grid1', "RowAllSelect$" + $(this).hasClass("x-grid-hd-checker-on")); }); }); </script> 注意hasClass的用法,这里判断选中还是没有选中,在Grid中打个断点,看看触发没触发 可以看到boolall参数为true,就是触发了,这时就可以在前台Grid控件里重写这个事件了。 正常情况下就触发成功了。 第三步封装 封装了么,当然没有,写F.ready里就不是封装啊,首先给Grid加一个属性,EnableRowAllSelectEvent 为了打开或关闭全选事件,复制EnableRowSelectEvent就行了, /// <summary> /// 全选是否自动回发 /// </summary> [Category(CategoryName.OPTIONS)] [DefaultValue(false)] [Description("全选是否自动回发")] public bool EnableRowAllSelectEvent { get { object obj = FState["EnableRowAllSelectEvent"]; return obj == null ? false : (bool)obj; } set { FState["EnableRowAllSelectEvent"] = value; } } 然后把这个用在生成方法里就可以了,用到OnFirstPreRender string rowallselectevent = ""; if (EnableRowAllSelectEvent) { rowallselectevent += "$('#" + this.ID + "').find('.x-column-header.x-column-header-checkbox').on('click', function () {"; rowallselectevent += "__doPostBack('" + this.ID + "', \"RowAllSelect$\" + $(this).hasClass(\"x-grid-hd-checker-on\"));"; rowallselectevent += " });"; } StringBuilder sb = new StringBuilder(); sb.Append(gridSelectModelScript + gridStoreScript + pagingScript + gridColumnsScript + cellEditScript ); sb.AppendFormat("var {0}=Ext.create('Ext.grid.Panel',{1});", XID, OB); sb.Append(sbGetSelectID.ToString()); sb.Append(rowallselectevent);//加到生成的输出里就可以了 AddStartupScript(sb.ToString()); 注意sb.Append的位置,这样就可以了,前台用 EnableRowAllSelectEvent="false" OnRowAllSelect="Grid1_RowAllSelect" <f:Grid ID="Grid1" Title="表格" EnableCollapse="true" ShowBorder="true" ShowHeader="true" runat="server" EnableCheckBoxSelect="True" DataKeyNames="Id,Name" Width="800px" OnRowCommand="Grid1_RowCommand" EnableRowAllSelectEvent="false" OnRowAllSelect="Grid1_RowAllSelect"> 后台cs protected void Grid1_RowAllSelect(object sender, FineUI.GridRowAllSelectEventArgs e) { Alert.Show("是否全选:"+e.boolall.ToString()); }
还是基础的东西,grid全选没有事件,给加个事件,除了需要会复制粘贴外还要点推荐那! 第一步:原理 事件被触发,方法被实现。 对于全选事件,被触发有两种方式可写,一个是通过原生extjs方式触发,extjs就没有全选的事件,正常思路就是选择的行等于全部的行就触发了,这个方法听起来就恶心,所以还是放弃了;简单点写就是第二种方法直接通过点击触发,点了全选就触发全选事件了。 所以这里还是写出来,无非就是获取元素F12 我们操作的就是这个div主意选中前和选中后的样式区别。 这样事件就写好了 1 $('#Grid1').find('.x-column-header.x-column-header-checkbox').on('click', function () {}); 方法,就是回发,这里我们要触发的是cs的事件写法,不是回发的PageManager,而是回发的自定义事件,核心在__doPostBack的参数,什么样的参数可以触发cs的事件而不是没有意义,这里我参照了已有的事件rowselect事件,这时就用到了高级程序员才能用到的方法——复制! 第二步:实现 FineUI触发的事件就是基于的js回发,rowselect 是怎么触发的呢,右键查看源码得知 listeners 监听 select 选择 __doPostBack 两个参数,一个是Grid1 就是ID= Grid1 的控件 触发的,第二个有意思,主意是写死的RowSelect$ 再加上参数 index 就是第几行,原来是通过RowSelect$ 来通知后台触发的是行选择事件,那么我们定位Gird.cs,找到RowSelect$ 第3218行有一段 1 if (EnableRowSelectEvent) 2 { 3 string validateScript = "var args='RowSelect$'+index;"; 4 validateScript += GetPostBackEventReference("#RowSelect#").Replace("'#RowSelect#'", "args"); 5 string rowSelectScript = JsHelper.GetFunction(validateScript, "model", "record", "index"); //String.Format("function(model,rowIndex){{{0}}}", validateScript); 6 selectOB.Listeners.AddProperty("select", rowSelectScript, true); 7 } 不用解释也应该能看出来,上面触发select的js 代码就是这样输出的。再点查询,又找到一个 1 else if (eventArgument.StartsWith("RowSelect$")) 2 { 3 string[] commandArgs = eventArgument.Split('$'); 4 if (commandArgs.Length == 2) 5 { 6 OnRowSelect(new GridRowSelectEventArgs(Convert.ToInt32(commandArgs[1]))); 7 } 8 } OnRowSelect 说明找到了,是在这里截取的__doPostBack 传来的参数RowSelect$并且写成了事件就可以在cs里触发他的重写了。 需要分析么?不需要,直接复制粘贴 1 else if (eventArgument.StartsWith("RowAllSelect$")) 2 { 3 string[] commandArgs = eventArgument.Split('$'); 4 if (commandArgs.Length == 2) 5 { 6 OnRowAllSelect(new GridRowAllSelectEventArgs(bool.Parse(commandArgs[1].ToString()))); 7 } 8 } 改成了RowAllSelect$ ,前台给我传这个就说明触发cs 的OnRowAllSelect 行全选事件,为啥 OnRowAllSelect(new GridRowAllSelectEventArgs(bool.Parse(commandArgs[1].ToString())));这句会有波浪线?因为没有这个方法啊!接着复制OnRowSelect 改成 OnRowAllSelect 就可以了 找到OnRowSelect 复制 1 #region OnRowAllSelect 2 private static readonly object _rowAllSelectHandlerKey = new object(); 3 /// <summary> 4 /// 头部全选事件(需要启用EnableRowAllSelect) 5 /// </summary> 6 [Category(CategoryName.ACTION)] 7 [Description("头部全选事件(需要启用EnableRowAllSelect)")] 8 public event EventHandler<GridRowAllSelectEventArgs> RowAllSelect 9 { 10 add 11 { 12 Events.AddHandler(_rowAllSelectHandlerKey, value); 13 } 14 remove 15 { 16 Events.RemoveHandler(_rowAllSelectHandlerKey, value); 17 } 18 } 19 /// <summary> 20 /// 触发行选中事件 21 /// </summary> 22 /// <param name="e">事件参数</param> 23 protected virtual void OnRowAllSelect(GridRowAllSelectEventArgs e) 24 { 25 EventHandler<GridRowAllSelectEventArgs> handler = Events[_rowAllSelectHandlerKey] as EventHandler<GridRowAllSelectEventArgs>; 26 if (handler != null) 27 { 28 handler(this, e); 29 } 30 } 31 #endregion 还有哪个有波浪?GridRowAllSelectEventArgs 没有接着建,新建cs文件,把GridRowSelectEventArgs复制过来,不要忘了写上署名 1 #region Comment 2 /* 3 * Project: FineUI 4 * 5 * FileName: GridRowAllSelectEventArgs.cs 6 * CreatedOn: 2015-10-14 7 * CreatedBy: 没想好 935732994@qq.com 8 * 9 * 10 * Description: 11 * -> 12 * 13 * History: 14 * -> 15 * 16 * 17 * 18 * 19 */ 20 #endregion 21 using System; 22 using System.Data; 23 using System.Reflection; 24 using System.ComponentModel; 25 using System.Web.UI; 26 namespace FineUI 27 { 28 /// <summary> 29 /// 表格行选中事件参数 30 /// </summary> 31 public class GridRowAllSelectEventArgs : EventArgs 32 { 33 private bool _boolall; 34 /// <summary> 35 /// 选中状态 36 /// </summary> 37 public bool boolall 38 { 39 get { return _boolall; } 40 set { _boolall = value; } 41 } 42 /// <summary> 43 /// 构造函数 44 /// </summary> 45 /// <param name="rowIndex">选中状态</param> 46 public GridRowAllSelectEventArgs(bool boolall) 47 { 48 _boolall = boolall; 49 } 50 } 51 } 这样就应该不报错了,下回再写把。
有人老找JS,我吧FineUI自己写的JS沾过来方便大家看看,在实现前端的时候更灵活 JS 实例 注释 控件 F.ready F.ready(function(){}); 就是ready 很多方法都在这里写,写在页面后 无 F.alert F.alert('提示') FineUI 的提示框 无 F.confirm confirm: function (targetName, title, msg, okScript, cancelScript, iconShortName) { 确认对话框 无 F.customEvent F.customEvent(data) 向后台回发 参数为数据 用PageManager接收 , 子页面回发父页面就是parent.F.customEvent(参数) 通用 F('ID') 选择器,返回控件实例 无 f_setTitle F('Panel1').f_setTitle('新的标题')(f_setTitle没有效果就用setTitle) 设置控件的title 需要 this.f_state['Title']; 通用 f_setCollapse F('Panel1').f_setCollapse(); 控制这折叠 需要 this.f_state['Collapsed']; Panel f_isCollapsed F('Panel1').f_isCollapsed(); 判断是否折叠 Panel f_setChecked F('CheckBox').f_setChecked(); 控制选中状态 Checked =true CheckBox f_setValue F('text').f_setValue('值') (同理 获取就是getValue注意大小写,注意getValue没有f_,最新的4.2.2没有) <f:DropDownList runat="server" ID="DropDownList1" FocusOnPageLoad="true">... <f:Button ID="Button1" Text="选中[可选择项8]" runat="server" OnClientClick="select8()" CssClass="marginr"> function select8() { F('<% =DropDownList1.ClientID %>').setValue('Value8'); } 设置控件的值 表单元素 getValue F('DropDownList1').getValue() 返回控件的值 表单元素 enableRegEx F('DropDownList1').enableRegEx=true 启用模糊查询 表单元素 f_setLabel F('text'). f_setLabel('显示值') 设置输入框表单的lable 表单元素 f_getSelectedValues var values = F('CheckboxGroup').f_getSelectedValues(); 返回 CheckboxGroup的值["value1", "value2", "value3"] CheckboxGroup f_loadData F('ComboBox1').f_loadData(data); 重置 ComboBox1 的数据源 ComboBox f_getTextByValue F('ComboBox1').f_getTextByValue('值'); 通过值获取ComboBox1的显示文字,默认当前数据源 ComboBox listConfig.maxHeight var t = F('<% = DropDownList1.ClientID %>');if(t.listConfig){t.listConfig={};}t.listConfig.maxHeight=100; 修改下拉框最大高度,设置后生效 ComboBox setText F('Button1').setText('显示文字');(f_setText重写的方式不一样 By @长沙-雏鸟 )(checkbox的text是setBoxLabel) 设置按钮的文字 Button f_getData F('Grid1').f_getData() 得到当前表格数据,数据库查询是当前页表格 Grid f_expandAllRows F('Grid1').f_expandAllRows() panel 的 展开是 expand 展开所有的行扩展列 Grid f_collapseAllRows F('Grid1').f_collapseAllRows() panel 的 折叠是 collapse 隐藏所有的行扩展列 Grid f_getSelectedCount F('Grid1').f_getSelectedCount() 获取选中的行数,或者单元格数(单元格编辑模式) Grid f_selectRows F('Grid1').f_selectRows(rows) 选中某些行,默认读取SelectedRowIndexArray数据 Grid f_selectAllRows F('Grid1').f_selectAllRows() 选中全部行 Grid f_getSelectedRows F('Grid1').f_getSelectedRows() 获取选中的行 Grid f_selectCell F('Grid1').f_selectCell() 选中单元格(AllowCellEditing)。默认获取 SelectedCell 数据 Grid f_getSelectedCell F('Grid1').f_getSelectedCell() 获取选中的单元格(AllowCellEditing) Grid f_getHiddenColumns F('Grid1').f_getHiddenColumns() 获取隐藏列的名称列表 Grid f_getColumns F('Grid1').f_getColumns() 获取表格列 Grid f_deleteSelected F('Grid1').f_deleteSelected() 从Store中删除选中的行(或者单元格) Grid f_addNewRecord F('Grid1').f_addNewRecord(data,true) 添加一条新纪录 数据,是否显示在最底下 Grid f_getDeletedRows F('Grid1').f_getDeletedRows() 获取删除的行索引(在原始的列表中) Grid columns[N].setText F('Grid1').columns[0].setText("就是好") 设置表头显示 Grid f_getCheckedNodes F('Tree1').f_getCheckedNodes() 获取选中的节点 Tree f_getSelectedNodes F('Tree1').f_getSelectedNodes() 获取选择的节点 Tree f_selectNodes F('Tree1').f_selectNodes() 设置选择节点,不接受参数 读SelectedNodeIDArray属性 Tree parentNode node.parentNode 父节点,是个属性不是个方法,详见下面的实例 Tree set() F.ready(function () { F('<% =Tree1.ClientID %>').on('checkchange', function (node, checked) { //node 当前触发的节点 checked 是否选中 //父节点选中 注意set方法直接设置属性,而不是checked = true ,set会触发一系列操作 //这种写法不会触发父节点的选中后台事件,而会触发当前节点的后台事件 node.parentNode.set("checked", checked); }); }) 直接设置属性,这里只涉及的Tree,又让我想到了封装属性,此处有淫笑声 Tree f_hide F('window1').f_hide() 隐藏(关闭)window window(通用) hide F("<%=panelMapMenuP.ClientID %>").hide(); 隐藏panel 通用 f_hide_refresh F('window1').f_hide_refresh() 隐藏刷新window window f_show F('window1').f_show(iframeUrl, windowTitle, width, height) 显示窗体参数就不说了 window(通用) show F("<%=panelMapMenuP.ClientID %>").show(); 显示panel 通用 f_maximize F('window1').f_maximize() 设置最大化 window markInvalid F('<% =TextArea1.ClientID%>').markInvalid("写点啥吧"); 表单字段验证【表单字段验证失败效果by@沈阳-Sunday】 Form clearInvalid F('<% =TextArea1.ClientID%>').clearInvalid(); 清除表单字段验证失败效果 Form startDay F('<% =DatePicker1.ClientID%>').startDay = 1; 一周开始时间,0 星期日 1 星期一 ... Form.DatePicker setFieldLabel F('<% =Label4.ClientID%>').setFieldLabel('11212'); 表单修改Lable Form completeEdit F(grid).f_cellEditing.completeEdit(); 强制完成正在编辑的单元格 Grid startEditByPosition F(grid).f_cellEditing.startEditByPosition({row:2,column2}); 跟具坐标编辑的单元格 Grid setActiveTab (f_setActiveTab 这个方法取 f_stat[ActiveTabIndex]e) F(tab).setActiveTab(index); 通过index激活页签 Tab f_getActiveTabIndex F(tab).f_getActiveTabIndex(); 得到当前激活的页签号 Tab hideTab F(tab).hideTab(tabId); 隐藏选项卡 选项卡ID Tab showTab F(tab).showTab(tabId); 显示选项卡 选项卡ID Tab addTab F(tab).addTab(id, url, title, closable); 新增选项卡 选项卡ID,地址,名称,是否关闭? Tab getTab F(tab).getTab(tabId); 得到选项卡 ID Tab removeTab F(tab).removeTab(tabId); 移除选项卡 ID Tab 先收集这些,其他的就是extjs原生的,也有常用的一些方法慢慢加吧用的到的都加上,没有的去extjs api上看吧 再补充一次,与FineUI相关的js要放在</form>下面
1.按条件控制Grid不可编辑 Grid编辑其实用到的不多...但是也有要控制权限或者其他条件不能编辑的情况其实挺简单,学过extjs的知道,我现在也只是写前台了,没有写到后台事件,有时间再说吧,如果条件是服务器判断可以参考官网的给行和列加样式的例子,轻轻点我就转到了 我这么好心就吧源码贴出来吧 1 F.ready(function () { 2 F('<% = Grid1.ClientID %>').on('beforeedit', function (editor, e) { 3 //判断数据,注意是ColumnID 4 if (e.record.get('Gender') == '1') { 5 F.alert('你是男的你忘了?不能编辑的'); 6 return false; 7 } 8 //判断字段,也是ColumnID 9 else if (e.field != 'Name') { 10 F.alert('女的就改个名得了呗...只能编辑性名列'); 11 return false; 12 } 13 //判断行号 14 else if (e.rowIdx == 1) { 15 return false; 16 } 17 //判断列号 18 else if (e.colIdx == 1) { 19 return false; 20 } 21 }); 22 }); 注意 : return false; 就是取消编辑,beforeedit就是编辑前事件,么么哒 2.禁止行选择事件 禁止指定行选择也有这个需求,也没写特别好,要自己学下,同样考虑分页和服务器条件的话要参考例子【不要用力点我就转到了】 放出源码 1 <f:HiddenField ID="highlightRows" Text="1,2,4" runat="server"> 2 </f:HiddenField> 1 <script src="../res/js/jquery.min.js"></script> 2 <script> 3 var highlightRowsClientID = '<%= highlightRows.ClientID %>'; 4 var gridClientID = '<%= Grid1.ClientID %>'; 5 function disabledRowcheck() { 6 // 增加延迟,等待HiddenField更新完毕 7 window.setTimeout(function () { 8 var highlightRows = F(highlightRowsClientID); 9 var grid = F(gridClientID); 10 $(grid.el.dom).find('.x-grid-cell-row-checker.x-item-disabled').removeClass("x-item-disabled"); 11 $.each(highlightRows.getValue().split(','), function (index, item) { 12 if (item !== '') { 13 var row = grid.getView().getNode(parseInt(item, 10)); 14 $(row).find('.x-grid-cell-row-checker').addClass('x-item-disabled'); 15 } 16 }); 17 }, 100); 18 } 19 F.ready(function () { 20 F('<% = Grid1.ClientID %>').on('beforeselect', function (t, record, index, e) { 21 var highlightRows = eval("[" + F(highlightRowsClientID).getValue() + "]"); 22 if (highlightRows.indexOf(index) >= 0) { 23 F.alert('这行不让选择了'); 24 return false; 25 } 26 else { return true; } 27 }); 28 F('<% = Grid1.ClientID %>').on('columnhide', function () { 29 disabledRowcheck(); 30 }); 31 F('<% = Grid1.ClientID %>').on('columnshow', function () { 32 disabledRowcheck(); 33 }); 34 F('<% = Grid1.ClientID %>').getStore().on('refresh', function () { 35 disabledRowcheck(); 36 }); 37 disabledRowcheck(); 38 }); 39 </script> 我是仿照例子写了 个 disabledRowcheck 方法,移出和新增了 x-item-disabled 样式,是内置的,加上了还点效果,禁止编辑也可以这么写,beforeselect 事件就是选择之前的事件,注意return ,disabledRowcheck 分别在 列隐藏,列显示,刷新事件中触发, 这是由于这几个事件都会重新绘制HTML 以上版本为 4.1.5 ,只是应用了几个extjs 的事件,其他的都来自官方示例,我也就是给出个提示,大家千万不要跪着看我的博客
以前写过一个表格自动补全,改下,就出现了部分级联修改。测试版本4.1.1 共享下JS,ext-part2.js 后台再来个得到数据的方法就可以了。 1.方法还是删除+插入,主要在 if (record.dataIndex!='Gender'&&record.dataIndex!='EntranceYear') { 这两个字段就是关联字段,可以写成配置,其他信息还是页面上的数据,这个数据来自删除前
1.加图标和事件 上次已经通过DataSimulateTreeLevelField属性控制了树节点的显示,不用看也知道就是给指定列数据前面加个图标的HTML 可以在SimulateTreeHeper类里看到实现方法,不多说了,原理就是循环行累加上图片就可以了,注意图片的ID和样式 /// <summary> /// 给树赋值 /// </summary> private void SetValueTree() { if (EnableTree) { foreach (GridRow row in Rows) { //下级行数 string Nextindex = GetNextIndex(row, Rows); //默认图标 string iconname = "page.png"; //图片ID,点击用,绑定事件 string imgid = ClientID+"_tree_icon_" + row.RowIndex; //父节点图标 if (Nextindex != "") { iconname = "folder.png"; } //加入图标和ID,注意加过的就不加了 if (row.Values[FindColumn(TreeName).ColumnIndex].ToString().IndexOf(ClientID+"_tree_icon_") < 0) { row.Values[FindColumn(TreeName).ColumnIndex] = "<img id=\"" + imgid + "\" src=\"..\\res\\icon\\" + iconname + "\" width=\"16\" height=\"16\" style=\"margin-top:-5px;margin-bottom:-2px;cursor: pointer\"/>" + row.Values[1]; } } } } /// <summary> /// 得到下级行号 /// </summary> /// <param name="row">本节点</param> /// <param name="Rows">集合</param> /// <returns>集合以,隔开</returns> private string GetNextIndex(GridRow row, GridRowCollection Rows) { string topindex = ""; int pridindex = FindColumn(TreeDataParentIDField).ColumnIndex; int idindex = FindColumn(TreeDataIDField).ColumnIndex; foreach (GridRow gr in Rows) { //父ID等于本ID添加到集合 if (gr.Values[pridindex].ToString() != "" && gr.Values[pridindex].ToString() == row.Values[idindex].ToString()) { topindex += topindex == "" ? gr.RowIndex.ToString() : "," + gr.RowIndex.ToString(); } } return topindex; } 这里有个方法是得到下级的行号,这是个核心方法。 在AfterDataBind里加上就可以了 下面就是点击方法,用JQ实现的,从后台输出 /// <summary> /// 点击事件只给有子集的点击事件 /// </summary> private void TreeClick() { StringBuilder sbx = new StringBuilder(); foreach (GridRow row in Rows) { //有下级菜单才绑定事件 string Nextindex = GetNextIndex(row, Rows); if (Nextindex != "") { string imgid = ClientID + "_tree_icon_" + row.RowIndex; sbx.Append("$('#" + imgid + "').click(function(){F.customEvent(\"GridTreeClick_" + row.RowIndex + "\");});"); } } PageContext.RegisterStartupScript(sbx.ToString()); } 还是用到了F.customEvent方法回发后台,其实可以用doPostBack方法直接在Grid.cs里执行,但是考虑到会执行其他方法还是给放到页面的cs文件了, grid_tree.cs protected void PageManager1_CustomEvent(object sender, CustomEventArgs e) { if (e.EventArgument.IndexOf("GridTreeClick_") >= 0) { string rowindex = e.EventArgument.ToString().Split('_')[1]; Grid1.DoTreeClick(rowindex); } } 可以看到DoTreeClick就是点击树图标的方法,这个方法 实现收起和展开。 2.实现展开和收起 在DoTreeClick之前先要有第一次加载方法。 首先要先定义一个参数,这个参数记录应该删除的行号,前面已经说是通过删除行实现收起和展开的 private static string _moveindex;//移除的数据 /// <summary> /// 移除的数据 /// </summary> public static string moveindex { get { return _moveindex; } set { _moveindex = value; } } 可以看到这个参数是static的不会平白消失,FineUI控制属性的Ajax回发是在构造函数里, 有兴趣的可以研究下,我还没太懂 先写一个第一次加载的方法,实现绑定了所有数据但只显示第一层根节点。 /// <summary> /// 第一次显示数方法 /// </summary> private void ShowTree() { //初始化树 if (EnableTree) { List<string> movelist = new List<string>(); //循环行 foreach (GridRow row in Rows) { //得到层级 int lub = GetLevelNub(row, Rows); if (lub != 0) { //子集删除 movelist.Add(row.RowIndex.ToString()); } } //排序(重要) 从大到小排序,不排序会删除1后造成2变成1 movelist.Sort(delegate(string str1, string str2) { return int.Parse(str2) - int.Parse(str1); }); string MoveRowIndex = ""; foreach (string s in movelist) { MoveRowIndex += MoveRowIndex == "" ? s : "," + s; } if (MoveRowIndex != null) { //放入缓存记录已经消除的行 moveindex = MoveRowIndex; string js = "" + "var store =" + XID + ".getStore();" + "var rows = [" + MoveRowIndex + "];" + "Ext.Array.each(rows, function (rowIndex, index) {" + " store.removeAt(rowIndex);" + "});" //刷新行号,(重要) + XID + ".view.refresh();"; PageContext.RegisterStartupScript(js); } //绑定树点击事件 TreeClick(); } } 逻辑就是得到删除的行号,排序,缓存,js实现,注意刷新行号方法和XID的意义。 加上方法TreeClick方法写在了ShowTree里 下面就可以做展开和收起了,再建一个参数记录展开的行号,专业版实例里可以通过属性控制指定列的展开,我控制这个参数可以实现(这个方法没有写只是理论上的...) private static List<string> _RowCollapsed;//展开集合 /// <summary> /// 展开集合 /// </summary> public static List<string> RowCollapsed { get { return _RowCollapsed; } set { _RowCollapsed = value; } } 其实这里可以留扣了,逻辑说出来也不复杂,就都一次写出来吧 /// <summary> /// 点击树事件 /// </summary> /// <param name="treenode">点击的节点(行号)</param> public void DoTreeClick(string treenode) { if (EnableTree) { StringBuilder sb = new StringBuilder(); List<string> movelist = null; if (movelist == null) { //集合 if (RowCollapsed == null) { RowCollapsed = new List<string>(); } //每次点击更改集合,移出和新增 if (RowCollapsed.Contains(treenode)) { RowCollapsed.Remove(treenode); } else { RowCollapsed.Add(treenode); } movelist = new List<string>(moveindex.Split(',')); } int PIDindex = FindColumn(TreeDataParentIDField).ColumnIndex; int IDindex = FindColumn(TreeDataIDField).ColumnIndex; //得到下级菜单 string nextindex = GetNextIndex(Rows[Convert.ToInt32(treenode)], Rows); if (nextindex != "") { string[] s = nextindex.Split(','); for (int i = 0; i < s.Length; i++) { GridRow dr = Rows[Convert.ToInt32(s[i])]; string rowindex = dr.RowIndex.ToString(); //收起动作循环子集收起 if (!RowCollapsed.Contains(treenode)) { movelist.Add(rowindex); DoNextTreeClick(rowindex, ref movelist, false); } //展开循环子集展开 else { movelist.Remove(rowindex); if (RowCollapsed.Contains(rowindex)) { DoNextTreeClick(rowindex, ref movelist, true); } } } moveindex = ""; if (movelist.Count >= 2) { movelist.Sort(delegate(string str1, string str2) { return int.Parse(str2 == "" ? "0" : str2) - int.Parse(str1 == "" ? "0" : str1); }); } foreach (string ss in movelist) { moveindex += moveindex == "" ? ss : "," + ss; } if (moveindex != null) { string js = "" //+"F.ready(function(){" + "var store =F('" + ClientID + "').getStore();" + "var rows = [" + moveindex + "];" + "Ext.Array.each(rows, function (rowIndex, index) {" + " store.removeAt(rowIndex);" + "});" //+ XID + ".f_loadData();" + "F('" + ClientID + "').view.refresh();"; //+ "});"; //sb.Append(js); sb.Append("F('" + ClientID + "').f_loadData();"); sb.Append(js); } PageContext.RegisterStartupScript(sb.ToString()); } TreeClick(); } } /// <summary> /// 循环子集 /// </summary> /// <param name="treeindex"></param> /// <param name="movelist"></param> private void DoNextTreeClick(string treeindex, ref List<string> movelist, bool Collapsed) { if (EnableTree) { StringBuilder sb = new StringBuilder(); int PIDindex = FindColumn(TreeDataParentIDField).ColumnIndex; int IDindex = FindColumn(TreeDataIDField).ColumnIndex; //得到下一菜单 string nextindex = GetNextIndex(Rows[Convert.ToInt32(treeindex)], Rows); if (nextindex != "") { string[] s = nextindex.Split(','); for (int i = 0; i < s.Length; i++) { GridRow dr = Rows[Convert.ToInt32(s[i])]; string rowindex = dr.RowIndex.ToString(); //展开 if (movelist.Contains(rowindex) && Collapsed) { movelist.Remove(rowindex); } //收起 if (!Collapsed && !movelist.Contains(rowindex)) { movelist.Add(rowindex); } //展开子集在展开集合中则执行该子集的展开 if (Collapsed && RowCollapsed.Contains(rowindex)) { DoNextTreeClick(rowindex, ref movelist, true); } } } } } 就是点击展开子集,这里判断子集已经展开就继续展开子集,收起要收起该子集的所有子集,主要是参数逻辑的计算。至此功能全部实现 下面应该写图标的展开和收起的改变,全部的前台实现,及其他编辑选择锁定列的测试和修改
1.网上找到了行合并的示例,extjs写的,我把它挪过来改了下,FineUI也能用,就是只能放着看,选择和编辑行扩展列没有测试,放出来大家看着用吧。 <script> F.ready(function () { //方法span 参数(grid实例,行号,列号,合并状态,合并数量) var span = function (grid, row, col, type, num) { //这是一个列隐藏机制,发现列隐藏了合并的效果会错位 var hiddens = [], columns = grid.columns; var b = true; Ext.Array.each(columns, function (column, index) { if (column.isHidden()) { //如果要隐藏的列隐藏了 if (col == index + 1) { //合并单元格不生效 b = false; } } }); if (b) { switch (type) { //类型,行 case 'row': //getNode 找到节点 row 就是找到行,query,用JQuery所搜所有行的td,可以看出grid其实用表格拼的 tds = Ext.get(grid.view.getNode(row)).query('td'); //通过列号找到列,编辑属性 rowspan=num 合并数量 Ext.get(tds[col]).set({ rowspan: num }); //加入垂直居中属性,当然可以自己写 Ext.get(Ext.get(tds[col])).setStyle({ 'vertical-align': 'middle' }); //循环被盖住的单元格,destroy 扔掉 for (i = row + 1; i < row + num; i++) { Ext.get(Ext.get(grid.view.getNode(i)).query('td')[col]).destroy(); } break; case 'col': //合并列的,跟行的一样,没试,自己写写 tds = Ext.get(grid.view.getNode(row)).query('td'); Ext.get(tds[col]).set({ colspan: num }); break; } } }; //执行合并方法,注意参数 span(F('<% =Grid1.ClientID%>'), 2, 2, 'row', 3); //在列隐藏和显示时执行合并行,否则会错位 F('<% =Grid1.ClientID%>').on('columnhide', function () { span(F('<% =Grid1.ClientID%>'), 2, 2, 'row', 3); }); F('<% =Grid1.ClientID%>').on('columnshow', function () { var grid = F('<% =Grid1.ClientID%>'); span(grid, 2, 2, 'row', 3); }); }); </script> 这是个思路,具体情况很复杂,不能正式使用,要用的可以自己研究下 把这个沾到grid下看看就知道了没必要发例子了。 2.加了个列头上的过滤控件,当然可以加别的 查询可以用到,有兴趣的可以自己写写,还是说方法,其实以前就提到了好多遍,就是加了个属性,然后就没了。 我在源码的GridCloumn.cs里加了两个属性,filter和filterName,记录显示的东西和开启显示控件 private bool _filter = false; /// <summary> /// 启用过滤功能 /// </summary> [Category(CategoryName.OPTIONS)] [DefaultValue(true)] [Description("启用过滤功能")] public virtual bool filter { get { return _filter; } set { _filter = value; } } private string _filterName = ""; /// <summary> /// 过滤功能的列 /// </summary> [Category(CategoryName.OPTIONS)] [DefaultValue(true)] [Description("过滤功能的列")] public virtual string filterName { get { return _filterName; } set { _filterName = value; } } 这个一看就明白了,然后我再列上用这俩个属性, <f:BoundField Width="100px" filter="true" filterName='PanelGrid1_textName' ColumnID="Name" DataField="Name" DataFormatString="{0}" HeaderText="姓名" /> 注意filterName的参数,数控件的ClientID,这个我想用asp.net写法绑着,不成功,只能手写了。 PanelGrid1_textName是个text控件,我还写了个事件,编辑回发过滤刷新表格。 属性加好了就是实现,当然是OnFirstPreRender方法里,绘制事件,加上这句就可以了,可以看到就是加了个属性items 是哪个控件,当然是PanelGrid1_textName。最后一个true是原格式输出的意思,如果要加多个控件这个items就应该是个数组,这个还没有试。 到这就算成功了。 3.给grid加了个又侧栏,现在有底部的bar,extjs里叫bbar,其实还有tbar和rbar lbar,加了个rbar,可以参照自己加,感觉没啥大用。 还是改源码,这回改的是Grid.cs文件,先找到PageItems,PageItems就是bbar那我写一个PageRItems, 就是照贴,改个名。 下一步又找到了OnFirstPreRender方法,往里加就可以了, if (PageRItems.Count > 0) { OptionBuilder RBuilder = new OptionBuilder(); //RBuilder.AddProperty("displayInfo", true); //RBuilder.AddProperty("store", Render_GridStoreID, true); JsArrayBuilder ab = new JsArrayBuilder(); foreach (ControlBase item in PageRItems) { if (item.Visible) { ab.AddProperty(String.Format("{0}", item.XID), true); } } //cls: 'x-toolbar-paging', RBuilder.AddProperty("cls", "'x-toolbar-paging x-docked-bottom x-toolbar-docked-bottom x-toolbar-docked-bottom'", true); RBuilder.AddProperty("items", ab.ToString(), true); //rbarScript = String.Format("var {0}=Ext.create('Ext.ux.SimplePagingToolbar',{1});", Render_RBarID, RBuilder); OB.AddProperty("rbar", RBuilder, true); } 这里就用到了数组,可以研究下怎么拼的,最后加到rbar里,items是PageRItems里的的item。 到了前台的话没有问题就可以看到PageRItems了和PageItems平级 可以写写事件什么的,他自己就竖着排了
我用实例项目写了个子父页面传值,算是比较灵活的写法,可以把js提取出来写成包,然后调用,我先一步一步写,为有困难的朋友打个样。 先画个页面: 上面是个查询用的表单,底下是表格,内存分页,用到了VBox布局注意BoxFlex属性的应用,页面还有一个window页面。我会把源码放出来。 1.先看查询 姓名的查询按钮会弹出一个window,打开window前后台都差不多,我用了前台,这个样子滴 <f:TriggerBox runat="server" Label="姓名" AutoPostBack="false" TriggerIcon="Search" OnClientTriggerClick="ShowWindow()" ID="TriggerBox1" EnablePostBack="False"> </f:TriggerBox> 1 //显示window 2 function ShowWindow() { 3 F('<% =Window1.ClientID %>').f_show('./selectgrid.aspx', '选择', 800, 500); 4 } 第一个参数是url,第二个是标题,然后是宽和高。js比回发快不多说。 选择window下有按钮三个:确定,确定并查询,取消在最底下,注意grid 的EnableMultiSelect 属性,三按钮都是前台写的,没有触发后台,可能是写的简单,但也感觉挺灵活的。 贴出来注意多看一眼注释,确认和确认并查询的方法,其实就是多个回发,但回发要在上层执行,方法写在后台我感觉乱,转不过来,所以都搬前台来了,一步一步写,所以灵活,我可以中间干点别的。 function select(select_back) { //得到选择的行 var rows = F('<% =Grid1.ClientID %>').f_getSelectedRows(); //选择项 var provinceName; //注意each的使用 Ext.Array.each(rows, function (rowIndex, index) { //得到选择的行数据和列数据 var rec = F('<% =Grid1.ClientID %>').store.getAt(rowIndex); provinceName = rec.get('Name'); }); //核心:得到当前的window var activeWindow = F.wnd.getActiveWindow(); //核心:执行上层的js activeWindow.window.selectProvince(provinceName, select_back); //核心:隐藏该window activeWindow.f_hide(); } 执行了上层的selectProvince方法,再把selectProvince方法贴出来,上层就是window_grid页面 function selectProvince(name, select_back) { //给控件赋值 F('<% =TriggerBox1.ClientID %>').setValue(name); //是否回发 if (select_back) { //回发事件 F.customEvent('SelectGrid'); } } 其实有了customEvent方法 爱怎么回发怎么回发,后台方法就是通过姓名查询数据,没啥好写的。 这个例子主要是打开window,打开的window通过执行上层的js回发至后台灵活执行方法。 2.修改 也是打开个window,这里得到了一个选择行的Id,当做参数传过去了,后来发现这个没用到,反正我没用到,注意 rec.get(方法 参数是ColumnID function Grid_Edit() { var rows = F('<% =Grid1.ClientID %>').f_getSelectedRows(); var id; Ext.Array.each(rows, function (rowIndex, index) { var rec = F('<% =Grid1.ClientID %>').store.getAt(rowIndex); id = rec.get('ItemId'); }); F('<% =Window1.ClientID %>').f_show('./gridedit.aspx?id=' + id, '修改', 500, 300); } 加载数据也是js写的,Bindedit方法上下也用到了。 F.ready(function () { Bindedit(); }); function Bindedit(Position) { var activeWindow = F.wnd.getActiveWindow(); var id = activeWindow.window.GetSelectID(Position); F.customEvent('Bindedit_' + id); } 用到了上层的GetSelectID方法,参数是上还是下。 function GetSelectID(Position) { var grid = F('<% =Grid1.ClientID %>'); var selectedValues = []; var rows = grid.f_getSelectedRows(), id; Ext.Array.each(rows, function (rowIndex, index) { //上机制,到0了就上不去了 if (Position == "top" && rowIndex != 0) { rowIndex = rowIndex - 1; } //到最后也上不去了 if (Position == "next" && rowIndex != grid.f_getPaging().f_pageSize - 1 && rowIndex != grid.getStore().getCount()) { rowIndex = rowIndex + 1; } //想写翻页着,不好写 if (rowIndex == grid.f_getPaging().f_pageSize - 1) { //F.customEvent('rowIndex_Next_'); } //新方法:选择当前行 selectedValues.push(rowIndex); grid.f_selectRows(selectedValues); //得到行对应列的数据 var rec = grid.store.getAt(rowIndex); id = rec.get('ItemId'); }); return id; } 还有一个方法是GetWindow,我以前写过,看看代码吧。晚了睡觉。 这个JS写的有点肿,可以直接后台调用,可以参见 ASP.NET-FineUI开发实践-17对传值进行了优化 发个图: 下载地址: CSDN 0 分 备用地址
点我订阅 目前所有博客的截图,方便离线观看,点图片 FineUI初学手册 下载,实例项目搭建 FineUI初学手册-部分JS整理 部分JS整理 ASP.NET-FineUI开发实践-1 实际开发环境是FineUI 4.0.4,编辑页面回发,__doPostBack应用 ASP.NET-FineUI开发实践-2 1.Window控件显示2.显示隐藏控件4.直接通过行号修改指定列内容5.获取iframe ASP.NET-FineUI开发实践-3 1.参照模拟数据库分页通过缓存重写内存分页,优化页面响应速度2.得到指定行指定列的值后台3.按钮至少选择一项的通用方法,OnClientClick+=累加。 ASP.NET-FineUI开发实践-4 windown控件实现右下角提醒,tips,可以翻页哦 ASP.NET-FineUI开发实践-4(二) 右下角提醒扩展动画及展示 ASP.NET-FineUI开发实践-5 1.tree的右键事件和单击事件2.Panel右键效果 ASP.NET-FineUI开发实践-6 表格自动行补全 ASP.NET-FineUI开发实践-6(二) 表格自动行补全 ASP.NET-FineUI开发实践-6(三) 表格自动行补全 ASP.NET-FineUI开发实践-7 下拉显示grid列表,选择 点着点着发现我的页签总保持一个,不知道啥时候改的,也放出来吧 ASP.NET-FineUI开发实践-8 表格下拉显示grid列表+自动行补全 ASP.NET-FineUI开发实践-8(二) 表格下拉显示grid列表+自动行补全 ASP.NET-FineUI开发实践-9 Debug模式下源码分析 ASP.NET-FineUI开发实践-9(二) Debug模式下源码分析 ASP.NET-FineUI开发实践-9(三) Debug模式下源码分析1. TextChanged事件前台触发回发后台2. 改变颜色这里用jq的css控制 ASP.NET-FineUI开发实践-9(四) Debug模式下源码分析,grid列隐藏回发 ASP.NET-FineUI开发实践-10 grid扩展列显示grid,动态数据,表格嵌套 ASP.NET-FineUI开发实践-11 子父窗口交互,主JS,前后台交互 ASP.NET-FineUI开发实践-12 1.合并行、列 2.表头插入控件 3.表格右侧边栏 ASP.NET-FineUI开发实践-13(一) 树Grid ASP.NET-FineUI开发实践-13(二) 树Grid ASP.NET-FineUI开发实践-14 Grid部分级联编辑 ASP.NET-FineUI开发实践-15 1.按条件控制Grid不可编辑 2.禁止行选择事件 ASP.NET-FineUI开发实践-16(一) Grid全选事件【后台】 ASP.NET-FineUI开发实践-16(二) Grid全选事件【封装】 ASP.NET-FineUI开发实践-17 1.编辑全选 2.子父页面触发方法传参。后台实现 3.Grid保存选择,开源程序的专业版功能 ASP.NET-FineUI开发实践-18 Grid行编辑拉下级联
嵌套Grid,光棍月大放送,不藏着掖着。实在写的不好,没脸藏啊~只考虑显示排序修改什么的都不管! 话说三石官网加实例了,http://fineui.com/demo/#/demo/grid/grid_rowexpander_grid.aspx 是用extjs写的,我写的啰嗦点 扩展行是咋出来的,我着实没看懂,但是要实现效果也有方法,先想一下, 1.嵌套的Grid数据应该是动态的,我只知道在后头绑,事件是前台触发的,那就是把ID传后台就行了。 2.一个下拉生成一个Grid,后台生成到前台我不会,前台用ExtJs生成太复杂,要写好多的ExtJs原生。 3.结合以上两点大概代码是这样的,前台触发ID传到后台,后台绑定数据,在前台复制现成的控件,显示。 开始 1.先准备个grid,就不写了,准备行扩展列 <f:TemplateField ColumnID="griditem" Hidden="true" RenderAsRowExpander="true"> <ItemTemplate> <div runat="server" id="divItem" class="expander"> </div> </ItemTemplate> </f:TemplateField> 在每行展开的时候把扩展的grid复制到id=divitem里就可以了。 2.触发事件 API里真没找见,在网上找到的,拿过来可以用, F('<% =Grid1.ClientID%>').view.on('expandBody', function (rowNode, record, expandRow, eOpts) { //传到后台,参数为行ID,行绑定的数据ID F.customEvent('GridItem_' + rowNode.id + '_' + record.get('ItemId')); }); 3.准备个扩展的Grid 正常些就可以,我放在了Panel里。JQ是为了控制Grid2的样式。 <f:Panel runat="server" ID="PanelGrid1" Height="0px" Hidden="false"> <Items> <f:Grid ID="Grid2" Width="200px" runat="server" ShowBorder="false" ShowGridHeader="true" ShowHeader="false" AllowColumnLocking="True"> <Columns> <f:TemplateField Width="60px" HeaderText="序号"> <ItemTemplate> <asp:Label runat="server" Text='<%# Container.DataItemIndex + 1 %>'></asp:Label> </ItemTemplate> </f:TemplateField> <f:BoundField runat="server" HeaderText="身高" DataField="ShenGao" /> <f:BoundField runat="server" HeaderText="体重" DataField="TiZhong" /> <f:BoundField runat="server" HeaderText="血压低" DataField="XueYaDi" /> <f:BoundField runat="server" HeaderText="血压高" DataField="XueYaGao" /> </Columns> </f:Grid> </Items> </f:Panel> F.ready(function () { F('<% =Grid2.ClientID%>').autoWidth = true; F('<% =Grid2.ClientID%>').autoHeight = true; F('<% =Grid2.ClientID %>').setWidth(F('<% =Grid1.ClientID %>').getWidth() - 100); }); 4.准备复制方法 刚才说是前台复制,两种,一个是extjs可以复制grid2元素然后显示到指定位置,没试出来,实在没时间会。 第二个方法就是整个过程的核心也是最偷懒的地方,直接复制HTML,复制HTML最大的问题是ID,两个ID一样了EXTJS会乱的,好在他只认一个,我把PanelGrid1放在了Grid1 的上面,后台找到的Grid2就是Panel里的了;复制HTML还会复制Grid2 的所有属性,如果Grid2.Hidden=Ture那也复制过去了,不显示了,所以PanelGrid1的Height=0,就是隐藏的意思。 另一个重要的就是获取了,要把HTML复制用JQ找到来源和目标,直接上代码,大家不用找了 function showgirdItme(rowid) { //选择行隐藏列的ID,rowid来自后台 var itemid = $('#' + rowid).find('div .expander').attr('id'); //复制的HTML $('#' + itemid).html($('#<% =PanelGrid1.ClientID %>-innerCt').html()); } 手动触发showgirdItme正常,rowid哪来的,在事件里获到的。 还有一个关闭事件,仍掉复制的东西,省着占地, 不要写错地方,Sunny就写错了 下面这段放到F.ready里,放ready外面获取不到F('<% =Grid1.ClientID%>')的 F('<% =Grid1.ClientID%>').view.on('collapsebody', function (rowNode, record, expandRow, eOpts) { var itemid = $('#' + rowNode.id).find('div .expander').attr('id'); $('#' + itemid).html(''); }); 5.后台 触发了后台方法,把方法打出来 Sunny没看懂; 此事件触发 protected void PageManager1_CustomEvent(object sender, CustomEventArgs e) 所以首先要定义 PageManager1_CustomEvent 事件 比如:<f:PageManager ID="PageManager1" AutoSizePanelID="Panel2" OnCustomEvent="PageManager1_CustomEvent" runat="server" /> 你们都没我对她好 if (e.EventArgument.IndexOf("GridItem_") >= 0) { string rowid = e.EventArgument.Split('_')[1].ToString(); string id = e.EventArgument.Split('_')[2].ToString(); DataTable table = GetDataTable(); DataRow[] drs = table.Select("Id = '" + id + "'"); DataTable dt = new DataTable(); dt = table.Clone(); foreach (DataRow dr in drs) { //模拟数据 dt.Rows.Add(dr.ItemArray); dt.Rows.Add(dr.ItemArray); if (Convert.ToInt32(id)>102) { dt.Rows.Add(dr.ItemArray); dt.Rows.Add(dr.ItemArray); } } //绑定 Grid2.DataSource = dt; Grid2.DataBind(); //注意延迟方法 string sc = "window.setTimeout(function () {showgirdItme('" + rowid + "');},100);"; FineUI.PageContext.RegisterStartupScript(sc); } 就是获得数据,绑定,但是看到有setTimeout方法,因为Extjs是延迟加载,他会先执行js然后在绑定,所以只能绑定完了在执行复制,这是个处理的小技巧。 没了,上个图 还要注意嵌套的Grid点不了,因为一点就是选择的Grid1的行,应该是样式就可以调,不弄了,我也用不上。源码,看吧,有心情就弄,没有自己沾吧 这玩应也有脸拿出来也算第一人了,偷懒了,等成熟了(我会了)再完善吧。 本来不想放源码着 CSDN资源10分
现在是这么个问题,在开发中表格是动态出来的,就是标准板是全部字段列出,客户要根据情况列出自己想要的,在增加操作页面的同时要是能用前台自带的功能直接保存到后台就好了,现在的列显示和隐藏是不回发的。 1.FineUI引用的extjs是ext-part1.js,这就不说了,以前截过图,这个文件是压缩的,参数也是简化的不好看,其实这个就是ext-all.js,ext-all哪来的呢,就是extjs官方实例里下的,下来之后也是压缩的,旁边还有个不压缩的,ext-all-debug.js ,完全可以看,那我直接看它就等于看ext-part1.js了,下面我要找到生成grid表头的类,搜Ext.grid.一个一个找看到有Ext.grid.header,header就是头的意思,再在这里找,找到getColumnMenu,恩,字面意思就能看出得到列菜单,怎么确定呢,看到个属性是checkHandler 选择的意思,就是选择事件,触发的是onColumnCheckChange, onColumnCheckChange:function(checkItem, checked){ var header =Ext.getCmp(checkItem.headerId); header[checked ?'show':'hide'](); }, 看到show,hide ,显示和隐藏的意思。进一步测试,找到ext-part1.js下onColumnCheckChange,改了它,加个alert('1'),保存,刷新,当我勾选列隐藏和显示时弹出1,触发成功,就是改它。 2.位置找到了就是改,直接改ext-part1.js?那ext-part2就失去意义了,ext-part2是干嘛的,FineUI自己重写替换了好多方法,也新增了好多方法方便交互或修补bug,我直接找到了Ext.grid.Panel,就照着这个重写就行,看到 if(Ext.grid.Panel){ Ext.override(Ext.grid.Panel,{ 分析下,如果是Ext.grid.Panel就是引用的是Ext.grid.Panel类,override搜了下API就是覆盖重写的意思,那好,直接重写onColumnCheckChange,onColumnCheckChange上面的列是Ext.grid.header.Container,那么在Ext.gird.Panel上面写: if(Ext.grid.header.Container){ Ext.override(Ext.grid.header.Container,{ onColumnCheckChange:function(checkItem, checked){ var header =Ext.getCmp(checkItem.headerId); header[checked ?'show':'hide'](); F.customEvent("触发后台操作"); } }); } 又用到customEvent,咋用就不写了功能是Alert.Show(),编译,保存,刷新,成功触发后台方法。 3.干触发了没参数我保存啥,看见接受了两个参数,checkItem和checkd,意思是项和选择状态,checkItem可定就是选择的项,他都有什么属性呢,还是找到ext-all-debug.js下这个方法, for(; i < itemsLn; i++){ item = items[i]; menuItem =newExt.menu.CheckItem({ text: item.text, checked:!item.hidden, hideOnClick:false, headerId: item.id, menu: item.isGroupHeader ?this.getColumnMenu(item):undefined, checkHandler:this.onColumnCheckChange, scope:this }); 首先循环项,就是循环的列头总数,一个一个来,创建一个菜单选择元素,然后就是属性,那checkItem的属性就是menuItem的属性,那么有 if(Ext.grid.header.Container){ Ext.override(Ext.grid.header.Container,{ onColumnCheckChange:function(checkItem, checked){ var header =Ext.getCmp(checkItem.headerId); header[checked ?'show':'hide'](); F.customEvent("触发后台操作保存该列隐藏显示状态,列名:"+checkItem.text+" 列ID :"+checkItem.headerId+" 选择状态:"+checked+";gridID:"+this.grid.id); } }); } 在试试,触发后台方法,输出结果为预期。 更改了ext-part2.js就要保存好,如果项目里用了,升级的时候覆盖掉就没有了,另外还要介绍自己修改和添加FineUI控件的属性,好像以前提到过在ASP.NET-FineUI开发实践-6(三) 7 里,处理了TriggerBox的回车触发,怎么找到的就是转到定义找到FineUI项目的文件再搜索就可以了,用到的不是特别多,可以结合ext-part2.js再次做一些改进,就不细谈了。
1. TextChanged事件前台触发回发后台,接上文,先给TextBox1加上事件,看看是怎么生成出来的, 注意AutoPostBack="true",找源代码,f4多出了个 listeners: { change: function () { __doPostBack('SimpleForm1$TextBox1', ''); } } 在extjs API里搜 listeners 是监听事件的意思,change当然就是事件注意可以三个参数,例子里没写但api里有,__doPostBack就是回发,没了。 function add() { var row = Ext.create('Ext.form.field.Text', { f_state: {}, fieldLabel: "文本框 1", labelWidth: 180, anchor: "0", name: "SimpleForm1$TextBox1", allowBlank: false, listeners: { //事件 change: { fn: function (el, newvalue, oldvalue) { //回发 F.customEvent(this.id + newvalue); } } } }); F('<% =SimpleForm1.ClientID %>').insert(3, row); } 注意回发方法,F.customEvent(this.id + newvalue);走的是PageManager的OnCustomEvent="PageManager1_CustomEvent"事件,FineUI提供的 http://www.fineui.com/demo/#/demo/other/custom_postback2.aspx 后台 protected void PageManager1_CustomEvent(object sender, CustomEventArgs e) { Alert.Show("这个事件是回发的"+e.EventArgument); } 2. 改变颜色这里用jq的css控制,首先找到元素 F12,上图 JQ没学过就不要看了,注意直接this.id就是该生成控件的ID, listeners: { change: { fn: function (el, newvalue, oldvalue) { //F.customEvent(this.id + newvalue); } }, //渲染完成后触发事件,查看API render: { fn: function () { //得到元素,改变css,注意this.id空格input $('#' + this.id + ' input').css("color", "red"); } } } 直接找到元素就是 $('#<%= TextBox1.ClientID %> input').css("color", "red"); 还要讲的例子是列隐藏触发后台方法,就可以保存到数据库了,要改的不仅仅是页面上的东西,至此就有了属于自己的FineUI。
其实我也不会,老实教人学怕误人子弟,但是抱着毁人不倦的精神还是糊弄糊弄个别小白吧,最起码能加点原创。 下面以表单为例,打开官方项目,版本为FineUI_4.1.1,打开form_compare页,右键在浏览器中查看,右键查看源文件,这次要做的是前台通过按钮点击生成form及与后台的交互。 1.生成一个行 form_compare里有文本行 文本框1 ,源文件里搜 文本框1 看到了什么,fieldLable就是‘文本框 1‘,说明生成的lable和后面的input(就是textbox)是一个元素,在 form_compare.aspx里也是一个元素,那太好办了,直接拿过来,这里要注意写在</form>后面,为啥呢,例子里都这么写的。 var f4肯定不能要了,一看源文件就是f1.f2.f3 一直生成下来的,改; f_state 不知道干啥的,也没东西,不管;id,肯定不能重复,先扔掉; fieldLabel 就是前面标题的意思,改成自己喜欢的; lablelWidth字面就是宽度,不管理了; anchor 不知道,不管了; name 先放着; allowBlank 不知道是啥,不管了; 成这样了 F.ready(function () { var row = Ext.create('Ext.form.field.Text', { f_state: {}, fieldLabel: "自己喜欢的", labelWidth: 180, anchor: "0", name: "SimpleForm1$TextBox1", allowBlank: false }); }); 干有元素不行啊,还加到表单里,extjs 当然提供了表单的插入方法,开api ,找到form.panel 为啥: 明显f15就是表单,items就是表单的项,f4在里面呢。下面就是猜了,好多属性和方法 新增插入的关键词:add,insert,new 挨个搜,不行就全看一边,恩搜到了 写的太清楚了,昨天已经看到F('ID')就是获取元素,那就试试吧 F('<% =SimpleForm1.ClientID %>').insert(3, row); 位置是我随便写的。 2.生成一个按钮 恩,我把重置也沾过来了,同样插入 3.给添加按钮写方法 昨天已经看见了handler就是点击事件,把新增行的事件写在方法里就可以了。 F.ready(function () { var mybutton = Ext.create('Ext.button.Button', { text: "添加自己喜欢的", cls: "marginr", handler: function () { add(); //重置表单 F('SimpleForm1').f_reset(); } }); F('<% =SimpleForm1.ClientID %>').insert(13, mybutton); }); window.row_nub = 0; function add() { row_nub++; var row = Ext.create('Ext.form.field.Text', { f_state: {}, fieldLabel: "自己喜欢的" + row_nub, labelWidth: 180, anchor: "0", name: "SimpleForm1$TextBox1", allowBlank: false }); F('<% =SimpleForm1.ClientID %>').insert(3, row); } OK,完成了,就不贴效果了。 恩,还会扩展 text的change回发及改text的颜色。该回家了不写了。
用了FineUI有一段时间了,还是分享下我咋改的吧,没想的那么难,我也是从小白来的。 基础是要懂JQ和EXTJS,主要是要懂JQ和EXTJS能干啥,这里有两个网站 http://www.w3school.com.cn/jquery/traversing_find.asp http://extjs-doc-cn.github.io/ext4api/ http://www.fineui.com/api/ http://www.fineui.com/doc/ 1. 都烂大街了,JQ的肯定要看一遍,核心的是JQ选择器和事件,动画啥的感兴趣的可以都研究,JQ能通过各种样式、ID、属性获得元素,元素包括各种控件还可以找到子控件,只要是HTML上的没有找不到的;事件,用户操作触发的就是事件,各种事件的交织让HTML不死,比如点击,双击,获得焦点,值改变等;除此之外JQ还可以重写HTML,属性,样式,得到了就能改。 2.EXTJS基于JQ,还有API,写了一个一个的类,包成控件,一个新的实例就是一个控件,事件及属性API上都有,都能找到,FineUI也基于EXTJS看起来就方便了。 FineUI官方的实例里没有js,既然基于EXTJS怎么没有JS呢,JS都写在FineUI.dll里了,底层是extjs.js,只要创建实例就可以了,这就要用到asp.net的生命周期的一部分,也很好理解,就是在也没显示之前干了一件事,把页面上写的FineUI控件转成EXTJS再由extjs.js进行渲染显示页面,当然渲染要涉及css和图片,渲染的意思就是变成了HTML代码,DIV加CSS,所以HTML基础肯定要有。 3.FineUI的api其实不想写的,这个不看就不要用了,各种属性事件都不知道干啥的,怎么用,实在看不懂就把属性加上再删除看看有什么区别。 4.doc里三石写的有点老了,捡能看的看,拉到最后有我的博客目录 见图: 这个图不是第一次截了,在页面上的没有页面都有,比如我的页面上有个取消按钮: OK,我看见了什么,Button生成的extjs格式是个啥样的,显示的文字就是text属性,事件写在了handler里,我写的是点击事件showhide那么handler就是点击事件的意思, 这个图不知道是哪的我就白写了;我还看见了所有的控件都在 F.ready(function () {,里写的,我搜了下项目,在grid_checkboxfield_rowcheckall_clientside里找到了F.ready(function () { 分析下,'<%= btnSelectRows.ClientID %>'ASP.NET的独特写法,获取控件的ID是HTML的ID,就像上图的Button2,生成的ID是Grid1_Button2,on('click')是绑定单击事件,那么F(selectRowsID)就是通过ID获得对象,这里就叫对象吧。后面的就是方法,匿名函数,就是没名,我也是蹭课才知道的。一看见$就是JQ用的,选择器,.开头的就是class名 img是标签又一个类名,得到元素后再方法XXX,恩,这个页面不运行也知道干嘛的,checked是选择的意思,这个页是控制grid行里checkbox的选择状态的,都是单词,简单。这说明啥,官方的例子也有用js控制的功能,而且很方便,比后台再回发快多了(有的功能没必要回发)。 今天晚了,明天我会再发一个表单通过js添加元素的例子,大家一起学习。
把上回的做一些改进 1.点击grid2的行改变TriggerBox1的值 var v = $(item).find('.x-grid-cell-Name div.x-grid-cell-inner').text(); $("#<% =TriggerBox1.ClientID %>-inputCell").children("input").val(v); 通过JQ改的第一行是取值,第二行是赋值 2.改变TriggerBox1的值随时刷新grid2,做个模糊查询,只要绑定事件触发后台就可以了 //TriggerBox1值改变事件简单过滤 F('<% =TriggerBox1.ClientID %>').on('change', function (field, newvalue, oldvalue) { F.customEvent('TriggerBox1_change_' + newvalue); }); 问题又来了,第一个方法是行点击改变TriggerBox1,第二个方法是改变TriggerBox1后刷新列表,冲突了,想了想TriggerBox1获取焦点才触发改变事件。给出全部代码。 JS function showhide() { F('<% =Grid2.ClientID%>').hide(); } function tbxMyBox1_TriggerClick(t) { F.customEvent('Grid2_bind_'); //先隐藏 F('<% =Grid2.ClientID%>').hide(); //位置设定样式 $('#Grid2_wrapper').css('top', $("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().top + $("#<% =TriggerBox1.ClientID %>-triggerWrap").height()); $('#Grid2_wrapper').css('left', $("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().left); $('#Grid2_wrapper').css('position', 'fixed'); $('#Grid2_wrapper').css('z-index', '9999'); //显示方法 F('<% =Grid2.ClientID%>').show(F('<% =TriggerBox1.ClientID %>').getEl(), function () { //页面的点击事件 $(document).click(function (e) { var target = $(e.target); //判断是否点击的位置,是gird和当前编辑的TriggerBox1就不变,点击其他位置就隐藏grid //closest是一层层找上层元素,找不到返回0,可以在网上看看例子 //第二个判断是grid是否隐藏,显示的再触发隐藏 if (target.closest("#<% =Grid2.ClientID%>").length == 0 && !F('<% =Grid2.ClientID%>').isHidden() && target.closest("#<% =TriggerBox1.ClientID%>").length == 0) { showhide(); } }); }); } var tbxMyBox1 = '<%= TriggerBox1.ClientID %>'; // 页面AJAX回发后执行的函数 F.ready(function () { //浮动grid2能跑加上就不跑了 F('<% =Grid2.ClientID%>').draggable = false; //编辑之前的事件 F('<% = Grid1.ClientID %>').on('beforeedit', function (editor, e) { //列名 if (e.field == 'Name') { //列号 window._selectrowIndex = e.rowIdx; window._selectcellIndex = e.colIdx; window.setTimeout(function () { //新增input获取焦点事件,获取焦点后更改则重置grid $("#<% =TriggerBox1.ClientID %>-inputCell").children("input").on('focus', function () { window.TriggerBox1_down = 1; }); }, 100); } return true; }); //TriggerBox1值改变事件简单过滤 F('<% =TriggerBox1.ClientID %>').on('change', function (field, newvalue, oldvalue) { //获取焦点后更改则重置grid,没获得焦点更改则为点击选项更改 if (window.TriggerBox1_down != 1) { F.customEvent('TriggerBox1_change_' + newvalue); } window.TriggerBox1_down = 0; }); //项点击事件 F('<%= Grid2.ClientID %>').on('itemmousedown', function (View, record, item, index, e) { F('<% =Grid1.ClientID%>').f_cellEditing.cancelEdit(); F('<% =Grid1.ClientID%>').f_cellEditing.startEditByPosition({ row: _selectrowIndex, column: _selectcellIndex }); var v = $(item).find('.x-grid-cell-Name div.x-grid-cell-inner').text(); $("#<% =TriggerBox1.ClientID %>-inputCell").children("input").val(v); }); //行双击事件 F('<% =Grid2.ClientID%>').on('itemdblclick', function (grid, record, item, index) { F.customEvent('Grid2_click_' + index); F('<% =Grid2.ClientID%>').hide(); }); }); 注意注释,都不是白写的,注意触发后台的方法F.customEvent 后台CS protected void PageManager1_CustomEvent(object sender, CustomEventArgs e) { //值改变事件 if (e.EventArgument.IndexOf("TriggerBox1_change_") >= 0) { //得到新输入的值 string newvalue = e.EventArgument.Split('_')[2].ToString(); DataTable table = GetDataTable(); DataRow[] dRows = table.Select("Name like '" + newvalue + "%'"); DataTable dtNew = table.Copy(); dtNew.Rows.Clear(); foreach (DataRow dr in dRows) { dtNew.Rows.Add(dr.ItemArray); //dtNew.Rows.Add(dr); } //重新绑定表 Grid2.DataSource = dtNew; Grid2.DataBind(); } //双击事件 if (e.EventArgument.IndexOf("Grid2_click_") >= 0) { int index = Convert.ToInt32(e.EventArgument.Split('_')[2].ToString()); string name = Grid2.Rows[index].Values[Grid2.FindColumn("Name").ColumnIndex].ToString(); DataTable table = GetDataTable(); foreach (DataRow row in table.Rows) { if (row["Name"].ToString() == name) { string deleteScript = Grid1.GetDeleteIndexReference(); //string deleteScript = ""; JObject defaultObj = new JObject(); defaultObj.Add("Name", row["Name"].ToString()); defaultObj.Add("Gender", row["Gender"].ToString()); defaultObj.Add("EntranceYear", row["EntranceYear"].ToString()); defaultObj.Add("EntranceDate", row["EntranceDate"].ToString()); defaultObj.Add("AtSchool", (bool)row["AtSchool"]); defaultObj.Add("Major", row["Major"].ToString()); PageContext.RegisterStartupScript( deleteScript //删除当前行 + Grid1.GetAddNewRecordReferenceByindex(defaultObj) //新增定义的行 + "");//把窗体管理 } } } //重新绑定事件 if (e.EventArgument.IndexOf("Grid2_bind_") >= 0) { DataTable table = GetDataTable(); Grid2.DataSource = table; Grid2.DataBind(); } } 要改的地方还是grid显示的位置,好像是不好弄,凑合看吧,主要是方法。 实例源码:CSDN 10分
上回模拟的是下拉grid,这回我把下拉grid和表格自动补全放一起了,实在是好做,但是也有很多要注意的,现在分享下,大家学习。 接上回 传送门 1. 有个tbxMyBox1_TriggerClick方法直接重写了,目的是显示下拉的列表,直接上代码注意看注释,grid ID是grid2 function tbxMyBox1_TriggerClick() { //先隐藏 F('<% =Grid2.ClientID%>').hide(); //位置设定样式 $('#Grid2_wrapper').css('top', $("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().top + $("#<% =TriggerBox1.ClientID %>-triggerWrap").height()); $('#Grid2_wrapper').css('left', $("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().left); $('#Grid2_wrapper').css('position', 'fixed'); $('#Grid2_wrapper').css('z-index', '9999'); //显示方法 F('<% =Grid2.ClientID%>').show(F('<% =TriggerBox1.ClientID %>').getEl(), function () { }); } 下面问题来了:学挖掘机技术...不是这个 grid 显示出来老在那呆着,找了半天才找见解决方案,点击grid2以外自动隐藏grid2,加上这句 //页面的点击事件 $(document).click(function (e) { var target = $(e.target); //判断是否点击的位置,是gird和当前编辑的TriggerBox1就不变,点击其他位置就隐藏grid //closest是一层层找上层元素,找不到返回0,可以在网上看看例子 //第二个判断是grid是否隐藏,显示的再触发隐藏 if (target.closest("#<% =Grid2.ClientID%>").length == 0 && !F('<% =Grid2.ClientID%>').isHidden() && target.closest("#<% =TriggerBox1.ClientID%>").length == 0) { showhide(); } }); 加到show方法里,在grid2show出来之后触发。全: function tbxMyBox1_TriggerClick(t) { //先隐藏 F('<% =Grid2.ClientID%>').hide(); //位置设定样式 $('#Grid2_wrapper').css('top', $("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().top + $("#<% =TriggerBox1.ClientID %>-triggerWrap").height()); $('#Grid2_wrapper').css('left', $("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().left); $('#Grid2_wrapper').css('position', 'fixed'); $('#Grid2_wrapper').css('z-index', '9999'); //显示方法 F('<% =Grid2.ClientID%>').show(F('<% =TriggerBox1.ClientID %>').getEl(), function () { //页面的点击事件 $(document).click(function (e) { var target = $(e.target); //判断是否点击的位置,是gird和当前编辑的TriggerBox1就不变,点击其他位置就隐藏grid //closest是一层层找上层元素,找不到返回0,可以在网上看看例子 //第二个判断是grid是否隐藏,显示的再触发隐藏 if (target.closest("#<% =Grid2.ClientID%>").length == 0 && !F('<% =Grid2.ClientID%>').isHidden() && target.closest("#<% =TriggerBox1.ClientID%>").length == 0) { showhide(); } }); }); } showhide();以前写了,就不写了。显示出来了。 2. 问题又来了,当点击grid1外的位置编辑列还原了,就是又变为正常状态,输入框没了,好像是以前写过startEditByPosition是编辑方法,在ext-part2.js里找到的,有图为证 字面意思就是开始编辑的位置,参数是行号和列号。这个方法的用处就是点击gird2时 参数grid1的beforeedit事件 F.ready(function () { //浮动grid2能跑加上就不跑了 F('<% =Grid2.ClientID%>').draggable = false; //编辑之前的事件 F('<% = Grid1.ClientID %>').on('beforeedit', function (editor, e) { //列名 if (e.field == 'Name') { //列号 window._selectrowIndex = e.rowIdx; window._selectcellIndex = e.colIdx; } return true; }); }); 当项目点击事件里随时改变编辑位置,这个事件也是斟酌过的,其他的事件有刷新,一眼就看出来了,这个事件看不见刷新。 //项点击事件 F('<%= Grid2.ClientID %>').on('itemmousedown', function (View, record, item, index, e) { F('<% =Grid1.ClientID%>').f_cellEditing.cancelEdit(); F('<% =Grid1.ClientID%>').f_cellEditing.startEditByPosition({ row: _selectrowIndex, column: _selectcellIndex }); }); 3. 就差点击事件了,我用了表格的双击事件,前台js触发,可能是用惯了 F('<% =Grid2.ClientID%>').on('itemdblclick', function (grid, record, item, index) { F.customEvent('Grid2_click_' + index); F('<% =Grid2.ClientID%>').hide(); }); 后台: protected void PageManager1_CustomEvent(object sender, CustomEventArgs e) { if (e.EventArgument.IndexOf("Grid2_click_") >= 0) { int index = Convert.ToInt32(e.EventArgument.Split('_')[2].ToString()); string name = Grid2.Rows[index].Values[Grid2.FindColumn("Name").ColumnIndex].ToString(); DataTable table = GetDataTable(); foreach (DataRow row in table.Rows) { if (row["Name"].ToString() == name) { string deleteScript = Grid1.GetDeleteIndexReference(); //string deleteScript = ""; JObject defaultObj = new JObject(); defaultObj.Add("Name", row["Name"].ToString()); defaultObj.Add("Gender", row["Gender"].ToString()); defaultObj.Add("EntranceYear", row["EntranceYear"].ToString()); defaultObj.Add("EntranceDate", row["EntranceDate"].ToString()); defaultObj.Add("AtSchool", (bool)row["AtSchool"]); defaultObj.Add("Major", row["Major"].ToString()); PageContext.RegisterStartupScript( deleteScript //删除当前行 + Grid1.GetAddNewRecordReferenceByindex(defaultObj) //新增定义的行 + "");//把窗体管理 } } } } 实例源码:CSDN 10分
下拉显示grid列表。其实很简单,但是试了很多方法,水平有限,主要是都不好使,还是简单的好使了,分享下。 先是看了看网上的,是直接写个了extjs控件类(我也不懂),然后直接用就行了,要写成FineUI的我还没那个水平,就放弃了。又看了看其他的例子,没多大用。 原理就是点击下拉时把grid在指定位置显示出来,选择grid行后赋值给下拉控件。 下拉的控件DropDownList其实并不适合,首先要捕捉展开事件,难在赋值上,这个思路是给DropDownList绑定数据下拉显示grid而不是自动的数据,grid选完后控制DropDownList选择的数据,给DropDownList绑定后出现下拉框和grid同时出现,下拉的列表弄不没,看源码得知下拉的列表时自动生成的,ID不固定,没规律获取不到,如果只绑定数据不显示数据也会出现一个自动生成的div只是没内容,但是不好看,总之不要DropDownList,用TriggerBox原因是这俩长得一样。 1.TriggerBox下拉事件就是点击事件 OnClientTriggerClick 就可以了,grid的位置还是选择了用jq控制,extjs也能控制,但不适合FineUI。 2.利用在form下grid会先生成_wrapper div标签,挪了_wrapper 就等于挪了grid 3.上代码 function showgrid() { //获取的是TriggerBox1里那个输入框的位置,不是TriggerBox1的位置,因为还有个lable的宽度 //alert($("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().left); //alert($("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().top); //设置位置,top和left $('#Grid1_wrapper').css('top', $("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().top + $("#<% =TriggerBox1.ClientID %>-triggerWrap").height()); $('#Grid1_wrapper').css('left', $("#<% =TriggerBox1.ClientID %>-triggerWrap").offset().left); //设置该div是浮动 $('#Grid1_wrapper').css('position', 'fixed'); //显示grid,第二个参数是显示完成后执行的函数,先放着可以跟后台交互重新加载grid啥的 F('<% =Grid1.ClientID%>').show(F('<% =TriggerBox1.ClientID %>').getEl(), function () { }); //extjs 的方法showAt好像不管用,没试 //F('<% =Grid1.ClientID%>').showAt(F('<% =TriggerBox1.ClientID%>').getXY(),true); } 恩就这几行,已知问题是Grid1_wrapper老长了,挪完了可能会出滚动条,这个就不解决了,应该好弄。 如果grid能移动把在ready把这句加上 F('<% =Grid1.ClientID%>').draggable = false; 4.赋值,前台grid选择事件老是没试成功,只能选择了后台事件,注册grid的行点击事件,咋写就不说了,看例子,后台是获取数据和显示 TriggerBox1.Text = Grid1.Rows[index].Values[Grid1.FindColumn("Name").ColumnIndex].ToString(); 这句是显示名字的,如果是ID就放隐藏空间里,然后在取,就不写了。 上个图 5.grid的分页条弄出来了,放了个取消就是关闭grid,直接在前台实现是 F('<% =Grid1.ClientID%>').hide(); 这个功能可以放在TriggerBox控件的第二个按钮X完成,就不写了。 6.点着点着发现我的页签总保持一个,不知道啥时候改的,也放出来吧 7.DropDownList的展开收起事件 F('<% =DropDownList1.ClientID%>').on('expand', function (combo) { }); F('<% =DropDownList1.ClientID%>').on('collapse', function (combo) { //F('<% =Grid1.ClientID%>').hide(); }); 后记:DropDownList可以判断选择位置,地方不够了会显示到上面或者改变选项div的大小。我这个没判断,想弄的自己加吧,多测测
自动补全也算是好东西吧,我也不清楚下拉列表可以过滤为啥还有自动补全,其实自动补全用到还是通过jq获取服务端的动态数据补全。我没做动态的例子,其实好写,就不写了。 1.用到了两个js包 <script src="../../res/js/jquery.min.js" type="text/javascript"></script> <script src="../res/jqueryuiautocomplete/jquery-ui.min.js" type="text/javascript"></script> 2.用到的方法为 autocomplete 属性为source 值是 数组 3.我在后台拼了数组 4.找到数据框,很简单F12 看见我给放出来:.x-form-trigger-input-cell input 这里用jq获取就可以了 5.前台的input做绑定就可以了,这时要获取一个事件,即单击或双击编辑动态生成输入框后用户输入之前绑定补全数据,很伤脑筋,总之最后只找到beforeedit事件可以在编辑框生成之前出发,很恶心,所以做了延迟处理,在FineUI的js里setTimeout用的很多,拿来用。 6.成功,应该是有数据了,这个选择有两种一是选完了回车,二是鼠标点击选择,给选择加点代码实现多列的绑定,我直接触发了选好了按钮的点击事件,可以灵活写,完整代码: 7.按回车有BUG!,是因为倒霉的TriggerBox有默认的回车选择事件,右键查看源码,搜onTriggerClick就能看见specialkey回车触发了选择事件,FineUI项目里当然有是生成的,好像是用EnableEdit控制的,扔掉,新增EnabledSpecialkey属性,默认是false /// <summary> /// 是否允许编辑 /// </summary> [Category(CategoryName.OPTIONS)] [DefaultValue(true)] [Description("是否允许编辑")] public bool EnableEdit { get { object obj = FState["EnableEdit"]; return obj == null ? true : (bool)obj; } set { FState["EnableEdit"] = value; } } /// <summary> /// 是否触发回车 /// </summary> [Category(CategoryName.VALIDATION)] [DefaultValue(false)] [Description("是否触发回车")] public bool EnabledSpecialkey { get { object obj = FState["EnabledSpecialkey"]; return obj == null ? false : (bool)obj; } set { FState["EnabledSpecialkey"] = value; } } 都沾上看的明白, 改哪个文件我就不写了。。。,到现在回车就不触发打开window窗体的事件了。 全部成功,有BUG自己改。
1.上回说到修改以前的会出现好几个5: 这是因为新增时是只新增到最后一行,所以点击选好了就跑到最后一行了,而且行号不会累积,只加到初始化的行号。 其实js里是有插入的,例子里可以插入到第一行,新增是add,插入是insert。在ext-part2.js里 insert的参数是0,就是第一行,换成行号就可以了。我重写了方法 insert 的参数是rowIndex, rowIndex=window._selectrowIndex window._selectrowIndex是在弹出windown时记录的,即TriggerBox的点击js 在这里给到_selectrowIndex是因为如果点击选好了按钮前表格失去焦点新增和删除就无效了 重写了删除方法 再源码里加入重写的调用 成功。 再看个效果 呵呵,下次再说。
FineUI4.1.0更新,传说的V4版稳定版,很多人也从3.0+升级了,接着又连续更新了几次,现在是V4.1.3 2014-09-09日更新的。更新的挺快,感觉跟不上节奏,我很欣慰,看来开原版还是靠谱的。 表格编辑动态创建行第一次是在用友上看见的,后来想了想FineUI实现起来还真不难,把新增行的JObject给前台就可以了,JObject完全可以在后台拼。原理就是新增行,选择,把这个行删了在新增上动态拼出来的行: 见图,一个一个说 1.新增行 新增行按钮新增了一个 用户名,空空空空。。。 的列 ,此处用户名可选,我用的是模板列 TriggerBox ,TriggerBox怎么用就不写了,唯一的目的是把window显示出来,window里有个选择框,下拉选择后确定。 2.选好了 点击选好了,用户名那列就变成了选择的列,变的方法已经说了,就是删了再新增,上代码 呵呵,沾不了。 官网例子改的取数据,对比,得到删除行的JS,拼数据,输出。 3.嘿嘿,是不是有BUG,恩,没找到的我告诉你 看列号,好多个5,这是因为我修改前面的,前面的删了,新增的行号还是最后一行。解决方法是把行号扔掉,让用户傻傻的,或者,看下一期 PS:是时候推翻JS了。
1.tree的右键事件和单击事件 页面就不写了,准备一个树和一个菜单控件,随便写点啥 JS:注意注释 var menuSettings = F('menuSettings'); var tree = F(IDS.tree); //右键事件itemcontextmenuthis:view;record:属于选项的记录;item:选项元素;index:选项索引e:事件对象 tree.on('itemcontextmenu', function (view, record, item, index, event) { //判断子节点(重要) if (record.isLeaf()) { //阻止原右键事件 event.stopEvent(); //菜单显示位置,即鼠标点击位置 menuSettings.showAt(event.getPoint()); } }); //左键事件itemclick:参数一样 tree.on('itemclick', function (view, record, item, index, event) { //判断子节点(重要) if (record.isLeaf()) { //阻止原左键事件 event.stopEvent(); //参数获取url,text,icon,ShowWindow是执行的方法 ShowWindow(record.data.href, record.data.text, record.data.icon, record.data.icon); } }); //右键菜单出现后按钮点击的事件 F('<%= btnAdd.ClientID %>').on('click', function () { //得到tree var tree = F(IDS.tree); //得到tree数据,选项,Array.each在数组中定位,最后返回数据 var record = tree.getSelectionModel(); var selection = record.getSelection(); var rdata; Ext.Array.each(selection, function (record, index) { rdata = record; }); //获得右键对应的项的信息(url,icon,text) Addlinks(rdata.data.href, rdata.data.icon, rdata.data.text, rdata.data.icon); }); 2.Panel右键效果 一个panel一个tree var menu1 = F('<%= menu1.ClientID %>'); //panel的右键事件注意el,e:事件,t:元素 F('Panel1').el.on('contextmenu', function (e, t) { //阻止事件 e.stopEvent(); //菜单显示位置 menu1.showAt(e.getPoint()); }); //Panel1里有个标题栏,不用右键 F('<%= Toolbar1.ClientID %>').el.on('contextmenu', function (e, t) { //直接阻止了 e.stopEvent(); });
在网上找了找,实验了一下window弹出和关闭的动画效果分享一下。 接上次的代码 default.js window_tips.animCollapse= true;//打开动画效果 window_tips.animateTarget = Ext.getBody();//开始的定位,就是以哪定位,是个el,=按钮 也行 ext-part2.js f_showTips: function里加上 window_tips.on('beforehide', this.f_hideTips);//注册关闭前事件 //显示提醒窗体; //iframeUrl窗体的url,目前未考虑传''; //windowTitle窗体的title,目前未考虑传''; //txthtml窗体显示的数据 //width窗体的宽,目前未考虑传''; //height窗体的高,目前未考虑传''; f_showTips: function (iframeUrl, windowTitle,txthtml, width, height) { if (typeof (iframeUrl) === 'undefined') { iframeUrl = this.f_iframe_url; } if (typeof (windowTitle) === 'undefined') { windowTitle = this.title; } window.label_html=txthtml;//显示数据存到全局参数里 window.label_html_num=0;//第一次打开重置查看到第几条 this.f_showtips_label(label_html);//显示方法,传入数据 this.on('beforehide', this.f_hideTips);//注册关闭前事件 //this.on('beforeshow', this.f_beforshowTips); windowTitle="消息提醒"+" "+(label_html_num+1)+"/"+txthtml.length;//拼写标题 //原show方法 F.wnd.show(this, iframeUrl, windowTitle, this.f_property_left, this.f_property_top, this.f_property_position, this.id + '_Hidden', width, height); }, f_hideTips: function () { var self = this; if (self.hidden) { return false; }//隐藏就不执行 $("#window_tips_wrapper .x-ie-shadow").css("display","none");//阴影写死了 $("#window_tips_wrapper .x-shim").css("display","none");//写死了 self.el.slideOut('r',{ duration: 1000});//动画 r 是右的意思,后面的是时间 self.hidden=true;//隐藏属性 return false; //不执行默认的hide },
最近实在没时间研究东西,FineUI一直也没进一步实践,但是还是很想学点东西,所以找了个课题研究了下,在论坛里看见了又下角的提醒,自己想了想做了一个,我不是大神,接触EXTJS很少,就是用到哪看哪,没有系统的学习,所以这次做的不细只是提供下思路和学习方法,可以让初学者借鉴探讨。以下环境为官方实例FineUI4.0.6。 1.JS文件 首先运行项目进入default.aspx,右键查看源文件看见引用了三个JS文件<script src="/extjs/ext-part1.js" type="text/javascript"></script><script src="/extjs/ext-part2.js" type="text/javascript"></script><script src="/extjs/lang/zh_CN.js" type="text/javascript"></script> 最后一个是语言包,前两个都是FineUI自己基于EXTJS写的js包,打开有看到ext-part1是压缩(没有格式)的,ext-part2没有压缩,可以看成是FineUI写的js方法的增强或者重写。 default.aspx 在最后还引用了default.js文件,打开后都是本页的一些配置文件,写在F.ready()里,页面加载执行。这里要重申下FineUI的NO js 是页面上尽量减少JS的使用,其实都是打包好的。基于EXTJS.NET 不可能没有JS。 2.实现右下角提醒准备 2.1又下角的提醒很常见,我借鉴了FineUI论坛里的这个 可以看见基本元素有标题,翻页,关闭,不可拖动,不可调整大小,在页面顶端等。这让我想到了window组件,window打开时就是层级置顶的,而且有标题、不可拖动、关闭、不可调整大小等属性。页面里的消息我选择了Label控件。 传图片是因为不能一下看懂的可以自己把代码敲上理解一下。 Lable里 Label="1" ShowLabel="false" 没用,我写的玩的。 2.2下面要准备的就是显示,FineUI window.cs文件里提供了获取显示窗体的客户端脚本的方法GetShowReference,还有其他各种重载,到最后用返回的是f_show的js方法。要显示我的window_tips就是要重写f_show方法,在ext-part2.js里一下就搜到了,复制粘贴,取个名字叫f_showTips, 简单的看一下就是重写iframeUrl和标题,然后再执行wnd.show()(wnd就是window的缩写)方法,这个就够用了wnd.show就不重写了,也在这个js里,搜索‘show:’不是很难看懂。同样找到了关闭的方法 f_hide也看到了怎么实现的 ,还是用这个,没必要写新的。 2.3显示和关闭准备好了就是信息的显示还有翻页功能,明显window的标题那一行就有标题关闭最大化什么的,要自定义添加俩按钮还得用脑子,一下子就想起来default.js里预备下了 这个是FineUI4.0新加的,在树菜单的顶上加了个设置的图标点完了显示个菜单。正常的往回缩怎么能有个设置按钮呢,.aspx文件里也没有这个的按钮所以肯定就是js自己做了什么。 看的懂的参数第一个是类型,第二个是提示,第三个不知道,第四个就是按钮点击触发的方法。要实验我的蒙的对不对要先把window_tips的ClientID传过去,详见页面上GetClientIDS方法,在js接一下, 怎么实践的就不说了就是传个带ShowHeader="true"的控件看看有没有加上就行,是成功了,但是跑到关闭那个X的右侧了,因为是新增的,插入到左侧我也不会,干脆连关闭一块新增,但是都是设置图标,应该跟tyoe有关系,gear是设置的意思,其他的type我也没不知道啊。显示的是个图标那肯定跟样式有关,果断F12 嗯,被发现了,他旁边那个就是向左样式是x-tool-img x-tool-collapse-left,collapse是隐藏属性,隐藏-向左,左右关闭就都出来了, window_tips.addTool({ type: 'left', tooltip: '上一条', regionTool: true, handler: function (event, toolEl, panelHeader) { }); window_tips.addTool({ type: 'right', tooltip: '下一条', regionTool: true, handler: function (event, toolEl, panelHeader) { } }); window_tips.addTool({ type: 'close', tooltip: '关闭', regionTool: true, handler: function (event, toolEl, panelHeader) { window_tips.hide();//关闭方法 } }); 关闭按钮我把关闭方法加上了。 2.4还有就是window的显示位置,提醒肯定是在右下角,在官方的教程里提到位置黄金分割点,在ext-part2.js wnd.show()里找到了定义位置的方法, if (left !== '' && top !== '') { panel.setPosition(parseInt(left, 10), parseInt(top, 10)); } else { var bodySize = target.window.Ext.getBody().getViewSize(); var panelSize = panel.getSize(), leftTop; if (isGoldenSection) { leftTop = _calculateGoldenPosition(bodySize, panelSize); } else { leftTop = _calculateCenterPosition(bodySize, panelSize); //panel.alignTo(target.Ext.getBody(), "c-c"); } panel.setPosition(leftTop.left, leftTop.top); } 找能看的懂的,不多解释,总之在f_showTips下面加个方法 f_tipsPosition:function(){ var left = Ext.getBody().getViewSize().width - 4 - this.width; var top = Ext.getBody().getViewSize().height - 4 - this.height; this.f_property_left=left; this.f_property_top=top; this.setPosition(left, top); }, 在default.js里触发,window_tips.f_tipsPosition();就行了。 2.5左右信息的图标有了,还没有方法,可以写成dopostback获取,也可以执行JS,当然是写JS,把数据放个参数里,是个json再用js解析,不提了,直接看代码。 3.完成 f_tipsPosition没粘 ext-part2.js //显示提醒窗体; //iframeUrl窗体的url,目前未考虑传''; //windowTitle窗体的title,目前未考虑传''; //txthtml窗体显示的数据 //width窗体的宽,目前未考虑传''; //height窗体的高,目前未考虑传''; f_showTips: function (iframeUrl, windowTitle,txthtml, width, height) { if (typeof (iframeUrl) === 'undefined') { iframeUrl = this.f_iframe_url; } if (typeof (windowTitle) === 'undefined') { windowTitle = this.title; } window.label_html=txthtml;//显示数据存到全局参数里 window.label_html_num=0;//第一次打开重置查看到第几条 this.f_showtips_label(label_html);//显示方法,传入数据 windowTitle="消息提醒"+" "+(label_html_num+1)+"/"+txthtml.length;//拼写标题 //原show方法 F.wnd.show(this, iframeUrl, windowTitle, this.f_property_left, this.f_property_top, this.f_property_position, this.id + '_Hidden', width, height); }, //显示信息方法 //txthtml信息 //t翻页方向 f_showtips_label:function(txthtml,t){ //计算翻到第几页了 var nub=label_html_num; if (t=='left') {label_html_num=window.label_html_num-1;} if (t=='right') {label_html_num=window.label_html_num+1;} if (typeof (txthtml[label_html_num]) == 'undefined') { label_html_num=nub; } //设置标题 this.setTitle("消息提醒"+" "+(label_html_num+1)+"/"+txthtml.length); //显示信息,JQ方法,ID写死了,JSON格式所以写成 txthtml[0] $('#window_tips_window_tips_label-inputEl').text(txthtml[label_html_num]); } default.js //新增翻页按钮 window_tips.addTool({ type: 'left', tooltip: '上一条', regionTool: true, handler: function (event, toolEl, panelHeader) { window_tips.f_showtips_label(label_html, 'left');//触发翻页方法 } }); //新增翻页按钮 window_tips.addTool({ type: 'right', tooltip: '下一条', regionTool: true, handler: function (event, toolEl, panelHeader) { window_tips.f_showtips_label(label_html, 'right');//触发翻页方法 } }); //新增关闭按钮 window_tips.addTool({ type: 'close', tooltip: '关闭', regionTool: true, handler: function (event, toolEl, panelHeader) { window_tips.f_hide(); } }); //定位坐标 window_tips.f_tipsPosition(); //网上找的 Ext.EventManager.onWindowResize(window_tips.f_tipsPosition, window_tips); //window大小改变时,重新设置位置 Ext.EventManager.on(window, 'scroll', window_tips.f_tipsPosition, window_tips); //滚动时重新设置位置 传值 protected void MenuButton1_Click(object sender, EventArgs e) { List<string> liststr = new List<string>(); liststr.Add("消息一"); liststr.Add("消息二"); var resultObj = JsonConvert.SerializeObject(liststr); PageContext.RegisterStartupScript(window_tips.GetShowTips(String.Empty, String.Empty, Unit.Empty, Unit.Empty, resultObj)); } Window.cs加了方法 /// <summary> /// 返回右下角提醒JS /// </summary> /// <param name="iframeUrl">地址</param> /// <param name="windowTitle">标题</param> /// <param name="width">宽</param> /// <param name="height">高</param> /// <param name="innerHtml">JSON值提醒的HTML</param> /// <returns></returns> public string GetShowTips(string iframeUrl, string windowTitle, Unit width, Unit height, string innerHtml) { if (!String.IsNullOrEmpty(iframeUrl)) { iframeUrl = ResolveIFrameUrl(iframeUrl); } iframeUrl = JsHelper.GetJsStringWithScriptTag(iframeUrl); windowTitle = JsHelper.GetJsString(windowTitle); if (width != Unit.Empty && height != Unit.Empty) { return String.Format("{0}.f_showTips({1},{2},{3},{4},{5});", ScriptID, iframeUrl, windowTitle, innerHtml, width.Value, height.Value); } else { return String.Format("{0}.f_showTips({1},{2},{3});", ScriptID, iframeUrl, windowTitle, innerHtml ); } } 4.扩展 遗憾的是没有时间在完善下,标题,传iframeUrl,标题,定义大小都没写。显示和隐藏的滑动效果没有,这个还得学...,很突兀的显示和关闭了,FineUI并没有提供动画的方法。 5.后记 其实实现的方法有很多,我又不是专业的,自己弄出来有成就感,只是给刚入门的人看的,想说的是FineUI中提供了很多现成的可以学习的代码,肯研究的都能自己做点东西,FineUI是开源的后台的C#方法和js方法都是现成的谁也没说不能改。谁要是有现成的例子给我也共享个,我很愿意学习学习。
1.参照模拟数据库分页通过缓存重写内存分页,优化页面响应速度 Grid的响应速度是硬伤,我写了个通用方法把所有数据放在缓存中模拟数据库分页,比自带的缓存分页快很多,这里贴上实体类的通用方法,DataTable的就不贴了可以参照官方的自己写。记住要加上grid_PageIndexChange事件和grid_Sort事件,这里就不写了 /// <summary> /// 获取数据源 /// </summary> /// <returns></returns> private void GetData() { List<MsUser> lstUser = GetData();//数据库返回数据实体 ViewState["lstUserData"] = JsonConvert.SerializeObject(lstUser); } /// <summary> /// 数据源绑定 /// </summary> private void BindGridView() { string slstUserData = ViewState["lstUserData"].ToString(); BindGridView<MsUser>(slstUserData, ref grid); } /// <summary> /// 绑定数据源 /// </summary> /// <typeparam name="T">范数据</typeparam> /// <param name="vDataNew">数据Json</param> /// <param name="g">grid</param> public static void BindGridView<T>(string vDataNew, ref Grid g) { List<T> DataNew = (List<T>)JsonConvert.DeserializeObject(vDataNew, typeof(List<T>)); g.RecordCount = DataNew.Count; int pageSize = g.PageSize;//获取页显示数 int pageIndex = g.PageIndex;//获取当前页数 int pi = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(DataNew.Count) / Convert.ToDouble(g.PageSize))); pageIndex = pageIndex + 1 > pi ? pi - 1 : pageIndex;//重新绑定时(如查询)要判断页数 if (pageIndex < 0) { pageIndex = 0; } try { Reverser<T> reverser = null; string SortField = g.SortField;//获取排序字段 if (SortField == "") { SortField = g.Columns[1].SortField;//默认第二列为排序列 } reverser = new Reverser<T>(DataNew[0].GetType(), SortField, g.SortDirection == "ASC" ? ReverserInfo.Direction.ASC : ReverserInfo.Direction.DESC);//排序方法 DataNew.Sort(reverser); if (pageSize <= DataNew.Count) { int pz = pageSize < DataNew.Count - pageIndex * pageSize ? pageSize : DataNew.Count - pageIndex * pageSize; if (pageSize == DataNew.Count) { pz = pageSize; } DataNew = DataNew.GetRange((pageIndex) * pageSize, pz);//截取数据 } g.DataSource = DataNew;//绑定 g.DataBind(); } catch (Exception ex) { //Response.Write(ex.Message); g.DataSource = null; g.DataBind(); } g.PageIndex = g.PageIndex + 1 > g.PageCount ? g.PageCount - 1 : g.PageIndex; if (g.PageIndex < 0) { g.PageIndex = 0; } } /*从网上下的实体类排序类*/ /// <summary> /// 继承IComparer<T>接口,实现同一自定义类型 对象比较 /// </summary> /// <typeparam name="T">T为泛用类型</typeparam> public class Reverser<T> : IComparer<T> { private Type type = null; private ReverserInfo info; /**/ /// <summary> /// 构造函数 /// </summary> /// <param name="type">进行比较的类类型</param> /// <param name="name">进行比较对象的属性名称</param> /// <param name="direction">比较方向(升序/降序)</param> public Reverser(Type type, string name, ReverserInfo.Direction direction) { this.type = type; this.info.name = name; if (direction != ReverserInfo.Direction.ASC) this.info.direction = direction; } /**/ /// <summary> /// 构造函数 /// </summary> /// <param name="className">进行比较的类名称</param> /// <param name="name">进行比较对象的属性名称</param> /// <param name="direction">比较方向(升序/降序)</param> public Reverser(string className, string name, ReverserInfo.Direction direction) { try { this.type = Type.GetType(className, true); this.info.name = name; this.info.direction = direction; } catch (Exception e) { throw new Exception(e.Message); } } /**/ /// <summary> /// 构造函数 /// </summary> /// <param name="t">进行比较的类型的实例</param> /// <param name="name">进行比较对象的属性名称</param> /// <param name="direction">比较方向(升序/降序)</param> public Reverser(T t, string name, ReverserInfo.Direction direction) { this.type = t.GetType(); this.info.name = name; this.info.direction = direction; } //必须!实现IComparer<T>的比较方法。 int IComparer<T>.Compare(T t1, T t2) { object x = this.type.InvokeMember(this.info.name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty, null, t1, null); object y = this.type.InvokeMember(this.info.name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty, null, t2, null); if (this.info.direction != ReverserInfo.Direction.ASC) Swap(ref x, ref y); return (new CaseInsensitiveComparer()).Compare(x, y); } //交换操作数 private void Swap(ref object x, ref object y) { object temp = null; temp = x; x = y; y = temp; } } /**/ /// <summary> /// 对象比较时使用的信息类 /// </summary> public struct ReverserInfo { /**/ /// <summary> /// 比较的方向,如下: /// ASC:升序 /// DESC:降序 /// </summary> public enum Direction { ASC = 0, DESC, }; public enum Target { CUSTOMER = 0, FORM, FIELD, SERVER, }; public string name; public Direction direction; public Target target; } 2.得到指定行指定列的值后台,rows的values是行的值,后台可以看到,通过列号找到数组的值,这个方法不是很保险但是聊胜于无 01.grid.Rows[rowindex].Values[grid.FindColumn(Columnid).ColumnIndex].ToString(); 3.按钮至少选择一项的通用方法,OnClientClick+=累加。 /// <summary> /// 至少选择一项 /// </summary> /// <param name="g"></param> /// <param name="ctrls"></param> private void NoSelectionNull(Grid g, params FineUI.Button[] ctrls) { foreach (FineUI.Button ctrl in ctrls) { ctrl.OnClientClick += g.GetNoSelectionAlertReference("至少选择一项!"); } } protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { NoSelectionNull(Grid1, Button4, Button2, Button3, Button6); } }
1.选择程序版本 2.可以创建文件夹 3.新建片段 4.给片段取名 5.双击进行编辑 6.点击保存 7.直接使用
之前做项目的时候都是在每个页面中处理这不同的异常信息,一个页面数下来,很多个try{}catch{}语句块,令整个代码结构有些不够美观。 今天看到一篇帖子,是关于利用全局应用程序类来帮忙获取异常信息,利用 server.Transfer('''')指定接受错误的页面;加上在接受错误页面中利用 server.GetLastError() 获取前一个异常源。 Global.asax 中的Application_Error 函数如下: protected void Application_Error(object sender, EventArgs e) { //捕获整个解决方案下的所有异常 try { Server.Transfer("~/Error.aspx"); } catch { } } 错误接受页面 Error.aspx 获取异常信息的相关代码如下: Exception ex = Server.GetLastError().GetBaseException(); //获取异常源 if (ex != null) { Response.Write(ex.Message); } //清空前一个异常 Server.ClearError(); 测试页面Text.aspx中的测试异常代码如下: //测试是否捕获了异常信息 //test1 //int UserID = Convert.ToInt32(Request["UserID"].ToString()); //test2 string Name = "aganar"; int UID = Convert.ToInt32(Name); 运行Test.aspx页面,我们会看到相关的异常信息,我们能够清晰地看出,在页面Test.aspx页面中未曾有任何一个try{}catch{}语句块存在,我们即可很方便轻松地捕获到异常信息。
女朋友鄙视我原创少... 1.下载 进入官方论坛:http://www.fineui.com/bbs/ 要用到下载源代码和空项目下载 http://fineui.codeplex.com/ http://fineui.com/bbs/forum.php?mod=viewthread&tid=2123 http://pan.baidu.com/s/1bntUOr1 源代码直接下载,注意FineUI版本 空项目里下载 对应版本的空项目,注意.NET版本,与VS版本相关。_V后是FineUI的版本号。最新的在网盘里 注意看.net版本, 解压,现在得到两个文件 第二个是源码,第一个是空项目。把空项目里的extjs包复制到源码 FineUI_v4.2.0_source_all/FineUI.Examples 目录里, 这时源代码就可以生成运行了。 注意FineUI开发环境,比如,我电脑上是VS2010,打不开现在的最新项目4.2.+,其实就是解决方案打不开,会出现双击没反应情况。我用VS2010先建个空解决方案,再把项目加进去就可以了。 最新的有个test直接扔,不知道干啥的, 有些Vs2010 会出现 找不到 控件的问题, 解决办法就是 创建个工具箱 把DLL添加到工具箱里面 然后随便拖一个控件到窗体上 再把控件删掉 @北京-砖家 或者升级vs补丁 @沈阳-Sunday 2.学习FineUI 解决方案包含两个项目 FineUI FineUI.Examples ,FineUI 项目生成FineUI为FineUI后台源码,生成对应的FineUI.dll, FineUI.Examples为例子程序,对应官方在线示例http://fineui.com/demo/。 default.aspx为启动页,是个母版页式的页面,其他页面以页签形式展现,页面打开后上边是logo,左边是目录树,右边是显示区域。 生成项目成功后初学者应当对照项目查看官方教程http://www.fineui.com/doc/,对整个教程查看之后(最少看完前四章)对FineUI开发有个初步了解。 项目运行后可以点击 源代码 查看当前页面源代码 方便学习。地址栏会给出当前页面的地址http://fineui.com/demo/#/demo/grid/grid_groupheader.aspx即FineUI.Examples 目录下grid/grid_groupheader.aspx文件,VS中搜索grid_groupheader.aspx.cs即可。 注意打开解决方案跟踪活动项。 所有方法属性(不包括js)都可以在官方API查找 http://www.fineui.com/api/ ,提供理解属性的意义。 加入QQ群 158028499 方便解答各种问题,里面的保姆很专业,而且群共享里还有好多资源。 3.源代码分析 注意查看Web.config学习dll的引用。 AssemblyInfo.cs配置版本和页面信息。 CommonResourceHelper.RegisterCommonResource可配置页面加载引用的js,js目录在FineUI.Examples\extjs 可以自己添加必备的js。 其中string metaContent 参数配置网页信息。 ResourceManager提示为找到extjs错误的信息。 所有配置页面都继承自PageBase 其中绘制页面时通过Cookie判断页面样式。表格的创建(即模拟数据)及其他页面需要的方法。 控件的所有方法都可以找到对应源码(F12)供学习参考。 extjs为整个FineUI开源版基础,FineUI优化成了两个js文件,,其中ext-part2.js未压缩,可以参考学习一些基础用法。ext-part1可以搜索解压软件解压查看,也可参考extjs源码下的ext-4.2.1-gpl\ext-4.2.1.883\ext-all-debug.js文件。 FineUI是开源的,直接用的程序员不是好厨子。
最近发现很多人用动软代码生成,确实方便,有些经验记录下,以后查看回顾。 ..\Maticsoft\Codematic2\Template\TemplateFile 为模板文件夹,直接在目录下新建文件夹【我的自定义模版】,有个【模版示例.cmt】也直接复制到自定义文件下好了。在动软模版管理代码模板右键刷新就看见了,选择模板右键编辑并查看进入模板编辑。 头 <#@ template language="c#" HostSpecific="True" #> <#@ output extension= ".cs" #> <# TableHost host = (TableHost)(Host); host.Fieldlist.Sort(CodeCommon.CompareByintOrder); #> 语言是C#,输出文件是.cs,没啥好说的。TableHost host 就是当前的表信息,主要使用字段集合,下面的代码 host.Fieldlist 就是列的集合.Sort给集合排序。这里要注意模版代码的格式 <# #> 在这里的代码为模版识别的代码,不在这里的就是写啥生成啥。 基本语法知道了,下面我给出我的message生成代码分析下,循环列按固定格式生成。 <# foreach (ColumnInfo c in host.Fieldlist) { #> private string _str<#= c.ColumnName.ToString().Split('_')[2] #>;// <#= c.Description #> <# } #> <# foreach (ColumnInfo c in host.Fieldlist) { #>/// <summary> /// <#= c.Description == "" ? c.ColumnName : c.Description #> /// </summary> public string str<#= c.ColumnName.ToString().Split('_')[2] #> { get{ return ((!string.IsNullOrEmpty(_str<#= c.ColumnName.ToString().Split('_')[2]#>)) ?_str<#= c.ColumnName.ToString().Split('_')[2]#>: ""); } set{ _str<#= c.ColumnName.ToString().Split('_')[2]#> = value; } } <# } #> 支持语言是C# 当然有foreach 而 host.Fieldlist列集合的类型就是ColumnInfo 这里定义c 要注意<# #>格式。c.ColumeName为列名称可以.ToString()可以分组Split,c.Description为得到备注信息及数据库里字段说明,这个很有用,省着自己看不明白。第二段有了个三元表达式 如果说明为空则输出列名,注意标签<#= #>这里多个了= 表示输出不是处理代码。到最后注意 <# } #> 表示foreach的结束。关于.Split('_')[2]就是数据库字段的设计了,不细说。<#= host.GetModelClass(host.TableName) #>为输出表名。还有的判断用到了参数,设置个<#int i#>记录换行,<#if(i==5){i=0;#>");<#="\n"#><#}#> 记录和输出换行。 主要就是理解模板输出不在标签里的就是些啥输出啥,我一般就是把写好的代码都粘到模板里然后哪是动态的改哪。关于直接生成三层框架,这个跟自己的代码规范和数据库设计有关没有什么通用的地方,就不说明了。
用VS2008开发C#语言wince程序,发现程序里右键捕获不到,采集器上点也没反应,上网查好像有个c++版本的,看不懂啊,下面我给出C#实现右键效果的解决方案,请各位多多优化。 首先控件ContextMenu 和 Timer 设置contextMenu1 在dataGrid里实现右键点击,事件dataGrid1_MouseDown 按下鼠标按钮事件。 上代码 public int _x; public int _y; private void dataGrid1_MouseDown(object sender, MouseEventArgs e) { System.Drawing.Point Pt = new Point(e.X, e.Y); DataGrid.HitTestInfo Ht = dataGrid1.HitTest(Pt.X, Pt.Y); //this.dataGrid1.CurrentCell = new DataGridCell(Ht.Row, Ht.Column); //this.dataGrid1.Select(Ht.Row); if (Ht.Row >= 0 && Ht.Column >= 0) { _x = e.X + 2;//记录鼠标当前坐标 _y = e.Y + 1;//同上(priorPoint前面已经声明) SHRGINFO shr = new SHRGINFO(); shr.cbSize = Marshal.SizeOf(typeof(SHRGINFO)); shr.dwFlags = SHRGFLags.SHRG_RETURNCMD; shr.ptDownX = MousePosition.X - 2; shr.ptDownY = MousePosition.Y - 8; shr.hwndClient = GetActiveWindow(); int ret = SHRecognizeGesture(ref shr); if (ret == 1000) { timer1.Enabled = true;//开启计时器 } } } internal struct SHRGINFO { public int cbSize; public IntPtr hwndClient; public int ptDownX; public int ptDownY; public SHRGFLags dwFlags; } [Flags] internal enum SHRGFLags { SHRG_RETURNCMD = 0x00000001, SHRG_NOTIFYPARENT = 0x00000002, SHRG_LONGDELAY = 0x00000008, SHRG_NOANIMATION = 0x00000010, } [DllImport("aygshell")] extern private static int SHRecognizeGesture(ref SHRGINFO shr); [DllImport("coredll.dll", SetLastError = true)] public static extern IntPtr GetActiveWindow(); DataGrid.HitTestInfo 我也是第一次用,把坐标传进去就能知道当前左边是哪列哪行,有没有数据,下面做出判断有数据了才显示。 ret == 1000圆圈正好一圈 [DllImport("aygshell")] extern private static int SHRecognizeGesture(ref SHRGINFO shr); [DllImport("coredll.dll", SetLastError = true)] public static extern IntPtr GetActiveWindow(); 启用采集器DLL接口程序。 MousePosition.X - 2 当前鼠标-2这个是控制圆圈的,不在鼠标中建,我给调了调, _X _Y 会在时间里用到。timer1的事件,如果在此弹出右键菜单因为资源没有被释放,所以会报错的,只能启动下一个事件,我写在了timer1_Tick里,并使用timer1.Enabled = true启用事件 private void timer1_Tick(object sender, EventArgs e) { string pids = this.dataGrid1[this.dataGrid1.CurrentRowIndex, 0].ToString(); if (pids != "") { contextMenu1.Show(dataGrid1, new Point(_x, _y));//弹出上下文菜单 timer1.Enabled = false; } } 要写上contextMenu1绑定的控件dataGrid1 this.dataGrid1[this.dataGrid1.CurrentRowIndex, 0].ToString();可获得当前选中行某列。
1.Windows 中的换行符"\r\n" 2.Unix/Linux 平台换行符是 "\n"。 3.MessageBox.Show() 的换行符为 "\n" 4.Console 的换行符为 "\n" 换行符还因平台差异而不同。 为保持平台的通用性,可以用系统默认换行符 System.Environment.NewLine。
客户端开始 private void button1_Click(object sender, EventArgs e) { Execute(); } public void Execute() { IDestinationConfiguration ID = new MyBackendConfig(); RfcDestinationManager.RegisterDestinationConfiguration(ID); //登录 RfcDestination prd = RfcDestinationManager.GetDestination("PA0_000"); Execute(prd); //退出登录 RfcDestinationManager.UnregisterDestinationConfiguration(ID); } //登陆SAP前的准备工作 public class MyBackendConfig : IDestinationConfiguration { public RfcConfigParameters GetParameters(String destinationName) { if ("PA0_000".Equals(destinationName)) { RfcConfigParameters parms = new RfcConfigParameters(); parms.Add(RfcConfigParameters.AppServerHost, "192.168.2.165"); //SAP主机IP parms.Add(RfcConfigParameters.SystemNumber, "00"); //SAP实例 parms.Add(RfcConfigParameters.User, "SAP*"); //用户名 parms.Add(RfcConfigParameters.Password, "123"); //密码 parms.Add(RfcConfigParameters.Client, "000"); // Client parms.Add(RfcConfigParameters.Language, "ZH"); //登陆语言 parms.Add(RfcConfigParameters.PoolSize, "5"); parms.Add(RfcConfigParameters.MaxPoolSize, "10"); parms.Add(RfcConfigParameters.IdleTimeout, "60"); return parms; } else return null; } public bool ChangeEventsSupported() { return false; } public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged; } 分析:IDestinationConfiguration 字面意 我的指定设置 ,实例的一个类MyBackendConfig ,该类继承IDestinationConfiguration,说明该类用来配置或重写标准配置。 RfcDestinationManager.RegisterDestinationConfiguration(ID);保存配置。 //登录 RfcDestination prd = RfcDestinationManager.GetDestination("PA0_000"); 走的是RfcDestinationManager里的方法,并传递参数PA0_000,跟踪代码得知执行的是 public RfcConfigParameters GetParameters(String destinationName)方法,及自定义配置的方法。 其中:RfcConfigParameters.SystemNumber对应的是SAP中系统编号,在登录窗口点击属性 RfcConfigParameters.Client为集团编号 用户名密码一定要大写。 剩下的就不用管了。连不上的话会有各种提示的。 核心方法读取录入数据 public void Execute(RfcDestination prd) { RfcRepository SapRfcRepository = prd.Repository; IRfcFunction myfun = SapRfcRepository.CreateFunction("ZORDER_RFC"); IRfcStructure import = null; IRfcTable table = myfun.GetTable("IN_ORDER"); for (int i = 0; i < 3; i++) { import = SapRfcRepository.GetStructureMetadata("ZORDERTEST").CreateStructure(); import.SetValue("ORDERID", i); import.SetValue("ORDERCODE", DateTime.Now.ToString("yyyyMMdd")); import.SetValue("ORDERTIME", DateTime.Now); import.SetValue("ORDERUSER", "小李"+i); table.Insert(import); } myfun.Invoke(prd); string RETURNStr = myfun.GetString("R_SUBRC"); MessageBox.Show(RETURNStr); string[] columns = new string[]{ "ORDERID", "ORDERCODE", "ORDERTIME", "ORDERUSER" }; DataTable dt = new DataTable(); foreach (string clmn in columns) { dt.Columns.Add(clmn); } table = myfun.GetTable("IN_ORDER"); for (int i = 0; i < table.RowCount; i++) { DataRow dr = dt.NewRow(); foreach (string clmn in columns) { dr[clmn] = table[i].GetString(clmn); } dt.Rows.Add(dr); } this.dataGridView1.DataSource = dt; } 分析SapRfcRepository.CreateFunction("ZORDER_RFC"); 传入函数名称。 myfun.GetTable("IN_ORDER");,是gettable,传的是参数名。 SapRfcRepository.GetStructureMetadata("ZORDERTEST").CreateStructure();这传的是表名, myfun.GetString("R_SUBRC");接收返回的参数,应该是‘OK’,同样是参数名 dr[clmn] = table[i].GetString(clmn);table是接口自定义的类型,跟踪看下结构,像个json。 到星期一接口调通,还得学习SAP做单,表情如下
在客户端(非服务端) ,选择本地文件夹,一次导出多个excel。 前台: <div> <asp:LinkButton class="linktext" id="lnbExcel" runat="server" onclick="lnbExcel_Click" > 导出excel</asp:LinkButton> </div> <script> function browseFolder(path) { try { var Message = "\u8bf7\u9009\u62e9\u6587\u4ef6\u5939"; //选择框提示信息 var Shell = new ActiveXObject("Shell.Application"); var Folder = Shell.BrowseForFolder(0, Message, 64, 17); //起始目录为:我的电脑 //var Folder = Shell.BrowseForFolder(0, Message, 0); //起始目录为:桌面 if (Folder != null) { Folder = Folder.items(); // 返回 FolderItems 对象 Folder = Folder.item(); // 返回 Folderitem 对象 Folder = Folder.Path; // 返回路径 if (Folder.charAt(Folder.length - 1) != "\\") { Folder = Folder + "\\"; } document.getElementById(path).value = Folder; return Folder; } } catch (e) { alert(e.message); } } </script> <table> <tr> <td>选择导入数据源:</td> <td><asp:TextBox ID="path" runat="server"></asp:TextBox></td> <td><input type=button value="选择" onclick="browseFolder('path')"></td> </tr> </table> 后台 //导出EXCEL protected void lnbExcel_Click(object sender, EventArgs e) { #region dataTable导出到excel 弹出保存对话框 for (int i = 0; i < 2; i++) { string saveFileName = path.Text + i; int rowIndex = 1; //开始写入数据的单元格行 int colIndex = 0; //开始写入数据的单元格列 System.Reflection.Missing miss = System.Reflection.Missing.Value; Microsoft.Office.Interop.Excel.Application mExcel = new Microsoft.Office.Interop.Excel.Application(); mExcel.Visible = false; Microsoft.Office.Interop.Excel.Workbooks mBooks = (Microsoft.Office.Interop.Excel.Workbooks)mExcel.Workbooks; Microsoft.Office.Interop.Excel.Workbook mBook = (Microsoft.Office.Interop.Excel.Workbook)(mBooks.Add(miss)); Microsoft.Office.Interop.Excel.Worksheet mSheet = (Microsoft.Office.Interop.Excel.Worksheet)mBook.ActiveSheet; //Microsoft.Office.Interop.Excel.Range er = mSheet.get_Range((object)"A1", System.Reflection.Missing.Value); //向Excel文件中写入标题文本 //er.Value2 = fileName.Substring(0, fileName.LastIndexOf('.')); try { colIndex++; mSheet.Cells[1, colIndex] = "列1";//输出DataGridView列头名 rowIndex++; colIndex = 0; colIndex++; mSheet.Cells[rowIndex, colIndex] = "'" + "支部到"; //获取你使用的excel 的版本号 string _strVersion = mExcel.Version; int FormatNum = 0x00; if (Convert.ToDouble(_strVersion) < 12)//You use Excel 97-2003 { FormatNum = -4143; } else//you use excel 2007 or later { FormatNum = 56; } //保存工作已写入数据的工作表 mBook.SaveAs(saveFileName, FormatNum, miss, miss, miss, miss, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, miss, miss, miss, miss); //mBook.SaveAs(saveFileName, FormatNum, miss, miss, miss, miss, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, miss, miss, miss, miss); } catch (Exception ee) { throw new Exception(ee.Message); } finally //finally中的代码主要用来释放内存和中止进程() { mBook.Close(true, miss, miss); mBooks.Close(); mExcel.Quit(); //System.Runtime.InteropServices.Marshal.ReleaseComObject(er); System.Runtime.InteropServices.Marshal.ReleaseComObject(mSheet); System.Runtime.InteropServices.Marshal.ReleaseComObject(mBook); System.Runtime.InteropServices.Marshal.ReleaseComObject(mBooks); System.Runtime.InteropServices.Marshal.ReleaseComObject(mExcel); GC.Collect(); } } #endregion 命名空间 using Microsoft.Office.Interop.Excel; IE测试通过。在文件名冲突时可能报其他错误。 另,在可能受服务端excel配置影响报一下错误: 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件失败,原因是出现以下错误: 80070005 拒绝访问。 (异常来自 HRESULT:0x80070005 (E_ACCESSDENIED))。 说明: 执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。 异常详细信息: System.UnauthorizedAccessException: 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件失败,原因是出现以下错误: 80070005 拒绝访问。 (异常来自 HRESULT:0x80070005 (E_ACCESSDENIED))。 (部分)... 附加决解方案连接 http://blog.csdn.net/zijings8374/article/details/4256005 (未测试 0..0) 检索COM 类工厂中CLSID 为 {00024500-0000-0000-C000-000000000046}的组件时失败 在项目中将数据导出为Excel格式时出现“检索COM 类工厂中CLSID 为 {00024500-0000-0000-C000-000000000046}的组件时失败,原因是出现以下错误: 80070005”,从网上搜了一下有如下解决方案: 1:在服务器上安装office的Excel软件. 2:在"开始"->"运行"中输入dcomcnfg.exe启动"组件服务" 3:依次双击"组件服务"->"计算机"->"我的电脑"->"DCOM配置" 4:在"DCOM配置"中找到"Microsoft Excel 应用程序",在它上面点击右键,然后点击"属性",弹出"Microsoft Excel 应用程序属性"对话框 5:点击"标识"标签,选择"交互式用户" 6:点击"安全"标签,在"启动和激活权限"上点击"自定义",然后点击对应的"编辑"按钮,在弹出的"安全性"对话框中填加 一个"ASP.net"用户(注意要选择本计算机名),并给它赋予"本地启动"和"本地激活"权限. 7:依然是"安全"标签,在"访问权限"上点击"自定义",然后点击"编辑",在弹出的"安全性"对话框中也填加一个"ASP.net"用户,然后赋予"本地访问"权限. 这样,我们便配置好了相应的Excel的DCOM权限. 注意:这是在WINxp上配置的,在2003上,ASP.net用户改为NETWORK SERVICE用户。 根据上述操作了一遍,结果还是这个错误,后来在组件服务中又将“Microsoft Office Excel 预览器”配置了一遍才解决问题,配置过程如下: 1、在"DCOM配置"中找到"Microsoft Office Excel 预览器",在它上面点击右键,然后点击"属性",弹出"Microsoft Office Excel 预览器属性"对话框 2、点击"安全"标签,在"启动和激活权限"上点击"自定义",然后点击对应的"编辑"按钮,在弹出的"安全性"对话框中填加
1、Math.Round(0.333333,2);//按照四舍五入的国际标准 2、 double dbdata=0.335333; string str1=String.Format("{0:F}",dbdata);//默认为保留两位 3、 float i=0.333333; int j=(int)(i * 100); i = j/100; 4、 decimal.Round(decimal.Parse("0.3333333"),2) 5、 private System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo(); float test=0.333333f; nfi.NumberDecimalDigits=2; string result=test.ToString("N", nfi); 6、 string result= String.Format("{0:N2}",Convert.ToDecimal("0.333333").ToString());
JQgrid for asp.net 网上资料较少,自己总结了些不全,能用到的可以借鉴下 控件: @ Register Assembly="Trirand.Web" Namespace="Trirand.Web.UI.WebControls" TagPrefix="cc1" 使用: <cc1:JQGrid ID="Jqgrid1" runat="server" Width="800px"> </cc1:JQGrid> 内部控件: 【cc1:JQGrid】:控件,可配置属性包括 OnRowAdding新增方法(后台) protectedvoid Jqgrid1_RowAdding(object sender, Trirand.Web.UI.WebControls.JQGridRowAddEventArgs e) OnRowEditing修改方法(后台) protectedvoid Jqgrid1_RowEditing(object sender, Trirand.Web.UI.WebControls.JQGridRowEditEventArgs e) MultiSelect="true"选择框 MultiSelectMode选中模式:SelectOnRowClick选中即选择,SelectOnCheckBoxClickOnly点击选择框选择 OnDataRequesting加载方法(后台) protectedvoid Jqgrid1_DataRequesting(object sender, Trirand.Web.UI.WebControls.JQGridDataRequestEventArgs e) onsearching查找方法 protectedvoid JQGrid1_Searching(object sender, Trirand.Web.UI.WebControls.JQGridSearchEventArgs e) 【ToolBarSettings】:配置信息,可配置属性包括 ShowAddButton是否显示新增(true,false), ShowEditButton是否显示修改(true,false), ShowDeleteButton是否显示删除(true,false), ShowSearchToolBar是否显示查找(头部)(true,false), ShowSearchButton是否显示查找(按钮)(true,false) ShowInlineAddButton="true"控制行添加按钮 ShowInlineCancelButton="true"控制取消编辑按钮 ShowInlineDeleteButton="true"控制行删除按钮 ShowInlineEditButton="true"控制行修改按钮 ****/实例/**** <ToolBarSettings ShowAddButton="true" ShowEditButton="true" ShowDeleteButton="true" ShowSearchToolBar="true" ShowSearchButton="true" /> ****/实例/**** 【<Columns>】:开始绑定列</Columns> 【<cc1:JQGridColumn>】:绑定一列,可配置属性包括 Editable是否在修改页显示, Searchable是否可以查找, HeaderText列表头显示, DataField绑定数据源 Frozen="true"是否固定该列 EditDialogColumnPosition=(int)修改时坐标列 EditDialogRowPosition=(int)修改时坐标行 ****/实例/**** <cc1:JQGridColumn Editable="true" Searchable="true" HeaderText="ID" DataField="order_no"> </cc1:JQGridColumn> ****/实例/**** 【AddDialogSettings】:新增窗口配置,没有此控件弹出默认配置窗口,可配置属性包括 CancelText取消文本信息, Caption标题文本显示, ClearAfterAdding新增后清除(字面意), CloseAfterAdding新增后关闭(字面意), Draggable是否可以拖动窗口(此窗口为置顶窗口), TopOffset离浏览器高度, LeftOffset离浏览器左边长度, LoadingMessageText加载时显示文本(字面意), Modal模型(true,false), ReloadAfterSubmit返回后台(猜的), Resizable允许拉伸页面, SubmitText确定文本 【EditDialogSettings】:修改窗口配置 【DeleteDialogSettings】:删除窗口配置 ****/实例/**** <AddDialogSettings CancelText="取消" Caption="新增" ClearAfterAdding="true" CloseAfterAdding="true" Draggable="true" Height="400" Width="800" TopOffset="20" LeftOffset="40" LoadingMessageText="Adding a new row" Modal="true" ReloadAfterSubmit="true" Resizable="false" SubmitText="保存" /> <EditDialogSettings CancelText="Cancel Editing" Caption="Edit Dialog" CloseAfterEditing="true" Draggable="true" Height="400" Width="400" TopOffset="50" LeftOffset="20" LoadingMessageText="Currently Editing Data" Modal="true" ReloadAfterSubmit="true" Resizable="true" SubmitText="修改" /> <DeleteDialogSettings CancelText="Cancel delete" Draggable="true" Height="400" Width="400" TopOffset="100" LeftOffset="100" LoadingMessageText="Deleting" Modal="false" ReloadAfterSubmit="true" Resizable="true" SubmitText="Do delete" /> ****/实例/**** 【SearchDialogSettings】:查找窗口配置 Draggable是否可以拖动窗口(此窗口为置顶窗口), FindButtonText查看文本, ResetButtonText重置文本, MultipleSearch多重查询, TopOffset离浏览器高度, LeftOffset离浏览器左边长度, 【PagerSettings】:页脚属性 PageSize每页显示条数 PageSizeOptions分页显示数量,是个列表PageSizeOptions="[10,20,50,100,]" 【ClientSideEvents】:自定义事件,里面有好多时间,前台的, RowSelect选择事件function rowSelected(rowID, isSelected) {} LoadComplete翻页后启动,loadfunction restoreSelectedRows() {} JS常见方法及参数 【var grid = jQuery("#<%= Jqgrid1.ClientID %>");】:得到控件,必要参数 .setSelection(int row);选择行
当我在窗体初始化的时候,调用了一个外部的dill时,它就不知什么原因的 抛出一个“正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码”的异常,程序就卡掉了,在网上查了查,相关说明如下: .NET2.0中增加了42种非常强大的调试助手,MDA.Loaderlock 是其中之一。Loaderlock检测在一个拥有操作系统loader lock的线程上运行托管代码的情况。这样做有可能会引起死锁,并且有可能在操作系统加载器初始化DLL前被使用。 大致理解:就是窗体还没有完全生成,而你在这个时候就调用了别的dill,可能就抛出了这个异常! 解决此异常的方法为: 方法一 把vs2005菜单的 调试->异常->Managed Debuggin Assistants->LoaderLock 的选中状态去掉 如果异常(exception)这一项没有的话,在工具---自定义---命令选项卡---左边选择调试--右边把异常托到菜单里 快捷键Ctrl+Alt+E,修改Managed Debuggin Assistants->LoaderLock 的选中状态去掉 方法二 在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework下面增加一个String,值为"0" 不过这样做,在该计算机上所有基于.NET2.0开发都得不到MDA的好处。 来自:http://blog.sina.com.cn/s/blog_657061860100ogax.html
网上找的例子是这样的 private void button2_Click(object sender, EventArgs e) { FastReport.TfrxReportClass report = new FastReport.TfrxReportClass(); report.MainWindowHandle = (int)this.Handle; report.LoadReportFromFile("report\\jhjl.fr3");//添加报表路径 FrxDataTable rptdataset1 = new FrxDataTable("adoquery1"); FrxDataTable rptdataset2 = new FrxDataTable("adoquery2"); OleDbDataAdapter oleDbDataAdapter1 = new OleDbDataAdapter("select * from TZ_Unit", Class1.conn1); oleDbDataAdapter1.Fill(rptdataset1); OleDbDataAdapter oleDbDataAdapter2 = new OleDbDataAdapter(listsql, Class1.conn1); oleDbDataAdapter2.Fill(rptdataset2); // rptdataset1.AssignToReport(true, report); // rptdataset2.AssignToReport(true, report); // rptdataset2.AssignToDataBand("MasterData1", report); report.ShowReport(); } 我照着这个用第二次打开报表报错,后来发现.ClearDatasets();方法,要在实例后添加,第二次不报错。 report = new TfrxReportClass(); report.ClearDatasets(); report.SetGlobalVariable("Language", "Chinese");
1.检查web.config中是否加入对于asp.net ajax的支持的代码 2.检查ScriptManager中是否设置了EnableMethods=true 3.检查后台cs中是否引用的命名空间System.Web.Services或者加入了[System.Web.Services.WebMethod] 4.后台函数必须是public static 5. 还有一种情况就是,通常,有些人在复制这个aspx页面时,经常是连同,<%@ Page Language="C#" AutoEventWireup="true" CodeFile="addSight.aspx.cs" Inherits="Page_message_addSight" %>一起复制了,所以造成文件头的映射出现错误,导致PageMethods的方法指向出现错误,而这种错误并没有显示那里错误,所以检查这样的错误。这是新手经常出现的错误。
接收 string queryClause = ViewState["serQueryClause"].ToString(); msAstQuery = (MsQuery)JavaScriptConvert.DeserializeObject(queryClause, typeof(MsQuery)); 存储 ViewState["serQueryClause"] = JavaScriptConvert.SerializeObject(msAstQuery);
$(document).ready(function () { var Loginsession = '<%=((Smt.Message.LoginUser)Session["LogUserInfo"]).pSkinPath%>'; '<% =(Session["MySession"]=20) %>'; var mysession = '<%=(Session["MySession"])%>'; })
官网给的例子里单页全选得不到ID,一个一个选能得到,所以我要添加JS方法把rowid存到一个hidden里以便让后台也能收到,使全选时能存储ID。 选中状态的方法为.setSelection(rowid),因为本来这个页就被选中了,所以这个方法就不用写了。 第二步我要得到本页的所有rowid,因为我的rowid绑定的是数据ID,所以不是按顺序走的(可能跳),jqgrid for asp.net在前台也没有提供方法(反正我没找到)。每次加载行,把rowid存到控件里就可以了,所以直接用了编辑连接地址的方法 //显示资产信息 function doShowAssetInfo(cellValue, options, rowObject) { var strURL = "./_view.aspx?"; var imageHtml = "<a target='_blank' style='color:Blue; text-decoration:nonde;' href='" + strURL + "'>" + cellValue + "</a>"; document.getElementById("_hidListRows").value += document.getElementById("_hidListRows").value == "" ? rowObject[getColumnindex("Id")] : "," + rowObject[getColumnindex("Id")]; return imageHtml; } 存在了_hidListRows里,以‘,’隔开的。 第三部就是存储ID, function selectRowAll(e) {//接收控件 var grid = jQuery("#<%= Jqgrid1.ClientID %>");//得到jqgrid控件 var ListRows = document.getElementById("_hidListRows").value;//获得所有rowid var rows = [];//集合 var selection = false;//状态 if (e.checked) {//判断状态 selection = true;//更改状态 } rows = ListRows.toString().split(',');//重置集合 for (var r in rows) {//循环 // grid.setSelection(rows[r]);//无用 rowSelected(rows[r], selection);//存储ID的方法 } }需要个集合var selectedRows = []; function rowSelected(rowID, isSelected) {//存储ID的方法 selectedRows[rowID] = isSelected;//号对应状态 updateSelectedRowsHidden();//翻页记录ID } //翻页记录ID function updateSelectedRowsHidden() { var hiddenrowField = $("#<%= SelectedRowsrow.ClientID %>");//得到控件 var selectedrowValues = ""; for (var row in selectedRows) { if (selectedRows[row])//判断状态 selectedrowValues += selectedrowValues == "" ? row : "," + row; } hiddenrowField.val(selectedrowValues);//存到控件SelectedRowsrow里 } 因为rowSelected方法在选择行时用,所以就直接引用了这个方法,现在SelectedRowsrow控件里就是全选的ID了,var selectedRows = [];集合里也有选择的ID,循环时要判断状态。 第四步是触发selectRowAll,这里只能改js包了,jquery.jqGrid.min.js里"<input role='checkbox' id='cb_"+this.p.id+"'...是生成表头的checkbox控件,所以加了个onclick='selectRowAll(this)'就可以触发了。
先看列 <cc1:JQGridColumn Visible="false" Editable="true" DataType="String" Searchable="true" SearchToolBarOperation="IsEqualTo" HeaderText="入库单编号" DataField="inStoreCode"> <Formatter> <cc1:CustomFormatter FormatFunction="doShowInStoreInfo" UnFormatFunction="unformatCode" /> </Formatter> </cc1:JQGridColumn> 这里绑定了入库单号列,编辑的js方法为doShowInStoreInfo //显示入库单信息 function doShowInStoreInfo(cellValue, options, rowObject) { var vPageParam = "inStoreId=" + rowObject[3] + "&inStoreCode=" + cellValue; var strURL = "./_view.aspx?" + vPageParam; var imageHtml = "<a target='_blank' style='color:Blue; text-decoration:nonde;' href='" + strURL + "'>" + cellValue + "</a>"; return imageHtml; } 在js方法中,我要把这个行的第三列(比如是个ID)的值传到指定页面,而且rowObject只接受INT参数,那我要传第30列,第60列,不肯能一个一个数在多少列,要是动态的就更不可能了,所以我要写一个方法返回列名所在列的编号,在前台JS里可以通过rowid获得指定列的值,但是必须是列编辑完才能获到,doShowInStoreInfo是正在进行时所以不能用。我的解决办法是在添加行之前把列和列的编号放在一个参数里,在通过方法取出来,那么在前台我不知道哪个事件是在编辑行之前触发的,而且前台也没有遍历jqgrid列的方法,所以只能从后台入手,在页面Load时就加载,然后存到前台参数,这里用json格式, List<string> Itemlist = new List<string>(); if (_hidListColumns.Value == "") { if (Jqgrid1.Columns.Count != 0) { for (int i = 0; i < Jqgrid1.Columns.Count; i++) { var newObj = new { Item = i, ColumnsName = Jqgrid1.Columns[i].DataField }; string serExpr = JavaScriptConvert.SerializeObject(newObj); Itemlist.Add(serExpr); } var listColumns = new { Itemlist = Itemlist }; _hidListColumns.Value = JavaScriptConvert.SerializeObject(listColumns); } } 套了两层,存在_hidListColumns控件里。 前台接受 function getColumnindex(name) { var ListColumns = document.getElementById("_hidListColumns").value; eval("var ListColumns=" + ListColumns); if (ListColumns!="") { for (var i = 0; i < ListColumns.Itemlist.length; i++) { eval("var Items=" + ListColumns.Itemlist[i]); if (Items.ColumnsName == name) { return Items.Item; } } } } 因为是在load就赋值了,所以前台前台就能获到,编辑方法就可以传列名了 //显示入库单信息 function doShowInStoreInfo(cellValue, options, rowObject) { var vPageParam = "inStoreId=" + rowObject[getColumnindex("id")] + "&inStoreCode=" + cellValue; var strURL = "./_view.aspx?" + vPageParam; var imageHtml = "<a target='_blank' style='color:Blue; text-decoration:nonde;' href='" + strURL + "'>" + cellValue + "</a>"; return imageHtml; }
问题是这个样子的,ASP.NET后台继承框架page,page主要输出了一串JS代码,一个login的div,还有通过登录用户判断样式地址的link标签(登录后数据库取出存session里)。jqgrid for asp.net 要基于documentMode,IE的渲染号,是通过页面第一行的声明<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 得到的,后台page会在Render方法里通过Response.Write输出需要内容,这个事件时jqgrid页面还没有加载,第一句话自然就不是<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 所以jqgrid就获取不到了,就报错了。 因为页面没加载所以page输出位置变不了,也就是只能在第一行,不输出就不知道css的目录,所以目前的解决办法是把输出的东西存session里再由前台获取session输出到页面,JQ获取session 的方法 $(document).ready(function () { var Loginsession = '<%=((Smt.Message.LoginUser)Session["LogUserInfo"]).pSkinPath%>'; var _cssFolder = "css/"; var _cssSuffix = ".css"; var _cssFile = Loginsession.toString().replace("//", "/"); var cssFile = _cssFolder + _cssFile + _cssSuffix; document.all.userlink.href = "../" + cssFile; // alert(document.all.userlink.href); })这里只输出了link地址,其他DIV和js脚本也可这么输出,也可写个js包直接引用,反正就是不让继承的page在页面加载之前输出东西。还没有想到其他办法