none
listview columnheader systemarrow nach setzen wieder entfernen? RRS feed

  • Frage

  • Oh, ist mir ja schon peinlich! Schlage mich immer noch mit einer akzeptablen Lösung für meine Sort-Pfeile herum.

    Also, ich bin nun am Probieren mit den Windows-Systemarrows, so eine Darstellung zum sortieren wie beim Explorer.
    Ich verwende den code von rapidleach bzw OsuDevice.

    Pfeil setzen klappt ja gut, auch die listitems sind nun nicht mehr irgendwie eingerückt. Aber ihn wieder zu entfernen klappt nun nicht.

    Friend Sub SetColumnHeaderImage(ByVal lvw As ListView, ByVal Column As Integer, _
                          ByVal ImageAlignment As HorizontalImageAlignment, ByVal ImageIndex As Integer, _
                          ByVal UseWinArrows As Boolean, Optional ByVal imgList As ImageList = Nothing, _
                          Optional ByVal WinArrow As WindowsArrow = Nothing)
             Dim hwnd As IntPtr
             Dim lret As IntPtr
             If UseWinArrows Then
                'The header control includes all columns.
                'Get a handle to the header control.
                hwnd = SendMessage(lvw.Handle, LVM_GETHEADER, 0, 0)
                'When using the Windows Arrows there is no need to add imagelist to the header control.
                Dim col As LVCOLUMN
                col.mask = LVCF_FMT
                col.fmt = WinArrow
                col.iImage = -1
                col.cchTextMax = 0
                col.cx = 0
                col.iOrder = 0
                col.iSubItem = 0
                col.pszText = IntPtr.Zero
                lret = SendMessage(lvw.Handle, LVM_SETCOLUMN, Column, col)
                

    - als winarrow 0 oder nothing zu übergeben nützt nix.
    - in col.fmt = WinArrow + ImageAlignment wird Imagealignement nicht berücksichtigt. hab es daher weggelassen.
    - die iImage-Eigenschaft wird hier gar nicht ausgewertet. Setzen nützt nix.

    Habt Ihr hier eine Idee? (Seltsamerweise wird in den Quellen hier gar nicht drauf eingegangen)

    Gruß und Dank!

    Thomas

    Sonntag, 17. November 2013 14:41

Antworten

  • Hallo,
    übergebe ein 0 für die Pfeilrichtung (siehe das Enum weiter unten):

     SetColumnHeaderImage(ListView1, 0, HorizontalImageAlignment.Left, -1, Nothing, WindowsArrow.NOTHING)

    Ich habe bei dem Aufruf UseWinArrows weg gelassen, da der Parameter bei deinem (verkürzten) Code unnötig ist.

    Hier mein erweitertes Enum:

        Public Enum WindowsArrow
            WIN_ARROW_UP = 1024
            WIN_ARROW_DOWN = 512
    NOTHING = 0'Neu hinzugefügt End Enum
    Da die 0 oftmals der Standard ist habe ich das getestet und es funktionierte. (Unter Windows 8.1 x64 getestet)

    Nun noch für alle Anderen, der fehlende Code stammt von hier:
    http://social.msdn.microsoft.com/Forums/vstudio/en-US/60200f5a-5612-4322-934a-8016ee99d2b1/how-to-add-remove-listview-columnheader-sort-indicator-win-vista-vb-2008?forum=vbgeneralich%20f%C3%A4nde%20es%20aber%20schade,%20wen


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Sonntag, 17. November 2013 16:38
    Moderator
  • Hallo Thomas,

    ich habe da etwas aufgeräumt und ergänzt. Vorläufig ist dabei herausgekommen - weitere Tests Deinerseits sind empfehlenswert:

    ''' <summary>Anzeige der Sortierung im ListView ColumnHeader</summary>
    Public Enum ColumnHeaderArrow
        ''' <summary>kein Pfeil.</summary>
        None = 0
        ''' <summary>Pfeil nach oben.</summary>
        Up = 1
        ''' <summary>Pfeil nach unten.</summary>
        Down = 2
    End Enum
    
    Public Module ListViewNativeMethods
        ''' <summary>
        ''' Setzt oder löscht die Anzeige eines Pfeiles in einer ListView Überschrift
        ''' </summary>
        ''' <param name="header">Die Spalte.</param>
        ''' <param name="order">Die Sortierung.</param>
        Public Sub SetColumnHeaderArrow(header As ColumnHeader, order As ColumnHeaderArrow)
            Dim lvw = header.ListView
            If lvw.IsHandleCreated Then
                'Dim hwnd As IntPtr = SendMessage(lvw.Handle, LVM_GETHEADER, 0, 0)
    
                Dim column As LVCOLUMN
                column.mask = LVCF_FMT 'Or LVCF_IMAGE
                Select Case header.TextAlign
                    Case HorizontalAlignment.Right
                        column.fmt = LVCFMT_RIGHT
                    Case HorizontalAlignment.Center
                        column.fmt = LVCFMT_CENTER
                    Case Else
                        column.fmt = LVCFMT_LEFT
                End Select
                If order = ColumnHeaderArrow.Up Then
                    column.fmt = column.fmt Or HDF_SORTUP
                ElseIf order = ColumnHeaderArrow.Down Then
                    column.fmt = column.fmt Or HDF_SORTDOWN
                End If
                column.iImage = 0
                SendMessage(lvw.Handle, LVM_SETCOLUMN, header.Index, column)
            End If
        End Sub
    
        ''' <summary>
        ''' Liefert die Anzeige eines Pfeiles in einer ListView Überschrift
        ''' </summary>
        ''' <param name="header">Die Spalte.</param>
        ''' <returns>Die Sortierung.</returns>
        Public Function GetColumnHeaderArrow(header As ColumnHeader) As ColumnHeaderArrow
            Dim lvw = header.ListView
            If lvw.IsHandleCreated Then
                'Dim hwnd As IntPtr = SendMessage(lvw.Handle, LVM_GETHEADER, 0, 0)
    
                Dim column As LVCOLUMN
                column.mask = LVCF_FMT
                If SendMessage(lvw.Handle, LVM_GETCOLUMN, header.Index, column) <> IntPtr.Zero Then
                    If (column.fmt And HDF_SORTDOWN) = HDF_SORTDOWN Then
                        Return ColumnHeaderArrow.Down
                    ElseIf (column.fmt And HDF_SORTUP) = HDF_SORTUP Then
                        Return ColumnHeaderArrow.Up
                    End If
                End If
            End If
            Return ColumnHeaderArrow.None
        End Function
    
        Const LVM_GETCOLUMN = 4121
        Const LVM_SETCOLUMN = 4122
        Const LVM_GETHEADER = 4127
        Const LVCF_FMT = 1
        Const LVCF_IMAGE = 16
        Const LVCFMT_IMAGE = 2048
    
        ' Ausrichtung 
        Const LVCFMT_LEFT As Integer = &H0
        Const LVCFMT_RIGHT As Integer = &H1
        Const LVCFMT_CENTER As Integer = &H2
    
        ' Sortierung
        Const HDF_SORTUP As Integer = &H400
        Const HDF_SORTDOWN As Integer = &H200
    
        ' http://msdn.microsoft.com/en-us/library/windows/desktop/bb774743%28v=vs.85%29.aspx
        <StructLayout(LayoutKind.Sequential, Charset:=CharSet.Auto)>
        Private Structure LVCOLUMN
            Public mask As UInteger
            Public fmt As Integer
            Public cx As Integer
            Public pszText As IntPtr
            Public cchTextMax As Integer
            Public iSubItem As Integer
            Public iImage As Integer
            Public iOrder As Integer
        End Structure
    
        <DllImport("User32.dll", CharSet:=CharSet.Auto)>
        Private Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
        End Function
    
        <DllImport("User32", CharSet:=CharSet.Auto)>
        Private Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As Integer, ByRef lParam As LVCOLUMN) As IntPtr
        End Function
    End Module
    

    SetColumnHeaderArrow setzt dabei den Pfeil und verwendet das eingestellte Alignment weiter.

    GetColumnHeaderArrow liefert die eingestellte Sortierung, der jeweiligen Spalte.

    Beide erwarten den ColumnHeader - was die Zahl der Parameter reduziert und es auch einfacher nutzbar macht.

    Als Test via ColumnClick Ereignis:

        Private PreviousColumn As Integer = -1
    
        Private Sub ListView1_ColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) _
            Handles ListView1.ColumnClick
            Dim lvw = DirectCast(sender, ListView)
            If PreviousColumn <> e.Column Then
                If PreviousColumn >= 0 Then
                    ' Löscht Sortierung der vorherigen Spalte 
                    ListViewNativeMethods.SetColumnHeaderArrow(lvw.Columns(PreviousColumn), ColumnHeaderArrow.None)
                End If
            End If
            PreviousColumn = e.Column
            Dim columnArrow = GetColumnHeaderArrow(lvw.Columns(PreviousColumn))
            If columnArrow = ColumnHeaderArrow.None Then
                columnArrow = ColumnHeaderArrow.Down
            ElseIf columnArrow = ColumnHeaderArrow.Down Then
                columnArrow = ColumnHeaderArrow.Up
            Else
                columnArrow = ColumnHeaderArrow.None
            End If
            ListViewNativeMethods.SetColumnHeaderArrow(lvw.Columns(PreviousColumn), columnArrow)
            System.Diagnostics.Debug.WriteLine("ColumnClick {0} / {1}", e.Column, columnArrow)
        End Sub
    

    Etwas vorläufig ist das immer Löschen der Sortierung für die vorherige Spalte.

    Wobei denkbar wäre, die ColumnHeader Klasse zu erweitern, um das permanent zur Verfügung zu haben, braucht aber wiederum einen Designer etc. - bin ich jetzt aber erst einmal nicht angegangen.

    Gruß Elmar

    Sonntag, 17. November 2013 17:17
    Beantworter

Alle Antworten

  • Hallo,
    übergebe ein 0 für die Pfeilrichtung (siehe das Enum weiter unten):

     SetColumnHeaderImage(ListView1, 0, HorizontalImageAlignment.Left, -1, Nothing, WindowsArrow.NOTHING)

    Ich habe bei dem Aufruf UseWinArrows weg gelassen, da der Parameter bei deinem (verkürzten) Code unnötig ist.

    Hier mein erweitertes Enum:

        Public Enum WindowsArrow
            WIN_ARROW_UP = 1024
            WIN_ARROW_DOWN = 512
    NOTHING = 0'Neu hinzugefügt End Enum
    Da die 0 oftmals der Standard ist habe ich das getestet und es funktionierte. (Unter Windows 8.1 x64 getestet)

    Nun noch für alle Anderen, der fehlende Code stammt von hier:
    http://social.msdn.microsoft.com/Forums/vstudio/en-US/60200f5a-5612-4322-934a-8016ee99d2b1/how-to-add-remove-listview-columnheader-sort-indicator-win-vista-vb-2008?forum=vbgeneralich%20f%C3%A4nde%20es%20aber%20schade,%20wen


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Sonntag, 17. November 2013 16:38
    Moderator
  • Hallo Thomas,

    ich habe da etwas aufgeräumt und ergänzt. Vorläufig ist dabei herausgekommen - weitere Tests Deinerseits sind empfehlenswert:

    ''' <summary>Anzeige der Sortierung im ListView ColumnHeader</summary>
    Public Enum ColumnHeaderArrow
        ''' <summary>kein Pfeil.</summary>
        None = 0
        ''' <summary>Pfeil nach oben.</summary>
        Up = 1
        ''' <summary>Pfeil nach unten.</summary>
        Down = 2
    End Enum
    
    Public Module ListViewNativeMethods
        ''' <summary>
        ''' Setzt oder löscht die Anzeige eines Pfeiles in einer ListView Überschrift
        ''' </summary>
        ''' <param name="header">Die Spalte.</param>
        ''' <param name="order">Die Sortierung.</param>
        Public Sub SetColumnHeaderArrow(header As ColumnHeader, order As ColumnHeaderArrow)
            Dim lvw = header.ListView
            If lvw.IsHandleCreated Then
                'Dim hwnd As IntPtr = SendMessage(lvw.Handle, LVM_GETHEADER, 0, 0)
    
                Dim column As LVCOLUMN
                column.mask = LVCF_FMT 'Or LVCF_IMAGE
                Select Case header.TextAlign
                    Case HorizontalAlignment.Right
                        column.fmt = LVCFMT_RIGHT
                    Case HorizontalAlignment.Center
                        column.fmt = LVCFMT_CENTER
                    Case Else
                        column.fmt = LVCFMT_LEFT
                End Select
                If order = ColumnHeaderArrow.Up Then
                    column.fmt = column.fmt Or HDF_SORTUP
                ElseIf order = ColumnHeaderArrow.Down Then
                    column.fmt = column.fmt Or HDF_SORTDOWN
                End If
                column.iImage = 0
                SendMessage(lvw.Handle, LVM_SETCOLUMN, header.Index, column)
            End If
        End Sub
    
        ''' <summary>
        ''' Liefert die Anzeige eines Pfeiles in einer ListView Überschrift
        ''' </summary>
        ''' <param name="header">Die Spalte.</param>
        ''' <returns>Die Sortierung.</returns>
        Public Function GetColumnHeaderArrow(header As ColumnHeader) As ColumnHeaderArrow
            Dim lvw = header.ListView
            If lvw.IsHandleCreated Then
                'Dim hwnd As IntPtr = SendMessage(lvw.Handle, LVM_GETHEADER, 0, 0)
    
                Dim column As LVCOLUMN
                column.mask = LVCF_FMT
                If SendMessage(lvw.Handle, LVM_GETCOLUMN, header.Index, column) <> IntPtr.Zero Then
                    If (column.fmt And HDF_SORTDOWN) = HDF_SORTDOWN Then
                        Return ColumnHeaderArrow.Down
                    ElseIf (column.fmt And HDF_SORTUP) = HDF_SORTUP Then
                        Return ColumnHeaderArrow.Up
                    End If
                End If
            End If
            Return ColumnHeaderArrow.None
        End Function
    
        Const LVM_GETCOLUMN = 4121
        Const LVM_SETCOLUMN = 4122
        Const LVM_GETHEADER = 4127
        Const LVCF_FMT = 1
        Const LVCF_IMAGE = 16
        Const LVCFMT_IMAGE = 2048
    
        ' Ausrichtung 
        Const LVCFMT_LEFT As Integer = &H0
        Const LVCFMT_RIGHT As Integer = &H1
        Const LVCFMT_CENTER As Integer = &H2
    
        ' Sortierung
        Const HDF_SORTUP As Integer = &H400
        Const HDF_SORTDOWN As Integer = &H200
    
        ' http://msdn.microsoft.com/en-us/library/windows/desktop/bb774743%28v=vs.85%29.aspx
        <StructLayout(LayoutKind.Sequential, Charset:=CharSet.Auto)>
        Private Structure LVCOLUMN
            Public mask As UInteger
            Public fmt As Integer
            Public cx As Integer
            Public pszText As IntPtr
            Public cchTextMax As Integer
            Public iSubItem As Integer
            Public iImage As Integer
            Public iOrder As Integer
        End Structure
    
        <DllImport("User32.dll", CharSet:=CharSet.Auto)>
        Private Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
        End Function
    
        <DllImport("User32", CharSet:=CharSet.Auto)>
        Private Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As Integer, ByRef lParam As LVCOLUMN) As IntPtr
        End Function
    End Module
    

    SetColumnHeaderArrow setzt dabei den Pfeil und verwendet das eingestellte Alignment weiter.

    GetColumnHeaderArrow liefert die eingestellte Sortierung, der jeweiligen Spalte.

    Beide erwarten den ColumnHeader - was die Zahl der Parameter reduziert und es auch einfacher nutzbar macht.

    Als Test via ColumnClick Ereignis:

        Private PreviousColumn As Integer = -1
    
        Private Sub ListView1_ColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) _
            Handles ListView1.ColumnClick
            Dim lvw = DirectCast(sender, ListView)
            If PreviousColumn <> e.Column Then
                If PreviousColumn >= 0 Then
                    ' Löscht Sortierung der vorherigen Spalte 
                    ListViewNativeMethods.SetColumnHeaderArrow(lvw.Columns(PreviousColumn), ColumnHeaderArrow.None)
                End If
            End If
            PreviousColumn = e.Column
            Dim columnArrow = GetColumnHeaderArrow(lvw.Columns(PreviousColumn))
            If columnArrow = ColumnHeaderArrow.None Then
                columnArrow = ColumnHeaderArrow.Down
            ElseIf columnArrow = ColumnHeaderArrow.Down Then
                columnArrow = ColumnHeaderArrow.Up
            Else
                columnArrow = ColumnHeaderArrow.None
            End If
            ListViewNativeMethods.SetColumnHeaderArrow(lvw.Columns(PreviousColumn), columnArrow)
            System.Diagnostics.Debug.WriteLine("ColumnClick {0} / {1}", e.Column, columnArrow)
        End Sub
    

    Etwas vorläufig ist das immer Löschen der Sortierung für die vorherige Spalte.

    Wobei denkbar wäre, die ColumnHeader Klasse zu erweitern, um das permanent zur Verfügung zu haben, braucht aber wiederum einen Designer etc. - bin ich jetzt aber erst einmal nicht angegangen.

    Gruß Elmar

    Sonntag, 17. November 2013 17:17
    Beantworter
  • Hallo Tom

    0 hatte ich ja schon übergeben und hat nicht funktioniert. Habe daher nochmal meinen code überprüft und gemerkt, dass ich versehntlich die neue anstatt die alte Spalte übergeben hatte. Ich schiebs mal aufs Alter...

    Ganz herzlichen Dank!

    Montag, 18. November 2013 15:47
  • Uff Elmar, da hast Du Dich aber verdient gemacht.

    Ich denke, da werden noch viele von profitieren.

    VIELEN DANK!

    TH

    Montag, 18. November 2013 15:47