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

ASP.NET GridView 排序指示器组件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (12投票s)

2007 年 7 月 23 日

Apache

5分钟阅读

viewsIcon

99606

downloadIcon

643

本文介绍了一个组件,该组件可在 GridView 的列标题中显示数据排序所依据的排序指示器。

Class Diagram

引言

本文介绍了一个用于在 GridView 列中添加排序指示器图像的组件。

互联网上有很多关于如何为 GridView 添加某种排序指示器的网站,但它们都是针对单个 GridView 的。我们的应用程序有相当多的 GridView 需要此功能,因此我决定创建一个组件是正确的方法。组件的优点在于我可以只编写一次行为,而无需为每个单独的 GridView 反复编写。

本文的其余部分将展示如何使用 GridViewSortExtender(组件名称)以及它内部的工作原理。

直接使用

要使用 GridViewSortExtender,只需将其与要扩展的 GridView 放在同一页面上即可。

Designer

然后,在属性窗格中设置图像的属性,并指定应扩展哪个 GridView

Designer Properties

就是这样!运行应用程序。它看起来会像这样。

Browser

内部原理

GirdViewSortExtender 是一个相当简单的组件,但其中仍有一些有趣之处,我将在本节中进行讨论。

如何将指示器添加到排序列

我使用 GridViewSortExtenderOnPreRender 方法来修改已扩展的 GridView。这在我的项目中有效,因为我通常不再在页面本身的 OnPreRender 方法中修改页面内容。注意:页面上的 OnPreRender 在包含组件的 OnPreRender 方法之后被调用。

注意:图像始终被添加,否则在不绑定 GridView 的回发中将丢失(有关更多信息,请参见下方的消息部分)。

public class GridViewSortExtender : Control
{
    /// ... other code ...

    /// <summary>
    /// Adds an event handler to the DataBound event of the extended GridView.
    /// </summary>
    /// <param name="e"></param>

    protected override void OnPreRender(EventArgs e)
    {
        base.OnInit(e);

        GridView extendee = 
            this.NamingContainer.FindControl(this.ExtendeeID) as GridView;
        if (extendee != null && 
            extendee.AllowSorting && 
            extendee.HeaderRow != null && 
            !String.IsNullOrEmpty(extendee.SortExpression))
        {
            int field = GetSortField(extendee);
            if (field >= 0)
            {
                Image img = new Image();
                img.ImageUrl = 
                    extendee.SortDirection == SortDirection.Ascending ? 
                        this.AscendingImageUrl : this.DescendingImageUrl;
                img.ImageAlign = ImageAlign.TextTop;

                extendee.HeaderRow.Cells[field].Controls.Add(img);
            }
        }
    }

    /// <summary>
    /// Returns the index of the sort-column.
    /// </summary>
    /// <param name="extendee"></param>
    /// <returns></returns>

    private int GetSortField(GridView extendee)
    {
        int i = 0;
        foreach (DataControlField field in extendee.Columns)
        {
            if (field.SortExpression == extendee.SortExpression)
            {
                return i;
            }
            i++;
        }
        return -1;
    }
}

第一件事是获取已扩展的 GridView。这可以通过调用 GridViewSortExtenderNamingContainer 上的 FindControl 方法来实现。NamingContainer 是定义 GridViewSortExtender 的容器。因此,GridViewSortExtender 必须与它所扩展的 GridView 位于同一容器中。

如果找到了相应的 GridView,则会检查该 GridView 是否允许排序并且具有标题行——只有这样,添加排序指示器才是有意义的。

然后,根据已扩展 GridViewSortDirection,实例化一个图像。

棘手的部分是如何找到添加图像的正确列。我遍历已扩展 GridView 的所有列,并将其 SortExpressionGridViewSortExpression 进行比较——如果相同,则找到了正确的列。

最后,我将图像添加到该列的标题单元格中。

使用 SortExpression 的一个缺点是 GridViewSortExtender 仅适用于 BoundField 列。当您拥有具有动态生成列的 GridView 时,这尤其成问题。

旁注:通常,我会重写事件调用方方法(如 OnPreRender)而不是注册事件(如 PreRender),因为我不喜欢实例注册自身的事件。我认为这是糟糕的面向对象设计 :-)

如何使其支持设计器

我在我的应用程序中经常使用此组件,因此我希望有一个在设计器中具有良好行为的组件。

引用图像

GridViewSortExtender 有两个图像引用。我使用 Editor 属性在设计器中获得一个漂亮的选对话框。图像的 URL 然后存储在 ViewState 中。

/// <summary>
/// Image that is displayed when SortDirection is ascending.
/// </summary>

[DefaultValue("")]
[Editor(typeof(ImageUrlEditor), typeof(UITypeEditor))]
[Description("Image that is displayed when SortDirection is ascending.")]
public string AscendingImageUrl
{
    get { return this.ViewState["AscendingImageUrl"] != null ?
              (string)this.ViewState["AscendingImageUrl"] : ""; }
    set { this.ViewState["AscendingImageUrl"] = value; }
}

引用 GridView

为了获得类似的可扩展 GridView 选择行为,我为 ExtendeeID 属性选择了 IDReferenceProperty。不幸的是,结果不稳定:有时设计器会在属性列表中的选择列表中提供页面上的所有 GridView,有时则不会。因此,我添加了 TypeConverter 属性和一个自定义 TypeConverterGridViewIDConverter)。现在,设计器可以可靠地提供页面上所有 DataGrid 的选择。

/// <summary>
/// The GridView that is extended.
/// </summary>

[DefaultValue("")]
[IDReferenceProperty(typeof(GridView))]
[TypeConverter(typeof(GridViewIDConverter))]
[Description("The GridView that is extended.")]
public string ExtendeeID
{
    get { return this.ViewState["Extendee"] != null ?
              (string)this.ViewState["Extendee"] : ""; }
    set { this.ViewState["Extendee"] = value; }
}

GridViewIDConverter 继承自通用的 ControlIDConverter,它为我们做了大部分工作。我们仍然需要做的就是定义在设计器提供给程序员的列表中应可见哪些控件。在这种情况下,我只需定义该控件必须是 GridView

public class GridViewIDConverter : ControlIDConverter
{
    // Methods

    protected override bool FilterControl(Control control)
    {
        return control is GridView;
    }
}

在设计器中显示 GirdViewSortExtender

我添加了一个自定义控件设计器,以便在设计模式下获得组件的美观外观。Designer 属性指示我想将 GridViewSortExtenderDesigner 用作我的组件的设计器。GridViewSortExtenderDesigner 继承自 CodeDesigner,并重写了 GetDesignTimeHtml 方法以返回用于在设计模式下显示组件的 HTML。

注意:您可以在上面的图片中看到它的外观。

[Designer(typeof(GridViewSortExtenderDesigner))]
public class GridViewSortExtender : Control
{
    ... some code ...
}
    
/// <summary>
/// Designer for <see cref="GridViewSortExtender"/>.
/// </summary>

public class GridViewSortExtenderDesigner : ControlDesigner
{
    public override string GetDesignTimeHtml()
    {
        return 
            "<div style=\"background-color: #C8C8C8; " + 
            "border: groove 2 Gray;\"><b>GridViewSortExtender</b> - " + 
            this.Component.Site.Name + "</div>";
    }
}

可能的扩展

  • 当列未排序时添加分隔符图像(防止列宽更改)。

结论

GridViewSortExtenderDesigner 是一个简单的组件,用于向 GridView 添加排序指示器,并且具有良好的设计时行为,可简化在具有多个可排序 GridView 的项目中的使用。

代码来源

示例代码摘自我为 bbv Software Services AG 编写的 Web 应用程序。

感谢允许我撰写此文。

历史

  • 2007-07-23 - 初始版本。
  • 2007-08-06 - 使用 GridView 上的 DataBound 事件。感谢 Michael Tucker 的建议。
  • 2007-08-07 - 改回 PreRender 方法,因为否则在不绑定 GridView 的回发中会丢失排序指示器。感谢 Kelly Herald 的反馈。
© . All rights reserved.