Solved

columnheader alignment with icon

Posted on 2000-03-12
16
440 Views
Last Modified: 2013-12-25
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?
0
Comment
Question by:chaos_59
  • 10
  • 5
16 Comments
 
LVL 15

Expert Comment

by:ameba
Comment Utility
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
 
LVL 15

Expert Comment

by:ameba
Comment Utility
Oops, you mean columnheader icon. I thought you were adding listsubitem icons.
0
 
LVL 15

Expert Comment

by:ameba
Comment Utility
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
 
LVL 15

Expert Comment

by:ameba
Comment Utility
>Form2_Click
should be Form_Click
0
 
LVL 1

Author Comment

by:chaos_59
Comment Utility
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
 
LVL 1

Author Comment

by:chaos_59
Comment Utility
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
 
LVL 15

Expert Comment

by:ameba
Comment Utility
>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
 
LVL 1

Author Comment

by:chaos_59
Comment Utility
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
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 15

Expert Comment

by:ameba
Comment Utility
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
 
LVL 1

Author Comment

by:chaos_59
Comment Utility
I wish I could use left alignment, but when working with numbers it doesn't look right to have them left aligned.
0
 
LVL 15

Expert Comment

by:ameba
Comment Utility
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
 
LVL 15

Expert Comment

by:ameba
Comment Utility
'lvVMenn' is listview I used. More to come...
0
 
LVL 10

Expert Comment

by:caraf_g
Comment Utility
<ping> ;-)
0
 
LVL 15

Accepted Solution

by:
ameba earned 100 total points
Comment Utility
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
 
LVL 1

Author Comment

by:chaos_59
Comment Utility
Thanks a lot!
0
 
LVL 15

Expert Comment

by:ameba
Comment Utility
Thanks for the points.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

If you have ever used Microsoft Word then you know that it has a good spell checker and it may have occurred to you that the ability to check spelling might be a nice piece of functionality to add to certain applications of yours. Well the code that…
Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now