columnheader alignment with icon

I am using vb6, listview control in report view. I have several columns left justified and several right justified. Here's the problem. If I add an icon at design time all of the columns align correctly, but if I add or change an icon at run time the affected column becomes left justified regardless of how I set the alignment. I have tried resetting the alignment right after the icon is added but it doesn't work. Is this a bug? or am I doing something wrong?
LVL 1
chaos_59Asked:
Who is Participating?
 
amebaCommented:
OK, here is the sample (version 0.1)

' ----------------------------------------------------------
' module code
Option Explicit
' APIs
Public Declare Function LockWindowUpdate Lib _
        "user32" (ByVal hWndLock As Long) As Long
Public Declare Sub InvalidateRect Lib "user32" (ByVal hwnd As Long, ByVal t As Long, ByVal bErase As Long)
Public Declare Sub ValidateRect Lib "user32" (ByVal hwnd As Long, ByVal t As Long)
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Const WM_SETREDRAW = &HB
Public Const REDRAWOFF              As Long = 0
Public Const REDRAWON               As Long = 1
' for listview
Public Const LVCF_FMT = &H1
Public Const LVCF_IMAGE = &H10
Public Const LVCFMT_LEFT = &H0
Public Const LVCFMT_RIGHT = &H1
Public Const LVCFMT_CENTER = &H2
Public Const LVCFMT_JUSTIFYMASK = &H3
Public Const LVCFMT_IMAGE = &H800
Public Const LVCFMT_BITMAP_ON_RIGHT = &H1000
Public Const LVCFMT_COL_HAS_IMAGES = &H8000&
Public Const LVM_FIRST = &H1000
#If UNICODE Then
Public Const LVM_GETCOLUMN = (LVM_FIRST + 95)
Public Const LVM_SETCOLUMN = (LVM_FIRST + 96)
#Else
Public Const LVM_GETCOLUMN = (LVM_FIRST + 25)
Public Const LVM_SETCOLUMN = (LVM_FIRST + 26)
#End If
Public Type LVCOLUMN
    mask As Long
    fmt As Long
    cx As Long
    pszText As Long
    cchTextMax As Long
    iSubItem As Long
    iImage As Long
    iOrder As Long
End Type

Public Function ListViewGetColumn(ahWnd As Long, iCol As Long, pcol As LVCOLUMN) As Boolean
    ListViewGetColumn = SendMessage(ahWnd, LVM_GETCOLUMN, ByVal iCol, pcol)
End Function
Public Function ListViewSetColumn(ahWnd As Long, iCol As Long, pcol As LVCOLUMN) As Boolean
    ListViewSetColumn = SendMessage(ahWnd, LVM_SETCOLUMN, ByVal iCol, pcol)
End Function

' Purpose:  Modifies columnheader icon (listview control)
' Returns:  True if successfull
' Arguments:
'   ctlHwnd - hWnd property of the listview control
'   Column  - 1-based index of column
'   NewIcon - index of icon in ColumnHeaderIcons imagelist
'   ForceRepaint - if True, it'll use ctl.Refresh
Public Function SetColumnIcon(ctl As MSComctlLib.ListView, Column As Long, Optional NewIcon As Long, Optional ForceRepaint As Boolean = False) As Boolean
    Dim lvc As LVCOLUMN, ret As Boolean
    Dim o As MSComctlLib.ColumnHeader, savalignment As MSComctlLib.ListColumnAlignmentConstants
    On Error GoTo EH
   
    Set o = ctl.ColumnHeaders.Item(Column)
    savalignment = o.Alignment
' ----------------------------------------------------------
    ' Set new icon
    If Not IsMissing(NewIcon) Then
        ' I cannot do this the API way
        o.Icon = NewIcon   ' this will also reset column alignment to left
    End If
' ----------------------------------------------------------
    ' read columnformat into lvc.fmt
    lvc.mask = LVCF_FMT ' or LVCF_IMAGE
    ret = ListViewGetColumn(ctl.hwnd, Column - 1, lvc)
    Debug.Print "column style: H" & Hex(lvc.fmt) & " image"; lvc.iImage
   
    ' set columnformat in lvc.fmt
    lvc.fmt = lvc.fmt Or savalignment
    ret = ListViewSetColumn(ctl.hwnd, Column - 1, lvc)
    o.Alignment = savalignment ' update vb property, do we need this?
' ----------------------------------------------------------
    If ForceRepaint Then ctl.Refresh
    SetColumnIcon = True
    Exit Function
   
EH:
End Function


' ----------------------------------------------------------
' Form1
' add listview and imagelist
' imagelist1 has 2 icons: arrow up (index=1) and down (index=2)
Option Explicit

Private Sub Form_Load()
    Dim i As Long, j As Long
    InitLV
    ' load list
    With ListView1.ListItems
        For i = 1 To 100
            With .Add(, , "Item " & i)
                For j = 1 To 4
                    .SubItems(j) = "Itm" & i & " Col" & j + 1
                Next
            End With
        Next
    End With
End Sub

Public Function InitLV() As Boolean
    On Error GoTo EH
    Dim rStyle As Long, r As Long
    With ListView1
        ' imagelist1 has 2 icons: arrow up (index=1) and down (index=2)
        .ColumnHeaderIcons = ImageList1
        With .ColumnHeaders
            .Add 1, , "first", 975, 0
            SetColumnIcon Me.ListView1, 1, 1 ' set icon for column 1
            .Add 2, , "right", 1215, 1
            .Add 3, , "left", 1215, 0
            .Add 4, , "center", 1215, 2
            .Add 5, , "left", 1215, 0
        End With
        .View = lvwReport
    End With
    InitLV = True
    Exit Function

EH:
    MsgBox Err.Description
    Resume Next
End Function

Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader)
    ' disable repaint for the whole form
    Call SendMessage(hwnd, WM_SETREDRAW, REDRAWOFF, 0&)
    With Me.ListView1
        '-----------------------------
        '.ColumnHeaders(.SortKey + 1).Icon = 0
        SetColumnIcon Me.ListView1, .SortKey + 1, 0, False
        '-----------------------------
        If Not ColumnHeader.Index = .SortKey + 1 Then
            .SortOrder = lvwAscending
        Else
            If .SortOrder = lvwAscending Then
                .SortOrder = lvwDescending
            Else
                .SortOrder = lvwAscending
            End If
        End If
        '-----------------------------
        'ColumnHeader.Icon = .SortOrder + 1
        SetColumnIcon Me.ListView1, ColumnHeader.Index, .SortOrder + 1, False
        '-----------------------------
        .SortKey = ColumnHeader.Index - 1
        .Sorted = True
        ' enable repainting
        Call SendMessage(hwnd, WM_SETREDRAW, REDRAWON, 0&)
       
        Call InvalidateRect(.hwnd, 0&, 0&)  ' or use more smaller rectangles, form.clipcontrols set to false
    End With
End Sub
0
 
amebaCommented:
First column can be only left aligned.
>If I add an icon at design time ...
How do you do that? I can only add items at run-time.
0
 
amebaCommented:
Oops, you mean columnheader icon. I thought you were adding listsubitem icons.
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

 
amebaCommented:
It is a bug. Alignment will be reset to left. VB property will still show previous, right aligment, and it will think it doesn't need to change it.
Here is the workaround:

Private Sub Form2_Click()
    Dim savalignment As Integer
    With lvwTest.ColumnHeaders(2)
        savalignment = .Alignment
        .Alignment = lvwColumnLeft
        .Icon = 0
        .Alignment = savalignment ' restore alignment
    End With
End Sub
0
 
amebaCommented:
>Form2_Click
should be Form_Click
0
 
chaos_59Author Commented:
well, this works to reset the alignment, but it also removes the icon. It seems like maybe alignment and icon are mutually exclusive. If you have any other ideas let me know.
           Thanks
0
 
chaos_59Author Commented:
By the way, how did you know this was a bug? I assumed it was by how it was behaving, is there some resource where I can look for bugs and stuff?


0
 
amebaCommented:
>but it also removes the icon
Yes in my sample, I also tried
   .Icon = 2
with the same result.

>but if I add or change an icon
I don't see how to test changing columnheader icon, if I don't use that line. Of course, icon will be modified, you wanted this, you said:
>but if I add or change an icon

>how did you know this was a bug?
Bug is if something doesn't work OK. I am not saying this is PUBLISHED BUG.

Bug, how to reproduce:
If second column is right aligned, and column header has an icon, this line
    lvwTest.ColumnHeaders(2).Icon = 0
will also change column alignment to left.

Workaround:
after executing line ".Icon = 0"
we can check column alignment:
  debug.print lvwTest.ColumnHeaders(2).Alignment
the result is 1 (right aligned), but we see it is left aligned.
This means property Alignment, exposed to VB developers, has not been updated, i.e. it shows wrong value. To reset alignment to Right, this single line is not enough:
  lvwTest.ColumnHeaders(2).Alignment = lvwColumnRight
because vb 'thinks' it already is 'right aligned'

So, we have to change alignment twice:
Private Sub Form_Click()
    Dim savalignment As Integer
    With lvwTest.ColumnHeaders(2)
        savalignment = .Alignment
        .Alignment = lvwColumnLeft
        .Icon = 0
        .Alignment = savalignment ' restore alignment
    End With
End Sub

I am 100% sure this is the answer!
0
 
chaos_59Author Commented:
I created a test app to try your sample and it doesn't work if you try to set the icon to another icon instead of 0.
here is my code.    
Dim savalignment As Integer
    With lvwtest.ColumnHeaders(2)
        savalignment = .Alignment
        .Alignment = lvwColumnLeft
        .Icon = 2
        .Alignment = savalignment ' restore alignment
    End With

I stepped through the code line by line and when it reaches the ".Alignment = savalignment" statement the icon disappears.
I want my program to be able to toggle the icon between an up arrow and a down arrow depending on if the list is ascending or descending by column.
0
 
amebaCommented:
Sorry, I re-tested what I said and it works only when " .Icon = 0" is used.
It seems I only tested ".Icon = 2" when reproducing the bug.
So, I am withdrawing  my answer.

I'll try to find another workaround (e.g. SendMessage might work).

My examples for changing the columnheader icon when sorting column, all use only left alignment.
0
 
chaos_59Author Commented:
I wish I could use left alignment, but when working with numbers it doesn't look right to have them left aligned.
0
 
amebaCommented:
I have something working; this will modify icon in column 2, and make it right aligned

' in form declarations
Private Const LVCF_FMT = &H1
Private Const LVCFMT_LEFT = &H0
Private Const LVCFMT_RIGHT = &H1
Private Const LVCFMT_CENTER = &H2
Private Const LVCFMT_JUSTIFYMASK = &H3
Private Const LVCFMT_IMAGE = &H800
Private Const LVCFMT_BITMAP_ON_RIGHT = &H1000
Private Const LVCFMT_COL_HAS_IMAGES = &H8000&
Private Const LVM_FIRST = &H1000
#If UNICODE Then
Private Const LVM_GETCOLUMN = (LVM_FIRST + 95)
Private Const LVM_SETCOLUMN = (LVM_FIRST + 96)
#Else
Private Const LVM_GETCOLUMN = (LVM_FIRST + 25)
Private Const LVM_SETCOLUMN = (LVM_FIRST + 26)
#End If
Private Type LVCOLUMN
    mask As Long
    fmt As Long
    cx As Long
    pszText As Long
    cchTextMax As Long
    iSubItem As Long
    iImage As Long
    iOrder As Long
End Type
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Private Function ListViewGetColumn(ahWnd As Long, iCol As Long, pcol As LVCOLUMN) As Boolean
    ListViewGetColumn = SendMessage(ahWnd, LVM_GETCOLUMN, ByVal iCol, pcol)
End Function
Private Function ListViewSetColumn(ahWnd As Long, iCol As Long, pcol As LVCOLUMN) As Boolean
    ListViewSetColumn = SendMessage(ahWnd, LVM_SETCOLUMN, ByVal iCol, pcol)
End Function


' this will modify icon in column colno and make it right-aligned
Private Sub Form_Click()
    Dim newicon As Integer, colno As Long
    newicon = 2
    colno = 2
' ----------------------------------------------------------
    Dim o As ColumnHeader
    Set o = Me.lvVMenn.ColumnHeaders.Item(colno)
    o.Icon = newicon   ' this will also reset column alignment to left
' ----------------------------------------------------------
    Dim lvc As LVCOLUMN, ret As Boolean
    ' read columnformat into lvc.fmt
    lvc.mask = LVCF_FMT
    ret = ListViewGetColumn(Me.lvVMenn.hWnd, colno - 1, lvc)
    Debug.Print "column style: H" & Hex(lvc.fmt)
    ' set columnformat in lvc.fmt
    lvc.fmt = lvc.fmt Or LVCFMT_RIGHT ' make it right aligned
    ret = ListViewSetColumn(Me.lvVMenn.hWnd, colno - 1, lvc)
    Me.lvVMenn.Refresh
' ----------------------------------------------------------
End Sub
0
 
amebaCommented:
'lvVMenn' is listview I used. More to come...
0
 
caraf_gCommented:
<ping> ;-)
0
 
chaos_59Author Commented:
Thanks a lot!
0
 
amebaCommented:
Thanks for the points.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.