【番外篇】ASP.NET MVC快速入門之免費jQuery控件庫(MVC5+EF6)
目錄
【第一篇】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(基礎版)作為三石奉獻給社區的一個禮物,功能絕對強大到讓你心動:
- 擁有 FineUI(開源版)的全部功能。
- 擁有 FineUI(專業版)相同的 jQuery 前端庫,體積小,速度快。
- 擁有 FineUIMvc(企業版)強大的通知對話框和單元格編輯表格。
- 內置 30 種 Metro 和 jQueryUI 主題(以及自定義主題Bootstrap Pure)。
- 內置 500 多個 FontAwesome 圖標字體,控件原生支持。
- 9 年技術積累,1300 多位捐贈會員,100 多家企業客戶,穩定可信賴。
- 重要的事情說三遍:完全免費!完全免費!完全免費!
官網首頁: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>
<section name="FineUIMvc" type="FineUIMvc.ConfigSection, FineUIMvc"
requirePermission="false" />
</configSections>
<FineUIMvc DebugMode="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>
<add name="FineUIMvcScriptModule" type="FineUIMvc.ScriptModule, FineUIMvc"/>
</httpModules>
<httpHandlers>
<add verb="GET" path="res.axd" type="FineUIMvc.ResourceHandler, FineUIMvc"/>
</httpHandlers>
</system.web>
如果你之前用過FineUI(開源版),相信對這個配置很熟悉。
添加全局模型綁定器
在Global.asax中,添加全部模型綁定器:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(JArray), new JArrayModelBinder());
ModelBinders.Binders.Add(typeof(JObject), new JObjectModelBinder());
}
這個設置用於模型綁定,可以將客戶端傳入的JSON數組自動轉化為JArray對象,類似如下代碼:
當然這個設置不是必須的,如果去掉這個設置,則上面的代碼就需要這樣寫了:
public ActionResult Grid1_PageIndexChanged(string Grid1_fields, int Grid1_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()
<link href="~/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腳本:
// 點擊主題倉庫
function onThemeSelectClick(event) {
F.ui.windowThemeRoller.show();
}
選擇某個主題後的邏輯也很簡單,把用戶選擇的值保存到Cookie中然後刷新頁面,這個邏輯不難,請自行查看源代碼。
頁面刷新後,如何從Cookie中讀取值並設置所需的主題呢?這個邏輯其實是在布局視圖中完成的,下面來看下完整的布局視圖:
@{
var F = Html.F();
}
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title - FineUIMvc 空項目</title>
@F.RenderCss()
<link href="~/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, string tbxPassword)
{
if (tbxUserName == "admin" && tbxPassword == "admin")
{
FormsAuthentication.RedirectFromLoginPage(tbxUserName, false);
}
else
{
ShowNotify("用戶名或密碼錯誤!", MessageBoxIcon.Error);
}
return UIHelper.Result();
}
退出操作:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult 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,所以需要通過客戶端渲染函數來轉換為字符串:
function renderGender(value, params) {
return value == 1 ? ‘男‘ : ‘女‘;
}
而兩個操作列,則需要返回包含編輯和刪除圖標的HTML片段:
function renderDeleteField(value, params) {
return ‘<a href="javascript:;" class="deletefield">
<img class="f-grid-cell-icon" src="@Url.Content("~/res/icon/delete.png")"></a>‘;
}
function renderEditField(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中,是因為在批量刪除時也需要用到,所以提取為公共方法:
function deleteSelectedRows(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());
}
return UIHelper.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")
下面看下這個腳本處理函數:
function onDeleteSelectedClick(event) {
var grid1 = F.ui.Grid1;
if (!grid1.hasSelection()) {
F.alert(‘請至少選擇一項!‘);
return;
}
var selectedRows = grid1.getSelectedRows();
F.confirm({
message: ‘你確定要刪除選中的 <b>‘ + selectedRows.length + ‘</b> 行數據嗎?‘,
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的內置支持讓整個頁面交互輕松自然,不用跳來跳去,同時又能保持業務邏輯代碼的獨立,方便維護更新。
下載示例源代碼
文章出處:http://www.cnblogs.com/sanshi/p/6223164.html
【番外篇】ASP.NET MVC快速入門之免費jQuery控件庫(MVC5+EF6)