The ListView is a Common Control that has been shipping with Windows for a very long time, in both the Window NT and 9x flavours. Indeed, Windows (File) Explorer uses a ListView control, which you can see by using the Inspect tool from the Windows SDK:
Because the ListView control has been shipping with Windows for such a long time, deploying programs that use it are simplified, and so, desirable. However, using it for anything but it’s original use cases will have you run into some (perhaps unexpected) problems.
For example, if you want a lightweight, read-only grid control, your well within it’s capabilities by setting it to “report-view”, adding columns, switching on grid lines, etc. However, if you want to take it that one step further and allow in-place editing of each “cell”, your now outside it’s capabilities.
It’s true that the ListView is capable of editing the leftmost “cell” otherwise known as the “Item”. Those who are familiar with using Windows (File) Explorer should well know this – rename file. But in-place editing of the remaining “cells”, otherwise known as sub-items, is not one of the ListView’s built-in capabilities.
In theory, however, all you need is some “hit” testing an editing control (such as TextBox), some positioning and event handling code and your set – familiar last words, I’m sure.
Note: What I’m doing here is obviously an educational exercise. I like doing stuff like this myself every so often so I can better understand the problem space, the terminology, the tools, etc. Downloading or Copying and pasting code is not the point of the exercise. It’s also unfortunate that had to be said…
So here is the first thing I ran into, and will be the primary focus of this post:
In the shot above, I’m in the process of overlaying a text box over the second item’s sub-item. Or in other words, the second cell of the “Unformatted String” item. The ListView has been set to Report-view, Header’s set to None, Gridlines enabled, etc. The ListView has also been inset on the Form to accentuate the problem, at least to those that have been made aware of the scenario.
The Textbox has no boarders and a default (white) background. It is not a child of, or contained by the ListView, so you can see that the Textbox’s white background extends past the right bound of the ListView and, indeed, overlays the form’s grey background. The bounds of the Textbox were set from the sub-item’s bounds. Essentially:
textBox1.Bounds = subItem.Bounds;
After examining the Form’s design in Visual Studio, it becomes apparent that my ListView control’s bounds are smaller than the sum of the Column widths that I set (at design-time). After some quiet reflection, it becomes apparent that because the ListView is designed to be scrollable, both horizontally and vertically, it needs to allow column widths whose sum is larger than the ListView’s bounds – so it can scroll. However, because of this, when I ask for the bounds of a sub-item, it is not necessarily returning what I presumed would be it’s on-screen width.
Specifically, with scrollbars disabled, it renders what it can, but it does not auto-sized the column width to remain within the bounds of the ListView. True, you can ask the ListView to auto-size all or specific columns to be the width of the contents of the header or widest (sub)item. But it does not take the scrollable state, or the bounds of the control into consideration. At least, not that I’ve seen or read about.
So let’s do some tinkering, er, investigation. For this, I’m going to use Visual Studio 2013 Express Desktop in C#, .NET 4.51, Winforms and the Inspect tool.
Investigating
First step was to get the details of the various bounds being used, so I set a breakpoint and some watches as seen here:
At line 67, for diagnostic purposes, I get the bounds of the second item, which has a vertical offset and a height of 20 pixels, and a width of 455 pixels.
At line 68, I set the bounds of the Textbox to those of the sub-item. So the Left is 120 pixels in, the top is 20 pixels down, the width is 335 pixels, and height 16 pixels.
Ignoring the discrepancy of the height for the moment (16 instead of 20 pixels), the width happens to be the width I set for the Column at Design time, and not the (presumed) width I see on-screen. However, upon using the SDK’s “inspect” utility, it told (and showed) me that the sub-item’s width was actually within the bounds of the ListView, with a width of 264 pixels (485 – 221), and not the 335 we get in our code:
This result actually lead me into a fruitless debug session into the Winforms framework – though I’ll probably write that up in a separate post. After realising that the framework code was fine, I then realised that Inspect defaults to “UI Automation” mode. After switching to “MSAA” mode – Aha !
OK, so the most obvious thing one notices when switching between the modes is that the layout of the “objects” in the tree on the left and the properties on the right are quite different. Specifically, we do not have ListView sub-item objects in the tree when in MSAA mode, and instead of a “Bounding Rectangle” property there is a “Location” property. On the other hand, we do have ListView item objects are in both modes, and that is where we can see differences in the bounds between the two modes.
Here are the two modes, side by side (merged the two screen grabs):
From this, we see the ListView item has a reported width of 455 pixels in MSAA mode, and 384 pixels in UIA mode (485 – 101). So clearly MSAA mode provides information that is “less cooked”, and in this case, closer to the values returned via traditional (non-Accessibility) API routines such as LVM_GETSUBITEMRECT.
Next time, I’ll investigate this sub-item issue on other Windows platforms such as Windows XP, 2000 and 98SE, just to see whether this behaviour has changed. Indeed I’m also interested to continue with Windows 8.1 and how .NET and\or Winforms “enables” Version 6\visual styles.
You must be logged in to post a comment.