Use LVM_APPROXIMATEVIEWRECT to calculate the size of a ListView control

Put simple Tips and Tricks that are not entire Tutorials in this forum
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Use LVM_APPROXIMATEVIEWRECT to calculate the size of a ListView control

08 Jan 2018, 23:05

Introduction
The LVM_APPROXIMATEVIEWRECT message has been around forever but most developers don't use it. Some may have given it a try but abandoned it because it didn't return the values they wanted. Unfortunately, Microsoft dropped the ball on this one. There is no instruction manual or useful documentation on how to use this message correctly. See the Considerations section for more stuff.

The LVM_APPROXIMATEVIEWRECT message is used to calculate the approximate width and height of a ListView control required to display a given number of rows. Hopefully this document and these examples will help to use this message more effectively.

Stuff to Know
The LVM_APPROXIMATEVIEWRECT message can only be used on an existing ListView control. The reason for this is important to understand. The font, column header (or lack of one), icon size, number of rows, number of columns, and the width of each column, etc. all factor into the size of the ListView control and whether or not vertical and/or horizontal scroll bars are showing. Accurately calculating the size of the ListView control would be impossible without having all of this information.

In order to get accurate results, the developer must:
  1. Create the ListView control. The ListView control must have all the options, styles, and attributes that will be set when the control is displayed. This includes the font, column header, image list, etc.
  2. Populate the ListView control. This step is optional if the column width can be set without real data or if the LVM_APPROXIMATEVIEWRECT message is only used to calculate the height of the control.
  3. Set the width of the columns. The step is optional if the LVM_APPROXIMATEVIEWRECT message is only used to calculate the height of the control.
With these requirements and a few tips and tricks, the LVM_APPROXIMATEVIEWRECT message can be used to generate very accurate measurements.

Examples
To avoid posting a bunch of code, the examples and function are attached to this post in a Zip file.

Considerations
A few considerations:
  • Trial & Error. The documentation for the LVM_APPROXIMATEVIEWRECT message is very limited. Worse yet, examples out in the wild are mostly nonexistent. For these reasons, the stuff in this document and the examples is mostly from trial and error. If you run into any problems, let me know.
  • Report Only. The LVM_APPROXIMATEVIEWRECT message only appears to work with a ListView control in Report mode. Values returned while in other view modes are worthless.
  • Proposed Dimensions. The LVM_APPROXIMATEVIEWRECT message has a parameter (lParam) to enter a "proposed" size. I have never figured out how to get the "proposed" feature to work. Setting this parameter to any value does nothing different. The feature may have never been implemented or it may be my version of Windows (Win7). If anyone has figured out how to use this parameter, please let me know.
References
Credit
Some of the adjustments to the input/output of the LVM_APPROXIMATEVIEWRECT message were extracted from the AutoHotkey source. I've never seen these adjustments documented anywhere else so I would guess that they were created from trial and error.
Attachments
Examples_v1.0.zip
(5.63 KiB) Downloaded 297 times
User avatar
jballi
Posts: 724
Joined: 29 Sep 2013, 17:34

Re: Use LVM_APPROXIMATEVIEWRECT to calculate the size of a ListView control

18 Dec 2018, 21:31

I finally got around to creating a function that calculates the size of a ListView control using the AutoHotkey method described in the original post.

Code: Select all

;------------------------------
;
; Function: LVM_CalculateSize
;
; Description:
;
;   Calculate the width and height required to display a given number of rows of
;   a ListView control.
;
; Parameters:
;
;   p_NumberOfRows - The number of rows to be displayed in the control.  Set to
;       -1 (the default) to use the current number of rows in the ListView
;       control.
;
;   r_Width, r_Height - [Output, Optional] The calculated width and height of
;       ListView control.
;
; Returns:
;
;   An integer that holds the calculated width (in the LOWORD) and height (in
;   the HIWORD) needed to display the rows, in pixels.
;
;   If the output variables are defined (r_Width and r_Height), the calculated
;   values are also returned in these variables.
;
; The AutoHotkey Method:
;
;   This function uses the LVM_APPROXIMATEVIEWRECT message to calculate the
;   approximate width and height required to display a given number of rows in a
;   ListView control.  The AutoHotkey method (extracted from the AutoHotkey
;   source) makes minor changes to the data that is passed to the message and to
;   the results that are returned from the message.
;
;   The AutoHotkey method is the following.
;
;   _Input_: The actual or requested number of row is used minus 1.  For
;   example, if 10 rows is requested, 9 is passed to the LVM_APPROXIMATEVIEWRECT
;   message instead.
;
;   _Output_: 4 is added to both the width and height return values.  For
;   example, if the message returned a size of 300x200, the size is adjusted to
;   304x204.
;
;   The final result (in most cases) is a ListView control that is the exact
;   size needed to show all of the specified rows and columns without showing
;   the horizontal or vertical scroll bars.  Exception: If the requested number
;   of rows is less than the actual number of rows, the horizontal and/or
;   vertical scroll bars may show as a result.
;
; Remarks:
;
;   This function should only be used on a ListView control in the Report view.
;
;-------------------------------------------------------------------------------
LVM_CalculateSize(hLV,p_NumberOfRows:=-1,ByRef r_Width:="",ByRef r_Height:="")
    {
    Static Dummy67950827

          ;-- Messages
          ,LVM_GETITEMCOUNT       :=0x1004              ;-- LVM_FIRST + 4
          ,LVM_APPROXIMATEVIEWRECT:=0x1040              ;-- LVM_FIRST + 64

    ;-- Collect and/or adjust the number of rows
    if (p_NumberOfRows<0)
        {
        SendMessage LVM_GETITEMCOUNT,0,0,,ahk_id %hLV%
        p_NumberOfRows:=ErrorLevel
        }

    if p_NumberOfRows  ;-- Not zero
        p_NumberOfRows-=1

    ;-- Calculate size
    SendMessage LVM_APPROXIMATEVIEWRECT,p_NumberOfRows,-1,,ahk_id %hLV%

    ;-- Extract, adjust, and return values
    r_Width :=(ErrorLevel&0xFFFF)+4 ;-- LOWORD
    r_Height:=(ErrorLevel>>16)+4    ;-- HIWORD
    Return r_Height<<16|r_Width
    }

I hope that someone finds this useful.
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Use LVM_APPROXIMATEVIEWRECT to calculate the size of a ListView control

24 Apr 2022, 22:32

Report Only. The LVM_APPROXIMATEVIEWRECT message only appears to work with a ListView control in Report mode. Values returned while in other view modes are worthless.
Thanks for ending my wild goose chase. :)

Fortunately, using LVM_GETITEMRECT to retrieve the bounding rectangle of the last item (and then add a fixed amount for padding) was a sufficient workaround in my case.

In either case, it is necessary to compensate for A_ScreenDPI if you're using the values with coordinates passed to/received from the Gui commands/methods and haven't used -DPIScale.

Return to “Tips and Tricks (v1)”

Who is online

Users browsing this forum: No registered users and 12 guests