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

ASP.NET GridView ASCII 和数值排序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (95投票s)

2011年1月20日

CPOL

3分钟阅读

viewsIcon

55501

downloadIcon

1252

使用 ASP.NET GridView 进行 ASCII 和数值排序

引言

有多少人有一个包含复杂值的列表,并且希望根据 ASCII 码和实际值对其进行排序? 我想这篇文章肯定能解决你的问题。

我将尝试基于一个简单的场景来解释解决方案。

背景

其中一个用户需求是能够对包含地块记录(包括地块号、区域代码、面积和周长以及其他属性)进行排序和查看。地块号包含一系列字符,其中可能包含字母、数字和一个或多个非字母数字字符,例如正斜杠 (/)、反斜杠 (\)、短划线 (-) 或下划线 ( _ )。

因此,排序要求是,数字前有一个字母或非字母数字字符,并且应该按数字的实际值以及其余字符进行排序。例如

  • P-1054/A
  • P-100/B
  • P-807/A
  • P-1083/A
  • P-20/B

排序后将是

  • P-20/B
  • P-100/B
  • P-807/A
  • P-1054/A
  • P-1083/A

问题

为了解决这个要求,让我们分别研究一下 List<T> 泛型集合类和 DataView 类的默认 Sort 方法和属性。 它们都给我们一个排序列表,但不能给出所需的结果。 如果我们对前面的示例进行排序,输出将是

  • P-100/B
  • P-1054/A
  • P-1083/
  • P-20/B
  • P-807/A

解决方案

其中一个解决方案是遍历每个字符,并根据其数字、字母或特殊字符值比较其值。 因此,如果要比较的对象包含一个数字,则按数字值进行比较。 我将在下一节中解释如何执行此操作。

注意:对于本文,我将仅考虑 List<T> 类。

Using the Code

.NET 提供的内置比较接口之一是 IComparer<T> 接口,它是一个泛型类型。 在我称为 CharacterComparer<T>Comparer 类中实现此接口,使我们得到一个泛型 Compare 方法。该类考虑将使用 Type T 的哪个属性来比较值。 也可以进行区分大小写的比较。 我还使用 Reflection 来获取 Type T 的属性值,以便可以轻松地进行实际的字符比较。下面显示了排序类代码的一部分

namespace SortLibray
{
    //
    //  Full code available in the source code
    //

    /// <summary>
    /// Character Comparer class
    /// </summary>
    /// <typeparam name="T">A Type T which
    ///         it's property is being compared</typeparam>
    public class CharacterComparer<T> : IComparer<T>
    {
        /// <summary>
        /// Compares the left and the right property values of a Type T
        /// </summary>
        /// <param name="left">Left value of Type T</param>
        /// <param name="right">Right value of Type T</param>
        /// <returns>An indicator whether the left
        ///         or the right is greater or not</returns>
        /// <remarks></remarks>
        public int Compare(T left, T right)
        {
            //
            //  Full code available in the source code
            //

            // Traverse each character of the left and right values of Type T
            char charLeft = leftValue[indicatorLeft];
            char charRight = rightValue[indicatorRight];

            // Allocate char array, based on the left and right values length of Type T
            char[] spaceLeft = new char[lengthLeft];
            char[] spaceRight = new char[lengthRight];
            int lockerLeft = 0,lockerRight = 0;

            do // Iterate each characters of the left
               // value of the Type T , until you get a Digit 
            {
                spaceLeft[lockerLeft] = charLeft;
                lockerLeft = lockerLeft + 1;
                indicatorLeft = indicatorLeft + 1;

                if (indicatorLeft < lengthLeft)
                    charLeft = leftValue[indicatorLeft];
                else
                    break;

            } while (char.IsDigit(charLeft) == char.IsDigit(spaceLeft[0]));
        }

       //
       //  Full code available in the source code
       //
     }
}

比较逻辑的核心是遍历 Type T 对象的左侧和右侧属性值的每个字符。我们还可以将其扩展以符合特定类(例如 Plot)的每个属性(我选择此类是因为我已在本文的背景中提到了它)。

namespace SortDemo
{
    /// <summary>
    /// Plot class
    /// </summary>
    public class Plot
    {
        //
        // Public Properties
        //
 
        /// <summary>
        /// Plot class
        /// </summary>
        public Plot()
        {
        }

        /// <summary>
        /// Plot class
        /// </summary>
        /// <param name="plotNumber">PlotNumber value</param>
        /// <param name="areaCode">AreaCode value</param>
        /// <param name="area">Area value</param>
        /// <param name="perimeter">Perimeter value</param>
        public Plot(string plotNumber, int? areaCode, 
                    float? area, float? perimeter)
        {
            this.PlotNumber = plotNumber;
            this.AreaCode = areaCode;
            this.Area = area;
            this.Perimeter = perimeter;
        }
    }
}

扩展的 PlotNumberComparer 类现在将如下所示

namespace SortDemo
{
    /// <summary>
    /// PlotNumberComparer class
    /// </summary>
    public class PlotNumberComparer : CharacterComparer<Plot>
    {
        /// <summary>
        /// PlotNumberComparter class
        /// </summary>
        public PlotNumberComparer()
            :base("PlotNumber")
        {
            //
            // TODO: Add constructor logic here
            //
        }
        
        /// <summary>
        /// PlotNumberComparter class
        /// <param name="caseSensitive">Case Sensitivity indicator value</param>
        /// </summary>
        public PlotNumberComparer(bool caseSensitive)
            : base("PlotNumber",caseSensitive)
        {
            //
            // TODO: Add constructor logic here
            //
        }
    }
}

让我们看看我们如何演示此比较器功能。我选择了 ASP.NET 进行演示,使用 GridView 控件。 在进入之前,让我们为 Plot 类创建一个名为 Plots 的集合类,并为 List<T> 泛型集合类创建一个名为 SortExtension 的扩展类,它将与 GridView 控件兼容。

这是 Plots

namespace SortDemo
{
    /// <summary>
    /// Plots class
    /// </summary>    
    public class Plots : List<Plot>
    {
        /// <summary>
        /// Plots class
        /// </summary>
        public Plots()
        {
            this.Add(new Plot("P-1054/A", 3001, null, 105.081f));
            this.Add(new Plot("P-100/B", 01, 734.156f, null));        
            this.Add(new Plot("P-807/A", 3001, 764.277f, 111.299f));
            this.Add(new Plot("P-20/B", 01, 734.156f, 108.945f));
            this.Add(new Plot("P-1083/A", 3001, 198.52f, 68.108f));
        }
    }
}

这是 SortExtension

namespace SortDemo
{
    /// <summary>
    /// SortExtension class
    /// </summary>
    public static class SortExtension
    {
        /// <summary>
        /// List sort extension method
        /// </summary>
        /// <typeparam name="T">Type T to be sorted</typeparam>
        /// <param name="genericList">Generic list to be sorted</param>
        /// <param name="sortDirection">SortDirection value</param>
        /// <param name="comparer">Comparer value</param>
        /// <param name="caseSensitive">CaseSensitive indicator value</param>
        /// <returns>Sorted list of type T</returns>
        public static IList<T> SortedList<T>(this List<T> genericList,
            string sortExpression, SortDirection sortDirection,
            CharacterComparer<T> comparer = null, bool caseSensitive = false)
        {
            if (genericList == null ||
                string.IsNullOrEmpty(sortExpression) ||
                string.IsNullOrWhiteSpace(sortExpression))
                return null;
            else
            {
                if (comparer == null)
                    if (caseSensitive)
                        comparer = new CharacterComparer<T>(sortExpression, caseSensitive);
                    else
                        comparer = new CharacterComparer<T>(sortExpression);
                else
                    if (caseSensitive)
                        if (!comparer.CaseSensitive)
                            comparer.CaseSensitive = caseSensitive;

                genericList.Sort(comparer);
                if (sortDirection == SortDirection.Descending)
                    genericList.Reverse();
            }
            return genericList;
        }

        /// <summary>
        /// List sort extension method
        /// </summary>
        /// <typeparam name="T">Type T to be sorted</typeparam>
        /// <param name="genericList">Generic list to be sorted</param>
        /// <param name="sortDirection">SortDirection value</param>
        /// <param name="comparer">Comparer value</param>
        /// <param name="caseSensitive">CaseSensitive indicator value</param>
        /// <returns>Sorted list of type T</returns>
        public static IList<T> SortedList<T>
    (this List<T> genericList, SortDirection sortDirection,
            CharacterComparer<T> comparer, bool caseSensitive = false)
        {
            if (caseSensitive)
                if (!comparer.CaseSensitive)
                    comparer.CaseSensitive = caseSensitive;

            genericList.Sort(comparer);
            if (sortDirection == SortDirection.Descending)
                genericList.Reverse();
            return genericList;
        }

        /// <summary>
        /// List sort extension method
        /// </summary>
        /// <typeparam name="T">Type T to be sorted</typeparam>
        /// <param name="genericList">Generic list to be sorted</param>
        /// <param name="sortExpression">SortExpression value</param>
        /// <param name="sortDirection">SortDirection value</param>
        /// <param name="caseSensitive">CaseSensitive indicator value</param>
        /// <returns>Sorted list of type T</returns>
        public static IList<T> SortedList<T>
    (this List<T> genericList, string sortExpression, SortDirection sortDirection,
            bool caseSensitive = false)
        {
            return SortedList(genericList, sortExpression, 
            sortDirection, null, caseSensitive);
        }               
    }
}

现在到目前为止,大多数事情都做得很好。 让我们将它们放在表示层中。 在我们的表示代码类 GridViewSortDemo.aspx.cs 中,让我们放入几个方法和属性,这将使我们演示变得更容易。

以下是一些页面属性

///
/// Get or set GridView SortExpression in a viewstate
/// 
public string GridViewSortExpression
{
    get
    {
        if (ViewState[Constants.SORT_EXPRESSION] != null)
            return ViewState[Constants.SORT_EXPRESSION].ToString();
        return Constants.PLOT_NUMBER; // return PlotNumber as a default expression
    }
    set
    {
        ViewState[Constants.SORT_EXPRESSION] = value;
    }
}

///
/// Get or set GridView SortDirection in a viewstate
/// 
public SortDirection GridViewSortDirection
{
    get
    {
        if (ViewState[Constants.SORT_DIRECTION] != null)
            return (SortDirection)ViewState[Constants.SORT_DIRECTION];
        return SortDirection.Ascending; // return Ascending order
    }
    set
    {
        ViewState[Constants.SORT_DIRECTION] = value;
    }
}

以下是一些 private 方法

/// summary
/// Bind GridView to a DataSource
/// summary
private void bindGridView()
{
    try
    {
        Plots plots = new Plots();
        if (cbCharactorComparer.Checked) // Use the Character Comparer
        {
            if (GridViewSortExpression.ToLower().Equals(Constants.PLOT_NUMBER.ToLower()))
               plots.SortedList(GridViewSortDirection, 
        new PlotNumberComparer(), cbCaseSensitivity.Checked);
            else
               plots.SortedList(GridViewSortExpression, 
        GridViewSortDirection, cbCaseSensitivity.Checked);
            gvPlots.DataSource = plots;
            gvPlots.DataBind();
        }
        else // Use built in Linq Sort mechanism 
        {
            List<Plot> sortResult = plots;
            switch (GridViewSortExpression)
            {
                case Constants.PLOT_NUMBER:
                    sortResult = plots.OrderBy(p => p.PlotNumber).ToList();
                    break;
                case Constants.AREA_CODE:
                    sortResult = plots.OrderBy(p => p.AreaCode).ToList();
                    break;
                case Constants.AREA:
                    sortResult = plots.OrderBy(p => p.Area).ToList();
                    break;
                case Constants.PERIMETER:
                    sortResult = plots.OrderBy(p => p.Perimeter).ToList();
                    break;
                default:
                    sortResult = plots.OrderBy(p => p.PlotNumber).ToList();
                    break;
            }
            if (GridViewSortDirection == SortDirection.Descending)
                sortResult.Reverse();
            gvPlots.DataSource = sortResult;
            gvPlots.DataBind();
        }
    }
    catch(Exception exception)
    {
        Response.Write(exception.Message);
    }
 }

最后,让我们将方法 bindGridView() 调用到 Page_Load 事件

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
        bindGridView();
}

现在一切都展示得很好,可以进行测试了。 只要下载功能齐全的应用程序并运行它即可。 尽情享受。 :)

历史

  • 2011 年 1 月 17 日:第一个版本
  • 2012 年 2 月 27 日:更新版本
  • 2012 年 2 月 28 日:更新版本
© . All rights reserved.