目录

【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)

【第二篇】ASP.NET MVC快速入门之数据注解(MVC5+EF6)

【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)

【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)

【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)

FineUIMvc简介

FineUIMvc 是基于 jQuery 的专业 ASP.NET MVC 控件库,其前身是基于 WebForms 的开源控件库 FineUI(历时9年120多个版本)。FineUIMvc(基础版)包含开源版的全部功能,支持 30 种内置主题和 FontAwesome 图标,支持消息对话框和单元格编辑表格,功能强大,最重要的是完全免费

以下引自FineUI官网的介绍:

FineUIMvc(基础版)作为三石奉献给社区的一个礼物,功能绝对强大到让你心动:

1.       拥有 FineUI(开源版)的全部功能。

2.       拥有 FineUI(专业版)相同的 jQuery 前端库,体积小,速度快。

3.       拥有 FineUIMvc(企业版)强大的通知对话框和单元格编辑表格。

4.       内置 30 种 Metro 和 jQueryUI 主题(以及自定义主题Bootstrap Pure)。

5.       内置 500 多个 FontAwesome 图标字体,控件原生支持。

6.       9 年技术积累,1300 多位捐赠会员,100 多家企业客户,稳定可信赖。

7.       重要的事情说三遍:完全免费!完全免费!完全免费!

官网首页:http://fineui.com/mvc/
在线示例:http://fineui.com/demo_mvc/

这篇文章,我们将使用FineUIMvc(基础版)来实现本示例。

FineUIMvc空项目

登录FineUI的论坛,下载FineUIMvc空项目,这样不仅省去了配置的麻烦,而且还有默认的左右框架的实现:

http://fineui.com/bbs/forum.php?mod=viewthread&tid=9101

双击FineUIMvc.EmptyProject.sln,打开项目:

这个目录结构和之前的很类似,多了一个res目录,这个是FineUIMvc中的约定,用来放置静态资源,比如CSS、JS、图片以及一套Icon图标。

Ctrl+F5直接运行项目:

修改Web.config

空项目已经配置好了Web.config文件,主要是两个地方的改动:

<configSections><sectionname="FineUIMvc"type="FineUIMvc.ConfigSection, FineUIMvc"requirePermission="false" /></configSections><FineUIMvcDebugMode="true"Theme="Cupertino" />

这里设置FineUIMvc的全局参数:

l Theme: 样式主题,内置 30 种主题(其中 6 种 Metro 主题,24 种 jQueryUI 官方主题,默认值:Default)

n Metro 主题:Default, Metro_Blue, Metro_Dark_Blue, Metro_Gray, Metro_Green, Metro_Orange

n jQueryUI 主题: Black_Tie, Blitzer, Cupertino, Dark_Hive, Dot_Luv, Eggplant, Excite_Bike, Flick, Hot_Sneaks, Humanity, Le_Frog, Mint_Choc, Overcast, Pepper_Grinder, Redmond, Smoothness, South_Street, Start, Sunny, Swanky_Purse, Trontastic, UI_Darkness, UI_Lightness, Vader

l CustomTheme: 自定义样式主题(custom_default/bootstrap_pure)

l Language: 控件语言(en/zh_CN/zh_TW,默认值:zh_CN)

l FormMessageTarget: 表单字段错误提示信息的显示位置(Title/Side/Qtip,默认值:Side)

l FormLabelWidth: 表单字段标签的宽度(默认值:100px)

l FormLabelAlign: 表单字段标签的位置(Left/Right/Top,默认值:Left)

l FormRedStarPosition: 表单字段红色星号的位置(AfterText/BeforeText/AfterSeparator,默认值:AfterText)

l FormLabelSeparator: 表单字段标签与内容的分隔符(默认值:":")

l EnableAjax: 是否启用AJAX(默认值:true)

l AjaxTimeout: Ajax超时时间(单位:秒,默认值:120s)

l DebugMode: 是否开发模式,启用时格式化页面输出的JavaScript代码,便于调试(默认值:false)

l EnableAjaxLoading: 是否启用Ajax提示(默认值:true)

l AjaxLoadingType: Ajax提示类型,默认在页面顶部显示黄色提示框(Default/Mask,默认值:Default)

l EnableShim: 是否启用遮罩层,防止ActiveX、Flash等对象覆盖弹出窗体(默认值:false)

l EnableCompactMode: 是否启用紧凑模式(默认值:false)

l EnableLargeMode: 是否启用大字体模式(默认值:false)

l MobileAdaption: 是否启用移动浏览器自适应(默认值:false)

l EnableAnimation: 是否启用动画(仅Webkit浏览器支持动画效果)(默认值:false)

l LoadingImageNumber: 页面加载动画图片的序号(从 1 到 30,默认值:1)

更详细的介绍参考FineUIMvc在线示例站点:

http://mvc.fineui.com/#/Config/ModifyWebConfig

另外一处配置HTTP处理器:

<system.web><httpModules><addname="FineUIMvcScriptModule"type="FineUIMvc.ScriptModule, FineUIMvc"/></httpModules><httpHandlers><addverb="GET"path="res.axd"type="FineUIMvc.ResourceHandler, FineUIMvc"/></httpHandlers></system.web>

如果你之前用过FineUI(开源版),相信对这个配置很熟悉。

添加全局模型绑定器

在Global.asax中,添加全部模型绑定器:

protected voidApplication_Start()
{AreaRegistration.RegisterAllAreas();FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);ModelBinders.Binders.Add(typeof(JArray), newJArrayModelBinder());ModelBinders.Binders.Add(typeof(JObject), new JObjectModelBinder());
}

这个设置用于模型绑定,可以将客户端传入的JSON数组自动转化为JArray对象,类似如下代码:

当然这个设置不是必须的,如果去掉这个设置,则上面的代码就需要这样写了:

public ActionResult Grid1_PageIndexChanged(string Grid1_fields, intGrid1_pageIndex)
{JArray fields=JArray.Parse(Grid1_fields);//.....}

布局视图

布局视图类似于WebForms的母版页,位于Views/Home/Shared/_Layout.cshtml,我们先看下其中的代码:

@{var F = Html.F();
}<!DOCTYPE html>
<html>
<head><title>@ViewBag.Title - FineUIMvc 空项目</title>@F.RenderCss()<linkhref="~/res/css/common.css"rel="stylesheet"type="text/css" />@RenderSection("head", false)</head>
<body>@Html.AntiForgeryToken()@F.PageManager@RenderSection("body", true)@F.RenderScript()@RenderSection("script", false)</body>
</html>

整体架构非常简单,其中和FineUIMvc密切相关的代码:

1.     var F = Html.F():实例化FineUIMvc的HTML辅助方法,视图中所有的FineUIMvc控件都要通过F变量来初始化。

2.     F.RenderCss():放置FineUIMvc内置的CSS文件。

3.     F.PageManager:FineUIMvc的页面管理器,每个页面都需要,所以放到布局视图中。

4.     F.RenderScript():放置FineUIMvc内置的JavaScript文件。

除此之外,我们还通过MVC内置的RenderSection函数定义了几个段落,主要用于放置自定义CSS文件,页面主体HTML以及页面自定义JavaScript脚本文件。

Html.AntiForgeryToken()用于防止跨站请求伪造攻击,需要配合控制器中方法的[ValidateAntiForgeryToken]特性使用,本系列的第三篇文章曾做过详细的介绍。

注意:和用VS自动生成的布局视图有一个很大的不同,这里使用RenderSection("body", true)来方式主体HTML,第二个参数true表明这是一个必选项。所以在具体的视图页面,必须存在名为body的节,否则就会报错。

首页视图

首页视图的代码相对有点复杂,从截图上可以看出,整个页面被分为三部分,上半部分放置网址标题,操作按钮等控件,左侧部分是一个树控件,右侧部分是一个选项卡控件,我们先来看这部分的代码:

@(F.RegionPanel().ID("RegionPanel1").ShowBorder(false).IsViewPort(true).Regions(F.Region().ID("Region1").ShowBorder(false).ShowHeader(false).RegionPosition(Position.Top).Layout(LayoutType.Fit).ContentEl("#header"),F.Region().ID("Region2").RegionSplit(true).Width(200).ShowHeader(true).Title("菜单").EnableCollapse(true).Layout(LayoutType.Fit).RegionPosition(Position.Left).Items(F.Tree().ShowBorder(false).ShowHeader(false).ID("treeMenu").Nodes(F.TreeNode().Text("默认分类").Expanded(true).Nodes(F.TreeNode().Text("开始页面").NavigateUrl("~/Home/Hello"),F.TreeNode().Text("登录页面").NavigateUrl("~/Home/Login")))),F.Region().ID("mainRegion").ShowHeader(false).Layout(LayoutType.Fit).RegionPosition(Position.Center).Items(F.TabStrip().ID("mainTabStrip").EnableTabCloseMenu(true).ShowBorder(false).Tabs(F.Tab().ID("Tab1").Title("首页").BodyPadding(10).Layout(LayoutType.Fit).Icon(Icon.House).ContentEl("#maincontent"))))
)

最外层是一个RegionPanel控件,并设置了填充整个浏览器窗口(IsViewPort=true),上半部分的Region控件通过ContentEl属性来指定一个id=header的HTML片段;左侧Region里面放置了一个硬编码的Tree控件,通过指定Region的Layout=Fit来让Tree控件填充整个左侧区域;右侧Region里面放置了TabStrip控件,TabStrip放置了一个初始选项卡Tab,并通过ContentEl来指向一个id=maincontent的HTML片段。

这个分析过程也很容易理解,如果你之前使用过FineUI(开源版),对比应该并不陌生,只不过把之前的ASPX语法换成了Rezor语法而已。

左侧树控件和右侧选项卡控件的交互是由JavaScript代码控制的,这个常见交互FineUIMvc进行了封装:

@section script {<script>//页面控件初始化完毕后,会调用用户自定义的onReady函数F.ready(function() {//初始化主框架中的树和选项卡互动,以及地址栏的更新//treeMenu: 主框架中的树控件实例//mainTabStrip: 选项卡实例//updateHash: 切换Tab时,是否更新地址栏Hash值(默认值:true)//refreshWhenExist: 添加选项卡时,如果选项卡已经存在,是否刷新内部IFrame(默认值:false)//refreshWhenTabChange: 切换选项卡时,是否刷新内部IFrame(默认值:false)//maxTabCount: 最大允许打开的选项卡数量//maxTabMessage: 超过最大允许打开选项卡数量时的提示信息
F.initTreeTabStrip(F.ui.treeMenu, F.ui.mainTabStrip, {maxTabCount:10,maxTabMessage:'请先关闭一些选项卡(最多允许打开 10 个)!'});});</script>
}

对这里出现JavaScript代码的简单描述:

1.     F.ready:在控件初始化完毕后执行,类似于jQuery的$.ready,只不过F.ready是在DomReady之后执行的。

2.     F.initTreeTabStrip:设置树控件和选项卡控件的交互,点击树控件节点新建一个启用IFrame的选项卡控件。里面有很多参数可以控制交互行为,在上面的代码中都有注释,就不一一解释了。

3.     F.ui.treeMenu:引用左侧树控件实例。FineUIMvc会在F.ui命名控件下存储所有页面上初始化的FineUIMvc控件,因此可以方便的通过控件的名称来引用。

主题选择框

这个页面还有一个需要JavaScript脚本的交互过程,那就是页面右上角的[主体仓库]按钮,点击此按钮时会弹出一个启用IFrame的Window控件,默认这个Window控件是隐藏的:

@(F.Window().Hidden(true).EnableResize(true).EnableMaximize(true).EnableClose(true).Height(600).Width(1020).IsModal(true).ClearIFrameAfterClose(false).IFrameUrl(Url.Content("~/Home/Themes")).EnableIFrame(true).Title("主题仓库").ID("windowThemeRoller")
)

按钮的代码放置在id=header的HTML片段中:

@(F.Button().EnableDefaultCorner(false).EnableDefaultState(false).IconFont(IconFont.Bank).IconAlign(IconAlign.Top).Text("主题仓库").ID("btnThemeSelect").CssClass("icontopaction themes").Listener("click", "onThemeSelectClick")
)

通过Button的Listener属性来指定点击按钮时需要执行的JavaScript脚本:

//点击主题仓库
functiononThemeSelectClick(event) {F.ui.windowThemeRoller.show();
}

选择某个主题后的逻辑也很简单,把用户选择的值保存到Cookie中然后刷新页面,这个逻辑不难,请自行查看源代码。

页面刷新后,如何从Cookie中读取值并设置所需的主题呢?这个逻辑其实是在布局视图中完成的,下面来看下完整的布局视图:

@{var F = Html.F();
}<!DOCTYPE html>
<html>
<head><title>@ViewBag.Title - FineUIMvc 空项目</title>@F.RenderCss()<linkhref="~/res/css/common.css"rel="stylesheet"type="text/css" />@RenderSection("head", false)</head>
<body>@Html.AntiForgeryToken()@{var pm = F.PageManager;// 主题HttpCookie themeCookie = Request.Cookies["Theme_Mvc"];if (themeCookie != null){string themeValue = themeCookie.Value;Theme theme;if (Enum.TryParse<Theme>(themeValue, true, out theme)){pm.CustomTheme(String.Empty);pm.Theme(theme);}else{pm.CustomTheme(themeValue);}}}@F.PageManager@RenderSection("body", true)@F.RenderScript()@RenderSection("script", false)</body>
</html>

这个逻辑不难,首先从请求的HTTP参数中读取需要的Cookie值,然后设置PageManager的Theme属性,这里之所以有个Enum.TryParse的逻辑,是因为Cookie中保存的也可能是用户自定义主题,两者的处理不同。

登录页面

1.     访问学生列表页面,会要求用户先登录,如果直接在浏览器地址栏输入:

http://localhost:64475/Students

2.     页面会被重定向到:http://localhost:64475/Login?ReturnUrl=%2fStudents

3.     登录成功后,页面会重定向到首页:

4.     此时点击用户头像的下拉菜单项[安全退出],就会重定向到登录页面。

这一系列过程是通过表单身份验证完整的,先来看下登录页面的视图代码:

@{ViewBag.Title= "Login";var F =@Html.F();
}@section body {@(F.Window().Width(350).WindowPosition(WindowPosition.GoldenSection).EnableClose(false).IsModal(false).Title("登录表单").ID("Window1").Items(F.SimpleForm().ShowHeader(false).LabelWidth(80).BodyPadding(10).ShowBorder(false).ID("SimpleForm1").Items(F.TextBox().ShowRedStar(true).Required(true).Label("用户名").ID("tbxUserName"),F.TextBox().ShowRedStar(true).Required(true).TextMode(TextMode.Password).Label("密码").ID("tbxPassword"))).Toolbars(F.Toolbar().Position(ToolbarPosition.Bottom).ToolbarAlign(ToolbarAlign.Right).ID("Toolbar1").Items(F.Button().OnClick(Url.Action("btnLogin_Click"), "SimpleForm1").ValidateTarget(Target.Top).ValidateForms("SimpleForm1").Type(ButtonType.Submit).Text("登录").ID("btnLogin"),F.Button().Type(ButtonType.Reset).Text("重置").ID("btnReset"))))
}

这个页面是由几个FineUIMvc控件组成的:

1.     Window控件:默认弹出的Window控件位于页面的中央位置。

2.     Toolbar控件:Window控件的底部工具栏。

3.     Button控件:工具栏中的[登录]和[重置]按钮。

4.     TextBox控件:[用户名]和[密码]文本输入框。

点击[登录]按钮,发起一个HTTP POST请求,这个请求对应于控制器Login的btnLogin_Click方法,第二个参数SimpleForm1用于指定用于传入控制器方法的表单参数。

这个过程对于WebForms开发者来说应该很面熟,如果用WebForms的术语我可以这么来说:点击[登录]按钮,回发当前页面,触发后台的btnLogin_Click事件。这里我特地保留了WebForms中后台事件的命名方法,其实换汤不换药,百变不离其宗,了解HTTP协议的大概工作原理,就不难理解。

后台的btnLogin_Click方法:

[HttpPost]
[ValidateAntiForgeryToken]public ActionResult btnLogin_Click(string tbxUserName, stringtbxPassword)
{if (tbxUserName == "admin" && tbxPassword == "admin"){FormsAuthentication.RedirectFromLoginPage(tbxUserName,false);}else{ShowNotify("用户名或密码错误!", MessageBoxIcon.Error);}returnUIHelper.Result();
}

退出操作:

[HttpPost]
[ValidateAntiForgeryToken]publicActionResult onSignOut_Click()
{FormsAuthentication.SignOut();return RedirectToAction("Index", "Login");
}

学生列表页面(CRUD)

FineUIMvc中所有去往服务器的POST请求都是AJAX,所以我们可以毫不费力的制作单页应用程序。配合FineUIMvc内置的IFrame支持,可以将一些逻辑独立成一个页面,不仅有助于代码的解耦,而且页面效果也比较统一。

学生列表首页:

在这个页面中,我们可以进行如下操作:

1.     新增用户:弹出一个启用IFrame的Window控件,在新页面中进行新增用户操作,操作结束后,需要重新绑定表格数据(因为数据已经改变了)。

2.     编辑用户:和新增是类似的逻辑。

3.     删除单个用户:在表格行中集成了删除按钮,删除之前会有确认提示框。

4.     删除多个用户:点击表格工具栏中的[删除选中记录],可以一次删除多条记录,同样删除之前会有确认提示框。

表格页面

我们先来看下表格的视图代码:

@(F.Grid().IsViewPort(true).ShowHeader(false).ShowBorder(false).ID("Grid1").DataIDField("ID").DataTextField("Name").EnableCheckBoxSelect(true).Toolbars(F.Toolbar().Items(F.Button().ID("btnDeleteSelected").Icon(Icon.Delete).Text("删除选中记录").Listener("click", "onDeleteSelectedClick"),F.ToolbarFill(),F.Button().ID("btnCreate").Icon(Icon.Add).Text("新增用户").Listener("click", "onCreateClick"))).Columns(F.RowNumberField(),F.RenderField().HeaderText("姓名").DataField("Name").Width(100),F.RenderField().HeaderText("性别").DataField("Gender").FieldType(FieldType.Int).RendererFunction("renderGender").Width(80),F.RenderField().HeaderText("所学专业").DataField("Major").ExpandUnusedSpace(true),F.RenderField().HeaderText("入学日期").DataField("EntranceDate").FieldType(FieldType.Date).Renderer(Renderer.Date).RendererArgument("yyyy-MM-dd").Width(100),F.RenderField().HeaderText("").Width(60).RendererFunction("renderEditField").TextAlign(TextAlign.Center).EnableHeaderMenu(false),F.RenderField().HeaderText("").Width(60).RendererFunction("renderDeleteField").TextAlign(TextAlign.Center).EnableHeaderMenu(false)).DataSource(Model)
)

这里除了对表格列的定义之外,就是表格顶部工具栏的定义,里面包含图示的两个操作按钮。

表格数据是通过DataSource方法指定的,传入的Model参数类型在页面头部有定义:

@model IEnumerable<FineUIMvc.QuickStart.Models.Student>

行渲染函数RendererFunction

同时注意性别列和表格最后的两个操作列,都定义了RendererFunction方法,它用来指定列的客户端渲染脚本。对于性别列,我们知道其数据类型为int,所以需要通过客户端渲染函数来转换为字符串:

functionrenderGender(value, params) {return value == 1 ? '男' : '女';
}

而两个操作列,则需要返回包含编辑和删除图标的HTML片段:

functionrenderDeleteField(value, params) {return '<a href="javascript:;" class="deletefield">
<img class="f-grid-cell-icon" src="@Url.Content("~/res/icon/delete.png")"></a>';
}functionrenderEditField(value, params) {return '<a href="javascript:;" class="editfield">
<img class="f-grid-cell-icon" src="@Url.Content("~/res/icon/pencil.png")"></a>';
}

行中删除图标的点击事件

下面看下如何在脚本中处理行中的编辑图标和删除图标的点击事件:

F.ready(function() {var grid1 =F.ui.Grid1;grid1.el.on('click', 'a.deletefield', function(event) {var rowEl = $(this).closest('.f-grid-row');var rowData =grid1.getRowData(rowEl);F.confirm({message:'你确定要删除选中的行数据吗?',target:'_top',ok:function() {deleteSelectedRows([rowData.id]);}});});
});

首先通过F.ui.Grid1来获取页面上表格的实例,而F.ui.Grid1.el则是一个标准的jQuery对象,表示此表格在页面上的DOM元素。然后通过jQuery的on函数来注册编辑图标和删除图标的点击事件。

在删除事件中,通过jQuery的closest函数获取编辑图标所在的表格行,然后调用表格的getRowData方法获取行数据,删除时需要知道本行的行ID。然后调用F.confirm弹出确认对话框,在用户点击确认对话框的确认按钮时,执行删除操作(deleteSelectedRows函数)。

之所以将删除逻辑放到deleteSelectedRows中,是因为在批量删除时也需要用到,所以提取为公共方法:

functiondeleteSelectedRows(selectedRows) {//触发后台事件F.doPostBack('@Url.Action("Grid1_Delete")', {'selectedRows': selectedRows,'Grid1_fields': F.ui.Grid1.fields});
}

这里调用了FineUIMvc封装好的AJAX POST方法F.doPostBack(类似于WebForms中的__doPostBack的命名),第一个参数指定了请求的URL,第二个参数指定请求中附加的表单参数。

行中编辑图标的点击事件

由于需要在Window控件中弹出新增用户页面和编辑用户页面,所以我们还需要一个隐藏的Window控件:

@(F.Window().ID("Window1").Width(600).Height(300).IsModal(true).Hidden(true).Target(Target.Top).EnableResize(true).EnableMaximize(true).EnableIFrame(true).IFrameUrl(Url.Content("about:blank")).OnClose(Url.Action("Window1_Close"), "Grid1")
)

注意:我们为Window控制设置Target=Top属性,表明在顶层页面中弹出这个窗体,而不是局限在当前页面内,这个是FineUIMvc内置的特性,并且仅对于启用IFrame的Window窗体有效(EnableIFrame)。

在F.ready函数中注册编辑图标的点击事件:

grid1.el.on('click', 'a.editfield', function(event) {var rowEl = $(this).closest('.f-grid-row');var rowData =grid1.getRowData(rowEl);F.ui.Window1.show('@Url.Content("~/Students/Edit/")?studentId=' + rowData.id, '编辑用户');
});

在编辑事件中,同样先取得当前行数据,然后调用F.ui.Window1.show来在Window控件中显示编辑页面,第二个参数[编辑用户]指定Window控件的标题栏文本。

编辑窗体中的[保存后关闭]按钮逻辑

先来看下Edit视图代码:

@{ViewBag.Title= "Edit";var F =@Html.F();
}@model FineUIMvc.QuickStart.Models.Student@section body {@(F.Panel().ID("Panel1").ShowBorder(false).ShowHeader(false).BodyPadding(10).AutoScroll(true).IsViewPort(true).Toolbars(F.Toolbar().Items(F.Button().Icon(Icon.SystemClose).Text("关闭").Listener("click", "F.activeWindow.hide();"),F.ToolbarSeparator(),F.Button().ValidateForms("SimpleForm1").Icon(Icon.SystemSaveClose).OnClick(Url.Action("btnEdit_Click"), "SimpleForm1").Text("保存后关闭"))).Items(F.SimpleForm().ID("SimpleForm1").ShowBorder(false).ShowHeader(false).Items(F.HiddenFieldFor(m=>m.ID),F.TextBoxFor(m=>m.Name),F.RadioButtonListFor(m=>m.Gender).Items(F.RadioItem().Text("").Value("1"),F.RadioItem().Text("").Value("0")),F.TextBoxFor(m=>m.Major),F.DatePickerFor(m=>m.EntranceDate).EnableEdit(false))))
}

让我们把关注点转移到两个操作按钮:

1.     [关闭]按钮:Listener("click", "F.activeWindow.hide();"),通过这种方式注册一段JavaScript脚本,用来关闭当前IFrame所在的活动窗体。

2.     [保存后关闭]按钮:这个逻辑要在服务器端处理,因为我们需要先在服务器端保存数据到数据库,然后才能关闭当前活动窗体。

[HttpPost]
[ValidateAntiForgeryToken]public ActionResult btnEdit_Click([Bind(Include = "ID,Name,Gender,Major,EntranceDate")] Student student)
{if(ModelState.IsValid){db.Entry(student).State=EntityState.Modified;db.SaveChanges();//关闭本窗体(触发窗体的关闭事件)
PageContext.RegisterStartupScript(ActiveWindow.GetHidePostBackReference());}returnUIHelper.Result();
}

关闭活动窗体的逻辑是通过PageContext.RegisterStartupScript函数来注册一段JavaScript脚本来完成的,如果你之前使用过FineUI(开源版),详细对此函数一定非常熟悉:

1.     ActiveWindow代表的是当前活动的Window控件,而当前脚本是在Window控件的IFrame页面内执行的。

2.     GetHidePostBackReference用于获取一段脚本,首先关闭当前活动的Window控件,然后触发Window控件的Close事件。这里的命名和开源版中的命名一模一样,相信你不会陌生。

删除多行记录

表格工具栏中有一个[删除选中记录]按钮,用户可以通过Ctrl或者Shift选择多行,然后点击删除:

这个弹出确认框的逻辑是在客户端完成的,如果默认没有选中值,则会弹出另一个提示框:

首先在视图中通过Listener属性来注册按钮的客户端脚本处理函数:

F.Button().ID("btnDeleteSelected").Icon(Icon.Delete).Text("删除选中记录").Listener("click", "onDeleteSelectedClick")

下面看下这个脚本处理函数:

functiononDeleteSelectedClick(event) {var grid1 =F.ui.Grid1;if (!grid1.hasSelection()) {F.alert('请至少选择一项!');return;}var selectedRows =grid1.getSelectedRows();F.confirm({message:'你确定要删除选中的&nbsp;<b>' + selectedRows.length + '</b>&nbsp;行数据吗?',target:'_top',ok:function() {deleteSelectedRows(selectedRows);}});
}

这里有对FineUIMvc的客户端API接口的调用:

1.     hasSelection:返回表格是否有选中行。

2.     F.alert:用来弹出一个提示框。

3.     getSelectedRows:返回表格选中的行数组,数组元素是行的ID(在表格定义中通过DataIDField标识),这个函数还有个重载,如果传入true参数,则返回的表格元素为行数据。

4.     F.confirm:用来弹出一个确认框,target用来指定的弹出的位置,由于当前页面位于iframe中,所以我们希望在顶层窗口中弹出确认框。

表单检索与数据库分页

限于篇幅原因,表单检索和数据库分页就不讲解了,请自行查看源代码。

小结

本示例使用FineUIMvc(基础版)来实现相同的示例功能,但是页面效果更加专业,功能更加完善,所有到服务器的POST请求都是AJAX,而IFrame的内置支持让整个页面交互轻松自然,不用跳来跳去,同时又能保持业务逻辑代码的独立,方便维护更新。

下载示例源代码

转载于:https://www.cnblogs.com/sanshi/p/6223164.html

【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)相关推荐

  1. .net mvc html访问数据库,【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6) - 三生石上...

    图挂了的说明: 我是在Word中写的本系列文章,然后转成的HTML,所以图片都存在于 fineui.com 服务器,由于临时访问量太多,服务器喘不过气了.... 你可以稍后访问,实在是抱歉...... ...

  2. apache2.4.9 开启path_info访问_【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)...

    新建项目 打开VS2015,找到菜单项[文件->新建->项目],打开向导对话框: 注意我们的选择项: 运行平台:.NET FrameWork 4.5 项目模板:ASP.NET Web Ap ...

  3. cocos2d-x学习笔记番外篇05:如何快速屏蔽触摸

    cocos2d-x有个问题,即使暂停CCScene运行,触摸仍然有效,有些菜单和按钮仍然会被触发. 所以有的时候我们要手动屏蔽触摸,尤其是在弹出计费画面的,或者使用一些本平台自带控件的时候. 做法有几 ...

  4. 《SilverLight2快速入门》之基本控件DataGrid

    基本控件的CheckBox,RadioButton等常见控件的使用这里不再一一举例了. 今天介绍DataGrid,也顺便说点数据绑定的内容.与此雷同的可以有ListBox,ComboBox等. 本试验 ...

  5. EmWin学习课堂_小白EmWin_EmWin快速入门_EmWin用Button控件显示文本

    说到EmWin显示文本,大概就是设置字体大小,设置背景颜色,清屏(背景颜色更新),设置文本颜色,设置文字显示格式,再就是显示了: GUI_SetFont(&GUI_Font8x16); GUI ...

  6. 教你从0到1搭建秒杀系统-Canal快速入门(番外篇)

    Canal用途很广,并且上手非常简单,小伙伴们在平时完成公司的需求时,很有可能会用到.本篇介绍一下数据库中间件Canal的使用. 很多时候为了缩短调用延时,我们会对部分接口数据加入了缓存.一旦这些数据 ...

  7. 给深度学习入门者的Python快速教程 - 番外篇之Python-OpenCV

    转载自:https://zhuanlan.zhihu.com/p/24425116 本篇是前面两篇教程:给深度学习入门者的Python快速教程 - 基础篇 给深度学习入门者的Python快速教程 - ...

  8. 文本分类入门(番外篇)特征选择与特征权重计算的区别

    文本分类入门(番外篇)特征选择与特征权重计算的区别 在文本分类的过程中,特征(也可以简单的理解为"词")从人类能够理解的形式转换为计算机能够理解的形式时,实际上经过了两步骤的量化- ...

  9. 设计牛人——设计入门答疑番外篇有感

    看了老牛(牛MO王)的U1番外篇问答感慨非常多,事实上自己也算不上入门非常久的大师,只是有时间去整理整理自己在网页设计方面的经验分享给大家是一件非常好的事情,尤其是对一些想做设计或者想在设计方面转行的 ...

最新文章

  1. mysql如果存在则删除数据库_怎么判断sql数据库是否存在,存在删除
  2. 计算机删除默认共享怎样操作,如何清除计算机默认共享隐患
  3. vue3.0 AntDesignVue2.0 table的rowkey报错问题解决方法
  4. 吴忠军 - 如何理解马云所说的月入两三万,三四万的人最幸福?
  5. 网站服务器是租还是买,建站服务器是买还是租?编辑教你聪明选
  6. thrift (转)
  7. ubunt 文件permission denied问题的解决
  8. 2058. 笨拙的手指
  9. unity对敏感词库处理的记录
  10. vs2013连接数据库
  11. ActiveMQ专题2 —— ActiveMQ下载和安装(Linux版)
  12. 数字图像处理 DCT变换
  13. Python语言的适用范围
  14. 中国移动光猫H2-3拨号模式和bridge(桥)模式
  15. SQLServer 查询分析器里大小写转换快捷键
  16. Android Studio Build Output控制台输出乱码解决
  17. 浅析企业网络准入控制系统的部署方式
  18. 用java定义三维空间的点
  19. C语言之结构体及位段
  20. Arduino Software (IDE) 开发环境配置

热门文章

  1. 数据集如何影响作物病害识别的有效性
  2. Android10 root,Android Q系统Magisk完美实现ROOT
  3. 语言自制教具_学习笔记:蒙特梭利教师必备硬核技能“蒙氏理论+教具制作”...
  4. python 函数图解_Python函数说明(一)
  5. 真实教育场景手写/表格/公式OCR数据集
  6. 字符级中文文本分类-CNN基于TensorFlow实现
  7. java记事本打开功能_Java简易实现记事本的打开与保存
  8. 高温保护_耐高温保护膜可以用在哪一方面?
  9. 3. 机器学习中为什么需要梯度下降?梯度下降算法缺点?_一起学习西瓜书2
  10. centos8安装mysql5.5_CentOS 6.8 编译安装MySQL5.5.32 (二 多实例)