Avatar billede japlex Nybegynder
20. september 2001 - 11:15 Der er 25 kommentarer og
5 løsninger

Sortering i en Listview

Jeg sorterer i et listview ved klik på kolonneheaderen, MEN i en kolonne med kundenumre sorteres ikke korrekt.

Jeg får noget der ligner:

...
1451
1466
1487
1677
1800
1944
20001 ???
20014 ???
24511 ???
2001  ???
2045  ???
2099  ???
...

Alle de 4-cifrede tal burde komme før de 5-cifrede ??


koden:
******************************************************

ListView3.Sorted = True
ListView3.SortOrder = lvwAscending
ListView3.SortKey = ColumnHeader.Index - 1

******************************************************
Avatar billede cellaneous Nybegynder
20. september 2001 - 11:20 #1
listview kan ikke sortere \"numerisk\" af sig selv, den sorterer \"alfabetisk\" og i \"alfabetisk\" kommer 2 efter 1 derfor kommer 20001 efter 1944
nemmeste løsning er at du laver foranstillede nuller så alle tal har samme længde.
Avatar billede japlex Nybegynder
20. september 2001 - 11:23 #2
Tak - men kan det ikke lade sig gøre at sortere numerisk?
Avatar billede wilco Nybegynder
20. september 2001 - 11:27 #3
Det kunne måske løses ved at du indsætter værdien i som index\'et (kan det lade sig gøre ?) og så sætter sortkey til >0

SortKey
Returns or sets a value that determines how the ListItem objects in a ListView control are sorted.

The parts of the SortKey property syntax are described in the following table.

Part Description
object An object expression that evaluates to a ListView control.
integer An integer specifying the sort Key, as described in Settings.


Settings
The SortKey property settings are described in the following table.

Setting Description
0 Sort using the ListItem object\'s Text property.
> 1 Sort using the subitem whose collection Index is specified here. 


Remarks
The Sorted property must be set to True before the change takes place.

/wilco
Avatar billede japlex Nybegynder
20. september 2001 - 11:54 #4
muligvis ??

Jeg har ændret min \"add\" på min listview til:

*********************\'
Set itmX = Listview.ListItems.Add(rs!kundenr, , \"\")
************************

altså indsætter jeg et kundenr fra mit recordset som index, men får fejlmeddelelsen \"Index out of bounds\"????

Avatar billede cellaneous Nybegynder
20. september 2001 - 12:00 #5
jeg har ikke prøvet det, men hvis du absolut vil have den pågældende kolonne i numerisk sortering er der vist noget info her:

http://www.mvps.org/vb/samples.htm
http://www.mvps.org/vb/code/LVStyles.zip

ListViews are controls everyone loves to hate. This demo shows a good number of tricks you can use to make your life with ListViews easier. Sort dates and numbers instead of just text (and at a pace 10-20x faster than the KB method!), reorder the columns, toggle extended styles, use checkboxes on each item, the list goes on. As always with benchmarks, make sure that you time the sorts using an EXE rather than from within the IDE.
Important Note: Sorting a listview using the API (as two of the three methods here demonstrate) breaks the synchronization between the ListView object\'s ListItems collection and what\'s being displayed in the listview window. In other words, when you query VB\'s wrapped collection, the order of items returned will not match what\'s displayed. The only way to work around this \"issue\" is to go entirely to the API for all listview operations or live with VB\'s lame sorting options.
Warning: The fastest sort (the \"IListItem Hack\") is included for educational purposes only! If you\'re crazy enough to use that code in your application, you will deserve it when Microsoft breaks you. And don\'t doubt that they will!

Avatar billede jelzin101 Praktikant
20. september 2001 - 15:01 #6
hvad med go\'e gamle bucketsort :

For i = 1 To UBound(numbers) - 1
    For j = 1 To UBound(numbers) - 1
        If numbers(j) > numbers(j + 1) Then
            temp = numbers(j)
            numbers(j) = numbers(j + 1)
            numbers(j + 1) = temp
        End If
    Next
Next
Avatar billede cellaneous Nybegynder
20. september 2001 - 15:57 #7
jelzin > ser da ihvertfald en hel del mere overskuelig ud end det der link jeg fandt :-)
Avatar billede jelzin101 Praktikant
20. september 2001 - 17:48 #8
:) - og så er den meget meget hurtig....
Avatar billede jelzin101 Praktikant
20. september 2001 - 17:53 #9
hovsa der kludrede jeg i det :) det er ikke bucketsort :(  men bubblesort :)

den er ikke så hurtig igen fordi løkken skal køres mindst lige så mange gange som der er elementer....

meeeeeen måske kunne en quicksort gøre noget ved det......

finder den lige.
Avatar billede jelzin101 Praktikant
20. september 2001 - 18:19 #10
int function Partition (Array A, int Lb, int Ub);
  begin
  select a pivot from A[Lb]...A[Ub];
  reorder A[Lb]...A[Ub] such that:
    all values to the left of the pivot are <= pivot
    all values to the right of the pivot are >= pivot
  return pivot position;
  end;

procedure QuickSort (Array A, int Lb, int Ub);
  begin
  if Lb < Ub then
    M = Partition (A, Lb, Ub);
    QuickSort (A, Lb, M - 1);
    QuickSort (A, M + 1, Ub);
  end;

Avatar billede jelzin101 Praktikant
20. september 2001 - 18:20 #11
og i vb :
\' quick sort

Private Function Partition(ByRef A() As Variant, ByVal Lb As Long, ByVal Ub As Long) _
        As Long
    Dim t As Variant
    Dim pivot As Variant
    Dim i As Long
    Dim j As Long
    Dim p As Long

    \' partition array[lb..ub]

    \' select pivot and exchange with 1st element
    p = Lb + (Ub - Lb) \\ 2
    pivot = A(p)
    A(p) = A(Lb)

    \' sort Lb+1 .. Ub based on pivot
    i = Lb + 1
    j = Ub
    Do
        Do While i < j
            If pivot <= A(i) Then Exit Do
            i = i + 1
        Loop
        Do While j >= i
            If A(j) <= pivot Then Exit Do
            j = j - 1
        Loop
        If i >= j Then Exit Do
        t = A(i)
        A(i) = A(j)
        A(j) = t
        j = j - 1
        i = i + 1
    Loop

    \' pivot belongs in a(j)
    A(Lb) = A(j)
    A(j) = pivot
    Partition = j
End Function

Public Sub QuickSort(ByRef A() As Variant, ByVal Lb As Long, ByVal Ub As Long)
    Dim m As Long

    \' sort array A(lb..ub)

    Do While Lb < Ub
        \' quickly sort short lists
        If (Ub - Lb <= 12) Then
            Call InsertSort(A, Lb, Ub)
            Exit Sub
        End If

        \' partition into two segments
        m = Partition(A, Lb, Ub)

        \' sort the smallest partition to minimize stack requirements
        If m - Lb <= Ub - m Then
            Call QuickSort(A, Lb, m - 1)
            Lb = m + 1
        Else
            Call QuickSort(A, m + 1, Ub)
            Ub = m - 1
        End If
    Loop
End Sub

Avatar billede jelzin101 Praktikant
20. september 2001 - 18:21 #12
og qsort(for god ordens skyld):

Public Sub QSort(ByRef A() As Variant, ByVal Lb As Long, ByVal Ub As Long)
    Dim lbStack(32) As Long
    Dim ubStack(32) As Long
    Dim sp As Long              \' stack pointer
    Dim lbx As Long            \' current lower-bound
    Dim ubx As Long            \' current upper-bound
    Dim m As Long
    Dim p As Long              \' index to pivot
    Dim i As Long
    Dim j As Long
    Dim t As Variant            \' temp used for exchanges

    lbStack(0) = Lb
    ubStack(0) = Ub
    sp = 0
    Do While sp >= 0
        lbx = lbStack(sp)
        ubx = ubStack(sp)

        Do While (lbx < ubx)

            \' select pivot and exchange with 1st element
            p = lbx + (ubx - lbx) \\ 2

            \' exchange lbx, p
            t = A(lbx)
            A(lbx) = A(p)
            A(p) = t

            \' partition into two segments
            i = lbx + 1
            j = ubx
            Do
                Do While i < j
                    If A(lbx) <= A(i) Then Exit Do
                    i = i + 1
                Loop

                Do While j >= i
                    If A(j) <= A(lbx) Then Exit Do
                    j = j - 1
                Loop

                If i >= j Then Exit Do

                \' exchange i, j
                t = A(i)
                A(i) = A(j)
                A(j) = t

                j = j - 1
                i = i + 1
            Loop

            \' pivot belongs in A[j]
            \' exchange lbx, j
            t = A(lbx)
            A(lbx) = A(j)
            A(j) = t

            m = j

            \' keep processing smallest segment, and stack largest
            If m - lbx <= ubx - m Then
                If m + 1 < ubx Then
                    lbStack(sp) = m + 1
                    ubStack(sp) = ubx
                    sp = sp + 1
                End If
                ubx = m - 1
            Else
                If m - 1 > lbx Then
                    lbStack(sp) = lbx
                    ubStack(sp) = m - 1
                    sp = sp + 1
                End If
                lbx = m + 1
            End If
        Loop
        sp = sp - 1
    Loop
End Sub

Avatar billede cellaneous Nybegynder
20. september 2001 - 20:41 #13
jelzin, når du nu er så godt i gang, kunne du så ikke tænke dig at smide et eksmpel på et stykke kode af hvor/hvordan man kalder den sortering istedetfor den som listview ellers bruger?

jeg har ikke helt styr på listview og gad godt gemme dine guldkorn til hvis nu jeg engang får brug for dem :-)
Avatar billede bennytordrup Nybegynder
09. oktober 2001 - 15:09 #14
Som det er nævnt, så er problemet, at ListView sorterer alfa-numerisk. Det kan lade sig gøre at sortere efter tal, datoer eller hvad ellers man vil, men det kræver, at du foretager sorteringen via API.

Der findes en message LVM_SORTITEMS = (LVM_FIRST + 48), som skal sendes til listview\'et. Den tager en parameter med, som er adressen på den funktion, som skal foretage sammenligningen mellem to elementer. Den skal erklæres som

Public Function ListView_CompareValues(ByVal lParam1 As Long, ByVal lParam2 As Long, ByVal hInfo As Long) As ListViewSortResults

Public Enum ListViewSortResults
    lvsrPreceed = -1&
    lvsrEqual = 0&
    lvsrFollow = 1&
End Enum

Den kaldes således:

SendMessageAny lvBalanceAgeing.hWnd, LVM_SORTITEMS, VarPtr(SortInfo), ByVal FARPROC(AddressOf ListView_CompareValues)

SortInfo er en struktur, jeg bruger i sorteringen til at angive sorteringsinfo:

Public Type ListViewSortItemInfo
    hWnd As Long
    lDataType As VbVarType
    lSortOrder As ListSortOrderConstants
    lColumnHeaderIndex As Long
    lLastSortableIndex As Long
End Type

Min Compare-funktion ser således ud:

Public Function ListView_CompareValues(ByVal lParam1 As Long, ByVal lParam2 As Long, ByVal hInfo As Long) As ListViewSortResults
    Dim Idx1 As Long, Idx2 As Long
    Dim SortInfo As ListViewSortItemInfo
    CopyMemory ByVal SortInfo, ByVal hInfo, Len(SortInfo)
   
    With SortInfo
        objFind.Flags = LVFI_PARAM
        objFind.lParam = lParam1
        Idx1 = SendMessageAny(.hWnd, LVM_FINDITEM, -1, objFind)
        objFind.Flags = LVFI_PARAM
        objFind.lParam = lParam2
        Idx2 = SendMessageAny(.hWnd, LVM_FINDITEM, -1, objFind)
       
        .lLastSortableIndex = .lLastSortableIndex - 1
       
        If Idx1 > .lLastSortableIndex Or Idx2 > .lLastSortableIndex Then
            If Idx1 > .lLastSortableIndex And Idx2 > .lLastSortableIndex Then
                ListView_CompareValues = lvsrPreceed
            ElseIf Idx1 <= .lLastSortableIndex And Idx2 > .lLastSortableIndex Then
                ListView_CompareValues = lvsrPreceed
            ElseIf Idx1 > .lLastSortableIndex And Idx2 <= .lLastSortableIndex Then
                ListView_CompareValues = lvsrFollow
            End If
        Else
            Select Case .lDataType
                Case vbDate
                    ListView_CompareValues = CompareDate(ListView_GetItemDate(.hWnd, lParam1, .lColumnHeaderIndex), _
                                                                                                    ListView_GetItemDate(.hWnd, lParam2, .lColumnHeaderIndex), _
                                                                                                    .lSortOrder)
                Case vbLong, vbInteger, vbByte
                    ListView_CompareValues = CompareLong(ListView_GetItemValueLong(.hWnd, lParam1, .lColumnHeaderIndex), _
                                                                                                    ListView_GetItemValueLong(.hWnd, lParam2, .lColumnHeaderIndex), _
                                                                                                    .lSortOrder)
                Case vbDouble, vbSingle
                    ListView_CompareValues = CompareDouble(ListView_GetItemValueDouble(.hWnd, lParam1, .lColumnHeaderIndex), _
                                                                                                        ListView_GetItemValueDouble(.hWnd, lParam2, .lColumnHeaderIndex), _
                                                                                                        .lSortOrder)
                Case vbCurrency
                    ListView_CompareValues = CompareCurrency(ListView_GetItemValueCurrency(.hWnd, lParam1, .lColumnHeaderIndex), _
                                                                                                            ListView_GetItemValueCurrency(.hWnd, lParam2, .lColumnHeaderIndex), _
                                                                                                            .lSortOrder)
                Case vbString
                    ListView_CompareValues = CompareString(ListView_GetItemString(.hWnd, lParam1, .lColumnHeaderIndex), _
                                                                                                        ListView_GetItemString(.hWnd, lParam2, .lColumnHeaderIndex), _
                                                                                                        .lSortOrder)
            End Select
        End If
    End With
End Function

De enkelte CompareXxxx sørger for at sammenligne de datatyper, de får over og returnere den returværdi, der skal tilbage fra ListView_CompareValues.

Funktionerne ListView_GetItemXxxx sørger for at hente værdien fra et (sub)item og konvertere til den datatype, der skal sorteres efter.

Hvis du vil bruge API til sortering, skal du være opmærksom på, at du ikke kan bruge almindelig sortering på ListView. Desuden kan du ikke bruge rækkefølgen i ListItems collection, da den ikke vedligeholdes internt.

Benny
Avatar billede bennytordrup Nybegynder
24. oktober 2001 - 23:02 #15
Fik du løst dit problem?
Avatar billede japlex Nybegynder
25. oktober 2001 - 07:34 #16
Har ladet det ligge lidt - nok andet at lave, men vender så hurtigt som muligt tilbage !!
Avatar billede cole Nybegynder
03. november 2001 - 23:00 #17
Når du indsætter kundenummeret i listview så skriv
Dim Punkt As ListItem

Set Punkt = Listviw.ListItems.Add(, ,Format Kundenummer,\"00000\"))

Derved får du nuller foran de små tal og din sortering bliver rigtig.
Avatar billede bennytordrup Nybegynder
06. november 2001 - 23:24 #18
Hvad så, hvis brugeren af programmet ikke ønsker at få vist foranstillede nuller :-)
Avatar billede cole Nybegynder
07. november 2001 - 06:15 #19
Det er let nok du skal bare sætte en ekstra kollonnerne som en snyde kollonne med Width egenskaben til 0. Derved kan den ikke ses. F.eks. ListWiev1.ColumHeaders.Add 1, ,\"Kundenummer\", 0

Det er en god ide at bruge den første som snydekollonne kollonne da den ikke kan højrestilles. Når du så soterer bruger du bare den kollonne som du ikke kan se.
Avatar billede bennytordrup Nybegynder
07. november 2001 - 08:42 #20
For det første kræver din løsning, at du forinden afgør længden af det længste kundenummer (evt. max længde).

For det andet, min løsning giver mulighed for at sortere på valgfri datatype i valgfri kolonne - eks. dato i en kolonne, tal i en anden og tekst i en tredje.
Avatar billede bennytordrup Nybegynder
07. november 2001 - 08:43 #21
Hvis du skal sortere efter tal, så kræver det, at du finder det største/mindste tal først og finder længden på den tekstmæssige repræsentation af det.
Avatar billede japlex Nybegynder
07. november 2001 - 10:47 #22
>> Benny

Jeg er rimelig overbevist om at du er kommet med den bedste løsning, MEN jeg vil bede dig være tålmodig idet jeg i øjeblikket kæmper med vigtigere problemer i forbindelse med udviklingen af mit system

Jeg lover at vende tilbage :-)
Avatar billede bennytordrup Nybegynder
07. november 2001 - 10:51 #23
>> Japlex

Jeg er tålmodigheden selv. Det var bare cole\'s kommentarer, jeg kommenterede :-)
Avatar billede japlex Nybegynder
07. november 2001 - 11:06 #24
>> Benny

OK ;-)
Avatar billede cole Nybegynder
07. november 2001 - 20:40 #25
eg ved ikke hvad Benny.tordrup fisker for japlex det er kun et 15 points spørgsmål.
Hvis du har problemer med hvor mange cifre kundenr. har så sæt 10 nuller i Format funktionen feltet vises jo ikke.
med hensyn til at sortr valgfrit i alle kollonner så kræver det kun 5 linniers programering.

Gå til Listview sub og placer makøren midt i Sub. Klik i kombinationsboksen i kode vinduet og vælg ColumnClick. Der oprettes nu en Private Sub ListView1_columnClick(Byval ColumnHeader As MSComctLib.ColumnHeader).  ListView1 hvis du ikke har omdøbt Listview til et andet navn.

Skriv i sub\'en:
If ListView1.SortOrdre = lvwAscending Then
  ListView1.SortOrder = lvwDescending
Else
  Listview1.SortOrder = lvwAscending
End if

Nu kan du bare trykke på det ønskede Listview1 kollonne hovederne og kollonnen soterer sig først størst mod mindst og trykker man igen så mindst mod størst. Metoden kan også sortere datoer. Hvis du så skjuler kollonnen med de foranstående numre så kan du Starte med at skrive:
If ListView1.Sortkey = 2 Then ListView.Sortkey = 1

Så skulle alle dine problemer være løst.
Avatar billede bennytordrup Nybegynder
08. november 2001 - 08:24 #26
cole >>

Prøv at lave et listview, hvor du tilføjer felterne KundeNr, Navn, Saldo og Sidste køb (dato). I Saldokolonnen har du talværdier over og under 0 (inklusive)

Jeg kunne godt tænke mig at se koden til at sortere kolonne et efter numerisk kundenr, kolonne to efter alfanumerisk, kolonne tre efter beløbet og kolonne fire efter dato - alle kolonner enten stigende eller faldende.
Avatar billede cole Nybegynder
10. november 2001 - 09:32 #27
benny.tordrup tag dig lige sammen det har jeg jo svarret på du laver bare et snyde felt hvor dit kunnenummer har foranstillet nuller, men som man ikke kan se. har du brug for flere felter som skal soteres efter alfasystemet så må du lave flere snydefelter som man ikke kan se. I så fald så kan du ikke nøjes med  If Listview1.Sortkey = 2 Then Listview1.Sortkey = 1  men så må du udvide dine betingelser med Select Case  metoden
Du kan kun sorterer et felt ad gangen ved at klikke på listview hovedet, men det var jo også det spørgsmålet gik ud på. benny.tordrup din løsning er brubar men den tangere genopfindelsen af den dybe tallerken.
Avatar billede bennytordrup Nybegynder
12. november 2001 - 08:41 #28
cole>>

Så prøv din løsning med snydekolonner rundt omkring og samtidig tillade kolonneresizing. Det giver dig mulighed for at få vist snydekolonnerne ved at resize kolonnebredderne.

Og nej, der er ikke et resizeevent på listviewet.

Desuden bliver listviewet langsomt, jo flere kolonner, der er i det sammen med mange rækker. En skjult kolonne per kolonne giver et unødvendigt stort forbrug af ressourcer.
Avatar billede cole Nybegynder
13. november 2001 - 06:26 #29
benny.tordrup>>

ListView1.BorderStyle = ccFixedSingle
ListView1.View = lvwReport
ListView1.ColumnHeaders.Add 1, , \"Snyd\",0
ListView1.ColumnHeaders.Add 2, , \"Kundenr\",1500

ListView1.Sorted = True
ListView1.FullRowSelect = True

Nu har du et felt som ikke kan ses og et som kan ses.

Det er kun felter som resikerer at blive sorteret alfanummerisk som med fordel kan kopieres til et string felt med foranstillet nuller. tekst og datofelter sorteres fint uden at man laver snydekoder. Så selv om du har en gammel og langsom PC så skulle det ikke være noget problem.
Avatar billede bennytordrup Nybegynder
13. november 2001 - 08:24 #30
cole >>

Du tager ikke hensyn til, at du kan få vist kolonne \"Snyd\" ved at resize helt ude i venstre side.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester