1

General Information

I have a ListView containing multiple columns. I have used a Microsoft Method to sort my ListView Columns. The ListView is populated by a SQL Query which correctly sorts Strings and Integers together (code shown below). For Example:

String & Integer Sorting Problem

The following JobNumber strings are considered as sorted

"10", "1", "2", "3"

Using my SQL Query, they will become

"1", "2", "3", "10"


SQL String & Integer Sorter

Here is the Query I use to sort Strings and Integers correctly

SELECT PK_BillHeader, JobNumber, Description
FROM dbo.BillHeaders 
ORDER BY Case IsNumeric(JobNumber) 
    When 1 Then Replicate('0', 50 - Len(JobNumber)) + JobNumber 
        Else JobNumber 
    End

This enters zeros until it gets the maximum length of my SQL column (50 chars) minus the JobNumber's current length. That way, everything is considered as a string (including integers) which can then be sorted out correctly.


My Problem

When I click on the ListView Column Header (causing it to sort), it stops sorting the strings and integers correctly as it did with my SQL query. Instead, it sorts everything as a string which replicates my "String & Integer" sort problem once more ... What is more, if I have decimal values (second picture), it sorts them out pretty weird (see my compare code)

Machine Number String & Integer Problem Money Sort

Compare Code

Public Function [Compare](ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
        Dim xItem As clsSortWrapper = CType(x, clsSortWrapper)
        Dim yItem As clsSortWrapper = CType(y, clsSortWrapper)

        Dim xText As String = xItem.sortItem.SubItems(xItem.sortColumn).Text
        Dim yText As String = yItem.sortItem.SubItems(yItem.sortColumn).Text

        If Decimal.TryParse(xText, vbNull) Then xText = xText.PadLeft(10, "0"c)
        If Decimal.TryParse(yText, vbNull) Then yText = yText.PadLeft(10, "0"c)
        Return xText.CompareTo(yText) * IIf(Me.ascending, 1, -1)
    End Function

Is there a simpler way to acheive my result of sorting a ListView with Strings and Integers?


Note:

I omitted posting the ListView Sort code here because it would've clustered the post quite a bit. I provided the link which explains it thoroughly.

4

2 回答 2

2

方法 #1:来自 Natural Comparer 的 Codesnippet完整的源代码在这里

这将按照您在问题中描述的方式进行排序,还支持罗马值(“D”、“X”等)。

int System.Collections.Generic.IComparer<string>.Compare(string string1, 
                                                 string string2)
{
  mParser1.Init(string1);
  mParser2.Init(string2);
  int result;
  do
  {
    if (mParser1.TokenType == TokenType.Numerical & 
                 mParser2.TokenType == TokenType.Numerical)
      // both string1 and string2 are numerical 
      result = decimal.Compare(mParser1.NumericalValue, mParser2.NumericalValue);
    else
      result = string.Compare(mParser1.StringValue, mParser2.StringValue);
    if (result != 0) return result;
    else
    {
      mParser1.NextToken();
      mParser2.NextToken();
    }
  } while (!(mParser1.TokenType == TokenType.Nothing & 
             mParser2.TokenType == TokenType.Nothing));  
  return 0; //identical 
}

方法 #2:简单的自定义比较器(没有罗马值)

class MyComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int xVal, yVal;
        var xIsNumeric = int.TryParse( x, out xVal );
        var yIsNumeric= int.TryParse( y, out yVal );

        if (xIsNumeric && yIsNumeric)
            return xVal.CompareTo(yVal);
        if (!xIsNumeric && !yIsNumeric)
            return x.CompareTo(y);
        if (xIsNumeric )           
            return -1;
        return 1;   
    }
}
于 2013-06-27T19:26:42.140 回答
1

这是另一种方式。当您调用 CompareTo 时,在您的 Sort 代码中使用 0 填充字符串。

If Integer.TryParse(xText, VBNull) Then
      xText = xText.PadLeft(6, "0"c)
End If
If Integer.TryParse(yText, VBNull) Then
      yText = yText.PadLeft(6, "0"c)
End If
Return xText.CompareTo(yText) * IIf(Me.ascending, 1, -1)

这样“11004”将被比较为“011004”并出现在“110030”之前,但填充仅用于比较,该值仍将显示为“11004”。

更新:

普通字符串现在将在没有填充的情况下进行排序。

于 2013-06-27T20:00:23.013 回答