在ASP.NET MVC 4中使用Kendo UI Grid

之前寫過用ASP.NET WebForm作為AJAX式資料源的Kendo UI Grid範例,最近計劃在一個小專案試用ASP.NET MVC 4 RC,面對的第一個需求又是"以清單呈現查詢結果",就來看看如何用ASP.NET MVC 4 RC滿足Kendo UI Grid的需求吧!

記得前一次用ashx寫資料端,花了不少功夫處理分頁及排序,而且還沒實做Filter過濾功能。但在ASP.NET MVC上,要整合Kendo UI Grid,則有很酷的方便選擇 -- KendoGridBinder!!

以下是我實做Kendo UI Grid + ASP.NET MVC 4的過程:

  1. 建立一個ASP.NET MVC 4專案
  2. 使用NuGet安裝KendoUIWeb及KendoGridBinder
  3. 借用上回的SimMemberInfo Model類別 ,放在Model目錄下:
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Reflection;
    using System.Web;
     
    namespace KendoGridMvc.Models
    {
        //模擬資料物件
        public class SimMemberInfo
        {
            public string UserNo; //會員編號
            public string UserName; //會員名稱
            public DateTime RegDate; //註冊日期
            public int Points; //累積點數
     
            //模疑資料來源
            public static List<SimMemberInfo> SimuDataStore = null;
     
            static SimMemberInfo()
            {
                Random rnd = new Random();
                //借用具名顏色名稱來產生隨機資料
                string[] colorNames = typeof(Color)
                    .GetProperties(BindingFlags.Static | BindingFlags.Public)
                    .Select(o => o.Name).ToArray();
                SimuDataStore =
                    colorNames
                    .Select(cn => new SimMemberInfo()
                    {
                        UserNo = string.Format("C{0:00000}", rnd.Next(99999)),
                        UserName = cn,
                        RegDate = DateTime.Today.AddDays(-rnd.Next(1000)),
                        Points = rnd.Next(9999)
                    }).ToList();
            }
        }
    }

  4. 要引用Kendo UI,需要載入必要的JS及CSS,此時昨天介紹過的ASP.NET MVC打包壓縮功能馬上派上用場! 編輯App_Start/BundleConfig.cs,加入以下程式:

                bundles.Add(new ScriptBundle("~/bundles/kendoUI").Include(
                    "~/Scripts/kendo/2012.1.322/kendo.web.min.js"
                    ));
    //經實測,SytleBundle virtualPath參數使用"2012.1.322"會有問題,故向上搬移一層
    //將/Content/kendo/2012.1.322的內容搬至Content/kendo下
                bundles.Add(new StyleBundle("~/Content/kendo/css").Include(
                    "~/Content/kendo/kendo.common.min.css",
                    "~/Content/kendo/kendo.blueopal.min.css"
                    ));

    PS: 此處有一個眉角:由於CSS檔路徑會被當成引用圖檔的基準,原本Kendo UI的.css及圖檔被放在~/Content/kendo/2012.1.322/下,理論上StyleBundle應設成"~/Content/kendo/2012.1.322/css”,才能引導瀏覽器到該目錄下取用圖檔。不幸地,我發現StyleBundle的virtualPath參數出現2012.1.322時,會導致Styles.Render("~/Content/kendo/2012.1.322/css”)時傳回HTTP 404錯誤~ 為克服問題,我決定將2012.1.322目錄的內容向上搬一層,直接放在~/Content/keno目錄下,並將virtualPath設成"~/Content/kendo/css",這樣就能避開問題。
    [2014-10-13更新]關於此問題的深入探討

  5. 為了省去每個View都要加掛Kendo UI JS及CSS的麻煩,我索性將它們加在~/Views/Shared/_Layout.cshtml中:
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>@ViewBag.Title</title>
        @Styles.Render("~/Content/themes/base/css", "~/Content/css",
                       "~/Content/kendo/css")
        @Scripts.Render("~/bundles/modernizr")
        @Scripts.Render("~/bundles/jquery", "~/bundles/kendoUI")
        @RenderSection("scripts", required: false)
    </head>
    <body>
        @RenderBody()
    </body>
    </html>

  6. 網頁Index.cshtml的Client端做法,則跟上回WebForm AJAX範例幾乎完全相同:
    @section Scripts
    {
        <style>
            body { font-size: 9pt; }
            #dvGrid { width: 500px; }
            span.hi-lite { color: red; }
            #dvGrid th.k-header { text-align: center; }
        </style>
        <script>
            $(function () {
                //建立資料來源物件
                var dataSrc = new kendo.data.DataSource({
                    transport: {
                        read: {
                            //以下其實就是$.ajax的參數
                            type: "POST",
                            url: "/Home/Grid",
                            dataType: "json",
                            data: {
                                //額外傳至後方的參數
                                keywd: function () {
                                    return $("#tKeyword").val();
                                }
                            }
                        }
                    },
                    schema: {
                        //取出資料陣列
                        data: function (d) { return d.data; },
                        //取出資料總筆數(計算頁數用)
                        total: function (d) { return d.total; }
                    },
                    pageSize: 10,
                    serverPaging: true,
                    serverSorting: true
                });
                //JSON日期轉換
                var dateRegExp = /^\/Date\((.*?)\)\/$/;
                window.toDate = function (value) {
                    var date = dateRegExp.exec(value);
                    return new Date(parseInt(date[1]));
                }
                $("#dvGrid").kendoGrid({
                    dataSource: dataSrc,
                    columns: [
                        { field: "UserNo", title: "會員編號" },
                        { field: "UserName", title: "會員名稱",
                            template: '#= "<span class=\\"u-name\\">" + UserName + "</span>" #'
                        },
                        { field: "RegDate", title: "加入日期",
                            template: '#= kendo.toString(toDate(RegDate), "yyyy/MM/dd")#'
                        },
                        { field: "Points", title: "累積點數" },
                    ],
                    sortable: true,
                    pageable: true,
                    dataBound: function () {
                        //AJAX資料Bind完成後觸發
                        var kw = $("#tKeyword").val();
                        //若有設關鍵字,做Highlight處理
                        if (kw.length > 0) {
                            var re = new RegExp(kw, "g");
                            $(".u-name").each(function () {
                                var $td = $(this);
                                $td.html($td.text()
                               .replace(re, "<span class='hi-lite'>$&</span>"));
                            });
                        }
                    }
                });
                //按下查詢鈕
                $("#bQuery").click(function () {
                    //要求資料來源重新讀取(並指定切至第一頁)
                    dataSrc.read({ page: 1, skip: 0 });
                    //Grid重新顯示資料
                    $("#dvGrid").data("kendoGrid").refresh();
                });
            });
        </script>
    }
    <div style="padding: 10px;">
        關鍵字:
        <input id="tKeyword" /><input type="button" value="查詢" id="bQuery" />
    </div>
    <div id="dvGrid">
    </div>

  7. 最後來到重頭戲,負責以AJAX方式傳回資料的HomeController.cs的Grid() Action:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using KendoGridBinder;
    using KendoGridMvc.Models;
     
    namespace KendoGridMvc.Controllers
    {
        public class HomeController : Controller
        {
            //
            // GET: /Home/
     
            public ActionResult Index()
            {
                return View();
            }
     
            public JsonResult Grid(KendoGridRequest request, string keywd)
            {
                var result = SimMemberInfo.SimuDataStore.Where(o =>
                string.IsNullOrEmpty(keywd) || o.UserName.Contains(keywd));
                return Json(new KendoGrid<SimMemberInfo>(request, result));
            }
        }
    }
    什麼? 沒看錯吧? 這樣就好?
    是的,感謝Ryan Whitmire及Jose Ball的佛心與巧思,只要return Json(new KendoGrid<T>(KendoGridRequest, IEnumerable<T>)),餘下的換頁、排序,甚至欄位過濾功能,就都交給KendoGridBinder全權處理囉!

實際測試,換頁、排序功能一切正常,有了Kendo UI助陣,ASP.NET MVC之路順利多了~

歡迎推文分享:
Published 06 June 2012 09:43 PM 由 Jeffrey
Filed under: ,
Views: 73,734



意見

# linsf said on 13 August, 2012 11:51 PM

請問「2.使用NuGet安裝KendoUIWeb及KendoGridBinder」這畫面要從那裡叫出來,謝謝。

# Jeffrey said on 14 August, 2012 05:49 AM

to linsf, 在Solution Explorer的指定專案按右鍵,選單中會有個"Manage NuGet Packages"(前題是VS要先安裝NuGet Package Manager擴充套件),參考: docs.nuget.org/.../Managing-NuGet-Packages-Using-The-Dialog

# Aeson said on 21 November, 2012 05:13 AM

我遇到一个很严重的问题,我照着您的步骤做,在BundleConfig.cs那边加入那些代码,可是遇到一些问题,具体的问题在这里

www.kendoui.com/.../vs2012-rtm-mvc4-kendo-bundle-not-rendering.aspx,所以我照着方法做在BundleConfig.cs那里加入了

// Clear all items from the default ignore list to allow minified CSS and JavaScript files to be included in debug mode

bundles.IgnoreList.Clear();

// Add back the default ignore list rules sans the ones which affect minified files and debug mode

bundles.IgnoreList.Ignore("*.intellisense.js");

bundles.IgnoreList.Ignore("*-vsdoc.js");

bundles.IgnoreList.Ignore("*.debug.js", OptimizationMode.WhenEnabled);

,问题是解决了可是马上又遇到了另一个一个问题,我从chrome的develop tools里看到以下这些error,

Uncaught TypeError: Cannot call method 'ready' of undefined jquery:1

i.fn.i.init jquery:1

u jquery:1

n jquery:1

i.sub jquery:1

(anonymous function) kendojs:1

(anonymous function) kendojs:1

找了很久都解决不到,请问有人有遇过这样的问题吗?请问有人能帮我吗?

# AndyYou said on 22 February, 2013 06:14 AM

想請問 為何直接用 DbContext 子類別實體回傳 .ToList() 資料都會失敗。

例如:  

public JsonResult Grid(KendoGridRequest request)

{

  List<Person> result = db.People.ToList();

  var grid = new KendoGrid<Person>(request, result);

  return Json(grid);

}

但是自己 new 一個全新的物件沒問題

# Jeffrey said on 23 February, 2013 01:56 AM

to AndyYou, 有失敗的詳細訊息嗎?

# AndyYou said on 24 February, 2013 08:58 PM

這邊是我測試的範例:

Controller : http://goo.gl/yvvoK

Action : Grid  裡面有三種測試 只有 ToList()有錯 而錯誤訊息是在js

cl.ly/.../090y0Y2h2q0p

View 中的 JS 我有參考原作在比對一下

http://goo.gl/WgpPg

Model 是這個 http://goo.gl/IF09N

下圖則是我用監看式比對 應該只有差一點點吧

cl.ly/.../2S0b3Y1Q3P27

# Jeffrey said on 24 February, 2013 10:07 PM

to AndyYou, 由 cl.ly/.../090y0Y2h2q0p 判斷,最方下的/Daily/Grid傳回了一個HTTP 500,代表Server端程式發生Exception,訊息文字內容有16K,其中應該有進一步的線索,可以看一下當時傳回的錯誤訊息找方向。

# lin said on 02 September, 2013 09:39 AM

照你步驟做,出現錯誤,有無完整程式碼可下載 ?

0x800a1391 - JavaScript 執行階段錯誤: 'kendo' 未經定義

# Jeffrey said on 03 September, 2013 08:51 AM

to lin, kendo未經定義應是kendo.web.min.js未正確載入所致,建議用IE Dev Tool檢查一下。我猜一個可能原因是目前NuGet下載到的KendoUIWeb已更新至2013.2.716版,故BundleConfig.cs程式的~/Scripts/kendo/2012.1.322/kendo.web.min.js也要調整。

# Shen said on 03 December, 2013 02:33 AM

Hi,黑大,感謝您的這篇文章,我跑一個table來接是正常的,但遇到有複雜關聯的table就會出錯,不知道您有沒有遇到過EF循環參考的問題?我試過將JsonResult改為JSON.NET的回傳方式(可忽略循環參考),但JSON.NET似乎沒辦法解析KendoGridBinder產生的Data

# Jeffrey said on 03 December, 2013 03:59 AM

to Shen, 關於"JSON.NET似乎沒辦法解析KendoGridBinder產生的Data",可否再多提供一些錯誤細節?

# Shen said on 03 December, 2013 04:59 AM

hi,黑大,謝謝您的回覆,後來已經解決了,一樣是使用JSON.NET自訂Result即可解決循環參考的問題(我想是下午鬼打牆了...)

謝謝您 ^^

# Tim said on 03 December, 2013 11:42 PM

Hi,黑暗大前輩,我正在使用KendoUI的Pop Windows Create功能,後端是用ActionResult接,我想問的是ActionResult能直接轉換class,而不用類似Request["id"]去取得資料嗎??

js:

create: {

type: "POST",

url: "/Service/CreateStaff",

dataType: "json",

}

Controller

[AcceptVerbs(HttpVerbs.Post)]

public ActionResult CreateAdditional(Staff _staff)

{

//_staff都是Null,改用string或int等欄位接收就能接得到...

return Content("");

}

# Jeffrey said on 04 December, 2013 08:19 AM

to Tim, 沒有很懂你的意思。ActionResult是Controller Action方法回傳的型別,不是用來接收前端送入的資料,應該不會跟Request["id"]放在一起比較。

# Tim said on 04 December, 2013 08:32 PM

不好意思描述不太清楚@@

我想要的功能是類似官網的Gird pop window - add new record

demos.kendoui.com/.../editing-popup.html

照他的Sample Code在前端設定

create: {

type: "POST",

url: "/Service/CreateStaff", //Controller接收Ajax的Function

dataType: "json",

}

而後端其實就是接收Ajax post

[AcceptVerbs(HttpVerbs.Post)]

public ActionResult CreateStaff(Staff _staff)

{

//_staff class都是Null

return Content("");

}

改用欄位指定就可以,但欄位太多挺麻煩的

[AcceptVerbs(HttpVerbs.Post)]

public ActionResult CreateStaff(string StaffName)

{

return Content("");

}

我想讓後端可以接收自訂的class,應該是要在前端將新增的資料(object)做JSON.stringify,但我找不到KendoUI在哪下手

不知道黑大用KendoUI做Ajax Post新增時,都是用什麼方式?謝謝

# Jeffrey said on 05 December, 2013 09:13 AM

to Tim, 我在工作專案上通常會使用編輯UI透過自訂模組處理CRUD作業,倒沒用過KendoUI內建的編輯更新功能。由你的描述,應是KendoUI送出的資料格式無法靠Model Binder自動轉成MVC Action的輸入參數,我想透過一點客製技巧就可克服,這個議題挺有趣,容我找時間研究再寫範例。

# Jeffrey said on 07 December, 2013 11:37 PM

to Tim, 依我實測,Gird Pop Window會將編輯物件的所有屬性以POST Form形式傳至後端,理論上ASP.NET MVC可自動將其轉成你所定義成Staff _staff參數物件,但有個前題: Staff的StaffName要定義成屬性(有get; set;那種),我範例中的SimMemberInfo只寫成public string UserName;是不可行的,要改成public string UserName { get; set; },你看看是否問題出在這裡?

# Ricky said on 14 September, 2014 07:48 AM

Dear 黑暗大 , 可以請教您兩個問題嗎 , 謝謝

不知道 KendoGridBinder這個plugin,能不能搭配Web API用呢?

我嘗試過將後端改成以下code...好像jQuery都無法呼叫到...

transport: {

                   read: {

                       data: { keyword: $('#txtFilter').val() },

                       type: "POST",

                       url: "/Customer/GetCustomers",

                       //url: "/api/Customer",

                       dataType: "json"

                   }

               },

public HttpResponseMessage Get(KendoGridRequest request, string keyword)

       {

           var result = CustomerService.GetCustomers(keyword);

           var jsonData = new KendoGrid<CustomerViewModel>(request, result);

           return Request.CreateResponse(HttpStatusCode.OK, jsonData);

       }

另外就是KendoUI open source好像要停止支援了...不知道黑暗大有什麼替代的Grid Freamwork嗎

謝謝您 ^^

# Jeffrey said on 14 September, 2014 08:47 AM

to Ricky,

基於WebAPI與MVC Action的行為差異,我想沒法直接在WebAPI裡用KendoGridBinder,但Telerik有篇Blog介紹過KendoGrid與WebAPI的整合方式: blogs.telerik.com/.../the_facts_on_using_kendo_ui_with_asp_net_webapi

公司之前就已買了Telerik元件授權,主要是經過評估,確定「花錢買元件避免大量腦細胞死亡」很划算! XD

我在工作上對Grid的依賴度極高,Telerik Grid元件提供的彈性與擴充性,能相抗衡的幾乎都是付費元件,以前KendoGrid屬於開源算是賺到,現在只能算回歸正常,仔細算算還是很值得,也不太會想花時間去尋找替代的免費方案。以上是我的經驗,與你分享。

# Ricky said on 14 September, 2014 09:43 AM

謝謝您耐心地回答 :)

# Justin said on 02 December, 2014 02:06 AM

想請問黑大前輩,

關於樓上Tim的問題,我也是感到困惑,

而且我是兩種方法都抓不到資料,

我資料來源於資料庫,目前要實作更新部分,

卡在KendoUI使用修改按鈕按下儲存的時候不知該如何下手,

後端是如何取得前端送來的資料呢?

根據 demos.telerik.com/.../editing-popupdemos.telerik.com/.../editing-popup 的方法嘗試失敗..

# Jeffrey said on 03 December, 2014 07:20 PM

to Justin, 嘗試失敗是指依照 demos.telerik.com/.../editing-popup 提供的GridController.EditingPopup_Update() 範例測試不成功嗎?

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<June 2012>
SunMonTueWedThuFriSat
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication