One moment please

We are aware that some users are experiencing problems seeing the contents of some forums. Technicians are working on it, but it may not be fixed until tomorrow. Sorry for the inconvenience!
See more
See less

ListView_ [ SetColumnWidth / GetColumnWidth ]

  • Filter
  • Time
  • Show
Clear All
new posts

  • ListView_ [ SetColumnWidth / GetColumnWidth ]

    I have a thing for neatness of the user interface. Sometimes my code don't seem that way, but that's a different story. One of the things that bugs me is when I design something with a listview in it, is having to set the width of the columns. One can do it manually, and let the user use CTRL+(PlusKey) to call on windows to do this, but why make the user take this step when you can program in a quick function to resize the columns of the listview according to either the width of the header, or the width of the content?

    PB's DTT system for listview's have a very nice set of functions for doing that, setting the column width to either the content, or the header, but they lack a bit of finesse in making it really pretty by determining which is wider. If you want to find out which is wider, you need to get into the API itself with two macros:
    BOOL ListView_SetColumnWidth(
        HWND hwnd,
        int iCol,
        int cx
    int ListView_GetColumnWidth(
        HWND hwnd,
        int iCol
    With the power of these two macros, plus a couple of constants, %LVSCW_AUTOSIZE & LVSCW_AUTOSIZE_USEHEADER, plus two more calls:
    hHeader = ListView_GetHeader(hListView)
    lvhCount = Header_GetItemCount(hHeader)
    One can make an autosizing listview by stepping through the columns and checking which would fit better, the header width, or the content width.

    But we can even take it a step further, in handling a situation where one of the columns, if autosized to fit contents, causes the listview to exceed the right side of the window it's in, forcing the user to use the horizontal scroll bar to see the right hand columns.

    For an example, we have a 3 column listview, where the 2nd column needs to be adjusted to fit the remaining space after resizing the 1st & 3rd. You can't just autosize them then check for remaining space, according to the SDK:
    Automatically sizes the column to fit the header text. If you use this value with the last column, its width is set to fill the remaining width of the list-view control.
    So we need to fit all three columns, then check widths. We'll need a function to avoid code bloat:
    FUNCTION ResizeColumn(hListView AS DWORD, lvhIndex AS LONG, MaxWidth AS LONG) AS LONG
    LOCAL lvFitHeader, lvFitContent, lvhBestFit     AS LONG
        ListView_SetColumnWidth hListView, lvhIndex, %LVSCW_AUTOSIZE_USEHEADER
        lvFitHeader = ListView_GetColumnWidth(hListView, lvhIndex)
        ListView_SetColumnWidth hListView, lvhIndex, %LVSCW_AUTOSIZE
        lvFitContent = ListView_GetColumnWidth(hListView, lvhIndex)
        IF (MaxWidth = 0) THEN
            lvhBestFit = IIF(lvFitContent > lvFitHeader, lvFitContent, lvFitHeader)
            lvhBestFit = IIF(MaxWidth < 0, lvFitHeader, MaxWidth)
        END IF
        ListView_SetColumnWidth hListView, lvhIndex, lvhBestFit
        ResizeColumn = lvhBestFit
    When we use this on each of our columns, we get back the width to which the column was set to, providing we first pass it Zero as the MaxWidth parameter the first time around. So we collect those widths, total them, then check if they exceed the available width of the listview's window. Not the parent container.
    Column0 = ResizeColumn(hListView, 0, 0)
    Column1 = ResizeColumn(hListView, 1, 0)
    Column2 = ResizeColumn(hListView, 2, 0)
    GetClientRect hListView, lvRect
    cMax = lvRect.nRight
    IF (cMax < (Column0 + Column1 + Column2)) THEN
        cMax = cMax - (Column0 + Column2)
        Column1 = ResizeColumn(hListView, 1, cMax)
    END IF
    And TaDa! Our second column gets the remaining width of the listview with the 1st & 3rd columns also nicely presented.

    Of course, you can always skip that IF check and just set the 2nd column to fit remaining space regardless, it's all up to how you want to present things.
    Furcadia, an interesting online MMORPG in which you can create and program your own content.

  • #2
    Originally posted by colin glenn View Post
    ...why make the user take this step when you can program in a quick function to resize the columns of the listview according to either the width of the header, or the width of the content?
    Because it is seldom an ideal approximation.

    An alternative might be to concentrate the effort on making a higher-level specification of the listview, so that you could use a sub something like this:

    setLVcolumns(hListView, "mycol1|L|20,mycol2|R|50,mycol3|C|30") where hListView is the listview window handle, mycol1..3 are the column titles, L|R|C are alignment specifiers and the numerics are the percentage of the total available width. This approach simplifies the handling of resizeable dialogs containing listviews, for example, and of course you need only write it once!


    • #3
      Could probably do that, create a listview object to wrap the listview in like I did for the statusbar.
      Furcadia, an interesting online MMORPG in which you can create and program your own content.


      • #4
        Hey, more using these two calls, after using my above code to initially set the size, what if the user resizes the window containing the listview?

        Well, after all the rest of the resizing action handled in the %WM_SIZE:
        Column0 = ListView_GetColumnWidth(hListView, 0)
        Column2 = ListView_GetColumnWidth(hListView, 2)
        GetClientRect hListView, lvRect
        Column1 = lvRect.nRight - (Column0 + Column2)
        ListView_SetColumnWidth hListView, 1, Column1
        Furcadia, an interesting online MMORPG in which you can create and program your own content.


        • #5
          Hi Colin,
          Reviving this older thread ... I was looking at Outlook to see how they handle resizing of mailbox columns (From/Subject/Date/Size).

          As the app is resized, it seems that the columns maintain a fixed percentage of available width.

          But if the user manually changes the width of a column, the remaining width is proportionately distributed between the remaining, unsized columns - creating a new set of percentages which are used until another manual resize occurs.

          So for a 800p width, the column widths are about this:
          From 15% 120p
          Subj 60% 480p
          Date 15% 120p
          Size 10% 80p

          If the user adjusts the width of From to 200p, the widths become roughly this:
          From 25% 200p
          Subj 55% 420p
          Date 12% 110p
          Size 8% 70p

          By my calculations, the leftover width (800-200) is distributed to the remaining 3 columns in the same proportion as they were before the manual resize took place. That creates a new set of % of available width for each column.

          After manually resizing, the new percentages are maintained/applied when resizing the app.