65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 SharpKit 构建客户端网格控件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (12投票s)

2012年1月6日

LGPL3

4分钟阅读

viewsIcon

39566

downloadIcon

644

使用 SharpKit 在 C# 中构建客户端网格控件的示例代码

SharpKitGridSample/Grid.png

引言

此示例代码将向您展示如何使用 SharpKit 编写客户端网格控件。SharpKit 是一种工具,允许您编写 C# 代码并在编译期间将其转换为 JavaScript。此过程可以帮助您更快地编写代码,并减少错误,它还可以帮助您记录代码,以便其他开发人员可以更轻松地使用它。因此,为避免混淆,此处的所有代码都将以 C# 形式显示,但实际上,它是标准 JavaScript 代码,可以在有或没有 SharpKit 的情况下使用。

我们将从学习如何实现 Grid 开始,然后继续使用 Grid,最后,学习如何优化 DOM 操作以支持旧版浏览器。

实现网格

网格类

[JsType(JsMode.Prototype, Filename = "Grid.js")]
public class Grid : HtmlContext
{
    public Grid()
    {
            Rows = new JsArray<GridRow>();
    }
    public HtmlElement Element { get; set; }
    public HtmlElement GridBody { get; set; }
    public JsArray<GridRow> Rows { get; set; }
    public void Render()
    {
        if (Element == null)
            return;
        Element["_Grid"] = this;
        if (GridBody == null || GridBody.nodeName != "TBODY")
        {
            GridBody = document.createElement("TBODY");
            Element.appendChild(GridBody);
        }
    }
}

这个 Grid 类旨在以下列模式使用

var grid = new Grid { Element = document.getElementById("MyGrid") };
grid.Render();

GridRow 类

grid 类有一个 Rows 集合,其中每一行都是 GridRow 类型。GridRow 是一个 json 类,这意味着它仅用于包含有关该行的数据,在我们的例子中,我们将包含对表格行元素(TR 元素)的引用,以及一个将该行与某些数据对象关联的 Data 属性。

[JsType(JsMode.Json)]
public class GridRow
{
    public HtmlElement Element { get; set; }
    public object Data { get; set; }
}

GridRow 类旨在以下列模式使用

var row = new GridRow 
          { Element = grid.CreateRow(document.getElementById("MyGridRowTemplate")) };
grid.AddRow(row);

添加一行

现在是时候实现一个 AddRow 方法了,它将向我们的 Rows 集合添加一个 GridRow,并将其附加到 DOM 中。

public void AddRow(GridRow gr)
{
    var body = GridBody;
    body.appendChild(gr.Element);
    gr.Element["_GridRow"] = gr;
    Rows.push(gr);
}

从模板创建行元素

public HtmlElement CreateRowElement(HtmlElement template)
{
    return template.cloneNode(true);
}

此方法仅克隆一个元素,在我们的例子中,这将是 TR 元素,用于在 grid 中为每一行克隆。

使用网格

现在,我们已准备好使用 grid

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Grid Demo - SharpKitSamples</title>
    <link href="Grid.css" rel="stylesheet" type="text/css" />
    <script src="res/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Grid.js" type="text/javascript"></script>
    <script src="GridDemo.js"></script>
    <script>$(Load);</script>
</head>
<body>
<h1>Grid Demo</h1>
    <table id="MyGrid" class="Grid">
        <thead>
            <tr>
                <th>Name</th>
                <th>Age</th>
                <th>Phone Number</th>
                <th>Description</th>
            </tr>
        </thead>
        <tbody style="display: none">
            <tr id="MyGridRowTemplate">
                <td class="CellName"></td>
                <td class="CellAge"></td>
                <td class="CellPhoneNumber"></td>
                <td class="CellDescription"></td>
           </tr>
        </tbody>
    </table>
</body>
</html>

此 HTML 文件包含一个用于 gridTABLE 元素,其中包含一些标题和一个行模板。我们可以克隆此行以在 grid 中创建新行。您还可以注意到 $(Load) 代码,它实际上是 jQuery ready 事件,这意味着当 DOM 就绪时,将调用 Load() 函数。

[JsType(JsMode.Global, Filename = "GridDemo.js")]
public class GridDemoClient : jQueryContextBase
{
    public static void Load()
    {
        var list = new JsArray<Contact>();

        for (var i = 0; i < 30; i++)
        {
            var c = new Contact { Name = "MyContact" + i, Age = i, 
                    PhoneNumber = "44557799" + i, Description="This is a contact "+i };
            list.push(c);
        }
        var grid = new Grid { Element = document.getElementById("MyGrid") };
        grid.Render();
        foreach (var c in list)
        {
            var row = new GridRow { Element = grid.CreateRow
                      (document.getElementById("MyGridRowTemplate")), Data = c };
            var tr = J(row.Element);
            tr.find(".CellName").text(c.Name);
            tr.find(".CellPhoneNumber").text(c.PhoneNumber);
            tr.find(".CellAge").text(c.Age.ToString());
            tr.find(".CellDescription").text(c.Description);
            grid.AddRow(row);
        }
    }
}

在此代码中,我们正在生成 30 个示例联系人,然后我们为它们创建 GridRow 对象,我们还创建从我们的模板克隆的 TR 元素,并将联系人实例的数据绑定到每一行。

实现排序

有了可靠的网格 API,实现排序等功能就很容易了,我们所要做的就是清除网格中的行,对它们进行排序,然后将它们渲染回网格。

public static void SortByName()
{
    var rows = Grid.Rows.OrderBy(t => t.Data.As<Contact>().Name);
    Grid.DeleteAllRows();
    foreach (var row in rows)
        Grid.AddRow(row);
}

OrderBy 方法是在一个小实用程序类中实现的自定义扩展方法

[JsType(JsMode.Prototype, Filename = "GridDemo.js")]
static class JsArrayExtensions
{
    public static JsArray<T> OrderBy<T>(this JsArray<T> array, 
                  JsFunc<T, object> selector, bool desc)
    {
        var array2 = array.slice(0);
        if (!desc)
            array2.sort((x, y) => Compare(selector(x), selector(y)));
        else
            array2.sort((x, y) => CompareDesc(selector(x), selector(y)));
        return array2;
    }
    static JsNumber Compare(object x, object y)
    {
        var xx = x.As<int>();
        var yy = y.As<int>();
        if (xx > yy)
            return 1;
        if (xx < yy)
            return -1;
        return 0;
    }
}

这是 JavaScript 数组上的简化的 LINQ 排序实现,它使用扩展方法实现,以使代码使用更容易,更易于阅读。它仍然由 SharpKit 转换为纯 JavaScript。

下一步是支持按任何属性排序,并记住我们上次完成的排序,以在 升序降序 排序之间切换。我们还应该更改当前已排序列的外观,以便用户了解 grid 已排序。

static Grid Grid;
static HtmlTableCell LastSortHeader;
static JsString LastSort;
static bool IsLastSortDescending;
public static void SortBy(HtmlTableCell header, JsString pe)
{
    J(LastSortHeader).removeClass("Sorted").removeClass("Descending");
    IsLastSortDescending = LastSort == pe && !IsLastSortDescending;
    LastSort = pe;
    LastSortHeader = header;
    J(LastSortHeader).addClass("Sorted");
    J(LastSortHeader).toggleClass("Descending", IsLastSortDescending);

    var rows = Grid.Rows.OrderBy(t => t.Data.As<JsObject>()[pe], IsLastSortDescending);
    Grid.DeleteAllRows();
    foreach (var row in rows)
        Grid.AddRow(row);
}

现在,缺少的就是在单击 grid 标题时调用 SortBy 方法

<table id="MyGrid" class="Grid">
    <thead>
        <tr>
            <th onclick="SortBy(this, 'Name');">Name</th>
            <th onclick="SortBy(this, 'Age');">Age</th>
            <th onclick="SortBy(this, 'PhoneNumber');">Phone Number</th>
            <th onclick="SortBy(this, 'Description');">Description</th>
        </tr>
    </thead>
    <tbody style="display: none">
        <tr id="MyGridRowTemplate">
            <td class="CellName"></td>
            <td class="CellAge"></td>
            <td class="CellPhoneNumber"></td>
            <td class="CellDescription"></td>
        </tr>
    </tbody>
</table>

就是这样,简单、直接、快速且高度可定制。

优化网格

旧版浏览器可能会变慢,有时简单的 jQuery 用法会导致像 IE7 这样的浏览器出现严重的性能损失,有时您必须自己动手并实现自己的优化 DOM API。SharpKit 允许我通过实现扩展方法来轻松地执行此操作。它使代码易于阅读和维护。

[JsType(JsMode.Prototype, Filename = "Grid.js")]
static class Extensions
{
    public static void AppendChildFast(this HtmlElement el, 
                       HtmlElement newElement, HtmlElement lastChild)
    {
        if (lastChild != null && SupportsInsertAdjacentElement)
            lastChild.insertAdjacentElement("afterEnd", newElement);
        else
            el.appendChild(newElement);
    }
}

现在,我可以像在 HtmlElement 上使用方法一样使用 el.AppendChildFast(),而实际上,它是一个 static 扩展方法。关于此方法,在 IE7 等旧浏览器中,appendChild() 可能会非常慢,因为浏览器会迭代所有同级节点,直到到达末尾才能添加该元素。此方法获取指向 lastChild 的指针,并使用 insertAdjacentElement 方法添加元素,而无需这种开销。

关注点

总之,编写 JavaScript 代码有时可能会很复杂,使用 SharpKit 允许我们专注于编写代码,然后在之后执行重构和清理。也可以添加 XML 文档并生成帮助文件,以允许其他开发人员轻松集成您的组件。

历史

  • 2012 年 1 月 30 日:初始版本
© . All rights reserved.