Introduction
I had some data that I needed to display in a listview
. The data was represented by images, and sorted into groups. Since there could be dozens (hundreds?) of images, I thought it would be useful to be able to jump to any group using buttons (1 button per group). Unfortunately, scrolling to a group is not something that the .NET ListView
control supports out of the box.
Above is a screenshot of 4 buttons that scroll the listview
to 4 groups.
Background
The first time I tried to implement scroll-to-a-group, I used the EnsureVisible
method on the first list item in a group. This works, but it's not great. The problem is that the control will scroll just enough to make the item visible, and that could mean making it visible at the top or bottom of the control. If you have many 16x16 icons, it takes several seconds to find the item that you just EnsureVisible
'd. With EnsureVisible
, nothing will happen if the item is already visible. Also, EnsureVisible
can only be applied to ListViewItems
, not to ListViewGroups
(but Groups is what I wanted). The other method that was suggested to me was to use the TopItem
method. This cannot be used if the ListView
's view mode is LargeIcon
or SmallIcon
or Tile
(I was using LargeIcon
), so TopItem
was out of the question.
Using the Code
Usage is simple: Add the AdvancedListView
class to your project. When you want to scroll to a group, pass the group's name or index to AdvancedListView.ScrollToGroup()
and you will see your ListView
scroll so that the group header is right at the top of the control.
myListView.ScrollToGroup(myListViewItem.Group.Name);
Points of Interest
The main trick to getting this to work was realizing that the .NET Framework just couldn't scroll to a group. I needed to use the SendMessage
function to send a LVM_SCROLL
message to the ListView
control. In discussion forums, it had often been suggested to use WM_VSCROLL
, but this did NOT work. Using WM_VSCROLL
, I was only able to move the scrollbar, but could not scroll the contents. The LVM_SCROLL
message allows the listview
contents to be scrolled as well.
This message takes two values, which are used to determine how much to scroll the listview
by, relative to its CURRENT scroll position. The problem with this is that the position of the group is an absolute position, based on the position of the first item in the group:
myListViewGroup.Items[0].Position.Y - 30
To get the correct amount to scroll by, we need to first get the current scroll position, using the API function GetScrollInfo
:
int prevScrollPos = 0;
SCROLLINFO currentInfo = new SCROLLINFO();
currentInfo.cbSize = Marshal.SizeOf(currentInfo);
currentInfo.fMask = (int)ScrollInfoMask.SIF_ALL;
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref currentInfo)
prevScrollPos = currentInfo.nPos;
scrollPos -= prevScrollPos;
As it turns out, there are MANY ListView
messages. You can see all of them
here. I used a very simple structure for the one message that I needed:
private enum ListViewMessages : int
{
LVM_FIRST = 0x1000,
LVM_SCROLL = (LVM_FIRST + 20)
}
Using this message to scroll can be done like this (although my code doesn't actually have a scrollAmtX
because I was not interested in horizontal scrolling):
SendMessage(listViewHandle, (uint)ListViewMessages.LVM_SCROLL,
(IntPtr)scrollAmtX, (IntPtr)scrollAmtY);
History
- July 5 2008: Added a screenshot, added a bit more explanation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.