String mit Zahlen im RowChanged-Event korrekt sortieren

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von tragl.

    String mit Zahlen im RowChanged-Event korrekt sortieren

    Hallo zusammen.

    Ich stehe vor dem Problem, dass ich meine Strings mit Zahlen nicht sauber sortiert bekomme.
    z.B. habe ich folgende Strings (korrekt sortiert)
    F1
    F2
    F3
    F4
    F5
    F6
    F7
    F8
    F9
    F10
    F11

    Die Sortierung in meinem Programm sieht wie folgt aus:
    F1
    F10
    F11
    F2
    F3

    usw..

    Durch Recherche hab ich rausgefunden, dass man wohl Arrays mit einer Sort-Methode versehen kann - bei mir wird das aber wohl nicht klappen.
    Ich will zur Laufzeit die Daten im DataGridView anzeigen (angebundene BindingSource) und die Daten sollen dort korrekt sortiert angezeigt werden.
    So ähnlich löse ich das Bereits mit meinem TreeView, der arbeitet aber mit Katalognummern (z.B. 1.1.1 / 1.3.4 usw.) - das löse ich mit folgender Methode:

    VB.NET-Quellcode

    1. Public Function GetTreeViewSortKey(tocEntry As String) As String
    2. Dim spaceIndex = tocEntry.IndexOf(" "c)
    3. Select Case spaceIndex
    4. Case 0 : Throw New ArgumentException("mustn't start with ' '", NameOf(tocEntry))
    5. Case > 0 : tocEntry = tocEntry.Substring(0, spaceIndex)
    6. End Select
    7. Dim numbs = tocEntry.Split("."c).Select(AddressOf Integer.Parse).ToArray
    8. If numbs.Any(Function(n) n > Byte.MaxValue) Then Throw New ArgumentException("segments maximum is '255'", NameOf(tocEntry))
    9. Return String.Concat(numbs.Select(Function(n) $"{n,2:X}")) ' 2-stellig rechtsbündige Hex-Darstellung
    10. End Function


    Die Function wird im RowChanged-Event der TreeDataTable aufgerufen und funzt einwandfrei.
    So hätte ich das gerne auch für die o.g. Strings - im RowChanged-Event, reingeschrieben in eine SortKey-Spalte - die kann ich dann als Sort-Spalte bei der Bindingsource nutzen.
    Weiß jemand, wie ich das angehen kann?
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    @tragl Da musst Du Dir Deinen eigenen Sorter schreiben:
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Runtime.InteropServices;
    4. using System.Windows.Forms;
    5. namespace WindowsFormsApp2
    6. {
    7. public partial class MainForm : Form
    8. {
    9. string[] tasten = { "F1", "F10", "F11", "F2", "F3", };
    10. public MainForm()
    11. {
    12. this.InitializeComponent();
    13. }
    14. private void Button1_Click(object sender, EventArgs e)
    15. {
    16. listBox1.Items.AddRange(tasten);
    17. Array.Sort(tasten, new ExplorerComparer());
    18. listBox1.Items.Add("-------------");
    19. listBox1.Items.AddRange(tasten);
    20. }
    21. }
    22. /// <summary>
    23. /// Klasse, die Dateien wie im Exlorer sortiert
    24. /// </summary>
    25. /// <remarks></remarks>
    26. class ExplorerComparer : IComparer<string>
    27. {
    28. [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
    29. static extern int StrCmpLogicalW(string s1, string s2);
    30. public int Compare(string s1, string s2)
    31. {
    32. return ExplorerComparer.StrCmpLogicalW(s1, s2);
    33. }
    34. }
    35. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    entsprechend des angefragten Konzepts:

    VB.NET-Quellcode

    1. Public Function GetTasteSortKey(tocEntry As String) As Integer
    2. 'Vorraussetzung, dass tocEntry genau 1 Buchstabe, und dann 1-mehrere Ziffern enthält
    3. Return Integer.Parse(tocEntry.Substring(1))
    4. End Function
    5. 'oder so, wenn result unbedingt ein String sein muss
    6. Public Function GetTasteSortKeyString(tocEntry As String) As String
    7. Dim i = Integer.Parse(tocEntry.Substring(1))
    8. Return i.ToString("d6")
    9. End Function
    Hi.

    @RodFromGermany @Haudruferzappeltnoch: sind keine Tasten, sondern Spind-Nummern. Ich muss das im RowChanged-Event verarbeiten... die Sache mit dem Sortieren von Arrays war mir ja schon bekannt.
    @ErfinderDesRades: fast - die Nummern sind nun korrekt, die Buchstaben vorneweg allerdings nicht :(

    Mir wär auch eine "allgemein-gültige" Methode für jegliche Strings am Liebsten. Also egal was in dem String drin steht - mit Zahlen - soll er korrekt sortieren.


    Edit: Für den speziellen Fall hab ich's erstmal so lösen können:

    VB.NET-Quellcode

    1. Public Function GetStringSortKey(tocEntry As String) As String
    2. If Integer.TryParse(tocEntry.Substring(1), Nothing) Then
    3. Return $"{tocEntry.Chars(0)}{Integer.Parse(tocEntry.Substring(1)).ToString("d6")}"
    4. Else
    5. Return tocEntry
    6. End If
    7. End Function

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Ja EDRs und meine Methode schmeißen wie man unschwer erkennen kann den ersten Buchstaben weg.
    Entweder du nimmst dir RFGs Methode, die ist wohl "allgemein-gültig" (Microsoft muss ja mit allen möglichen Strings dealen)
    oder du ziehst es entsprechend nach (haste ja nun)
    bei mir:

    VB.NET-Quellcode

    1. keinetasten.OrderBy(Function(s) s(0)).ThenBy(Function(s) CInt(s.Remove(0, 1)))
    Ich kann bei einer BindingSource aber kein OrderBy nutzen, sondern kann dort nur nach einer Spalte sortieren.. zumindest ist mir nix anderes bekannt..
    Außerdem kennt er beim RowChangedEvent ja nicht die anderen Rows.

    VB.NET-Quellcode

    1. Partial Class SpindDataTable
    2. Private Sub SpindDataTable_ColumnChanged(sender As Object, e As DataColumnChangeEventArgs) Handles Me.ColumnChanged
    3. If e.Column IsNot NrColumn Then Return
    4. Dim rw = DirectCast(e.Row, SpindRow)
    5. If e.Row.Item(2).Equals(DBNull.Value) Then Return
    6. rw.SortKey = GetStringSortKeyFromOneCharAndNumbers(rw.Nr)
    7. End Sub
    8. End Class


    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    sind keine Tasten
    Das ist meiner Methode egal, mir auch.
    Was macht meine Methode falsch?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Was macht meine Methode falsch?

    Dass ich sie nicht im RowChanged-Event verwenden kann, da es dort keine anderen Strings für den Vergleich gibt ;)

    Es muss schon so sein, dass wenn die Row angefasst wird, ein korrekter SortKey hinterlegt wird. Damit klappt die Sortierung dann einwandfrei.
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    da es dort keine anderen Strings für den Vergleich gibt
    Dann hast Du Dein Problem falsch beschrieben.
    Was soll denn im RowChanged-Event passieren?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Was soll denn im RowChanged-Event passieren?

    Im RowChanged-Event soll er einen SortKey erzeugen. Mit diesem kann ich dann die Strings sortieren -> in meinem Beispiel ist die Table per BindinSource an DataGridView gebunden, da kann ich eben nicht mit einem Array arbeiten
    sondern kann nur die Sort-Methode der BindingSource nutzen und die bezieht sich ja immer auf eine oder mehrere Spalten.

    Löse ich aktuell wie folgt:

    VB.NET-Quellcode

    1. Partial Class SpindDataTable
    2. Private Sub SpindDataTable_ColumnChanged(sender As Object, e As DataColumnChangeEventArgs) Handles Me.ColumnChanged
    3. If e.Column IsNot NrColumn Then Return
    4. Dim rw = DirectCast(e.Row, SpindRow)
    5. If e.Row.Item(2).Equals(DBNull.Value) Then Return
    6. rw.SortKey = GetStringSortKeyFromOneCharAndNumbers(rw.Nr)
    7. End Sub
    8. End Class


    VB.NET-Quellcode

    1. Public Function GetStringSortKeyFromOneCharAndNumbers(tocEntry As String) As String
    2. If Integer.TryParse(tocEntry.Substring(1), Nothing) Then
    3. Return $"{tocEntry.Chars(0)}{Integer.Parse(tocEntry.Substring(1)).ToString("d6")}"
    4. Else
    5. Return tocEntry
    6. End If
    7. End Function
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    tragl schrieb:

    Sort-Methode der BindingSource
    Und der kannst Du nicht einen Sorter geben?
    Sortieren beruht doch darauf, dass zwei Werte miteinander verglichen werden.
    Wenn Du beim DGV auf die Titelzeile klickst, wird das DGV nach dieser Spalte sortiert.
    Da kann man ganz easy einen Sorter verwenden.
    Das sollte bei Deinem Problem ganz genau so funktionieren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany
    BindingSource.Sort sollte dir bekannt sein, dass das eine Property vom Typ String ist. Da is erstmal nix mit "easy einen Sorter" verwenden.
    Was da intern passiert, weiß ich nicht. Daran rumzucustomizen, das geht nunmal nicht.
    Mit einer Extraspalte wie der TE es tut kann man BindingSource.Sort dann im Customfall wieder nutzen. Die Extraspalte muss dementsprechend erzeugt werden, das is der "SortKey".

    Aber die Frage ist: Muss man denn zwingend BindingSource.Sort benutzen? Das kann ich nicht sagen weil ich nicht weiß wie das intern abgewickelt wird. Am Ende ist das Problem ja auch gelöst damit.
    An sich sind unsere anderen Ansätze genauso geeignet zum sortieren, man muss dann halt die DataSource neu zuweisen.

    Haudruferzappeltnoch schrieb:

    Aber die Frage ist: Muss man denn zwingend BindingSource.Sort benutzen?
    Verwende ich nicht.
    Ich sortiere die DataTable, die ich als DataSource verwende, mit einem IComparer(Of string).
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Haudruferzappeltnoch @RodFromGermany
    Das mit dem Neu-Zuweisen der DataSource habe ich noch nicht in betracht gezogen und wäre eine Idee. Dann würde das mit dem Array-Sorter auf DataTable-Basis sicher hin hauen.
    Wenn ich das richtig sehe, müsste ich das ganze dann aber sicher mit allen ggf. vorhandenen Cols im DataGridView betreiben. Sprich das entspr. Event abfangen, dann DataSource abstöpseln, Sortieren, wieder anstöpseln.
    Ich denke, da ist die Methode mit dem Sortkey schon einfacher ;)
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup: