The background: Most of us know the SysListView32
common control and the equivalent wrapper ListView
class provided by the .NET Framework. A little depth into its internals show that the scroll bars it provides for scrolling its contents are NOT controls themselves, but are managed by the SysListView32
control.
The goal: Always draw scroll bars even if it has no ListViewItems
to display or has very few such that no scroll bars are needed anyway; sort of like mimicking the RichTextBox
class with its ScrollBars
property set to ForcedBoth
. Or kinda like this ListBox
:
The problem(s):
ListView
.My workaround(s):
override
the WndProc
in a derived class and handle its WM_HSCROLL
and WM_VSCROLL
messages as per steps 2 and 3.base.WndProc
to do the actually required processing of the scroll functionality.WmScroll
and do my processing on it immediately after
base.WndProc
has returned.GetScrollInfo
. Determine if a scroll bar is actually needed. If it's not then call ShowScrollBar
and EnableScrollBar
with required values to draw visibly disabled scroll bars.Problems with the workaround:
ListViewGroup
, rendering them useless!The descriptive image:
The long awaited actual question:
How do I force scroll bars to always be Visible
within a ListView
irrespective of the number of ListViewItems
and disable them if they are unnecessary, at the same time avoiding size miscalculation (to display collapse buttons of the ListViewGroup
s) and theme deterioration?
Answers without code, and answers with code in C#, VB.NET and C++/CLR are welcome. If you post code in any other language supported by .NET, please also leave a link to a code conversion website I may use if the code seems, uh, incomprehensible.
Information:
ListView
does not have WS_HSCROLL | WS_VSCROLL
flags set in its styles.
Control
s that do not have these flags set.CreateParams
does not work either. You have to set it manually in the OnHandleCreated
method using SetWindowLong
.ShowScrollBar
for each window message forces these flags to be set.The Solution:
Define your WndProc
like the following:
protected override void WndPoc(ref Message m) { //custom code before calling base.WndProc base.WndProc(ref m); //custom after base.WndProc returns WmScroll(); //VERY INEFFICIENT, called for each message :( }
Define WmScroll() as follows:
protected virtual void WmScroll() { NativeMethods.ShowScrollBar(Handle, SB_BOTH, true); //si.fMask = SIF_PAGE | SIF_RANGE <- initialized in .ctor NativeMethods.GetScrollInfo(Handle, SB_HORZ, ref si); if(si.nMax < si.nPage) NativeMethods.EnableScrollBar(Handle, SB_HORZ, ESB_DISABLE_BOTH); else NativeMethods.EnableScrollBar(Handle, SB_HORZ, ESB_ENABLE_BOTH); NativeMethods.GetScrollInfo(Handle, SB_VERT, ref si); if(si.nMax < si.nPage) NativeMethods.EnableScrollBar(Handle, SB_VERT, ESB_DISABLE_BOTH); else NativeMethods.EnableScrollBar(Handle, SB_VERT, ESB_ENABLE_BOTH); }
It now, looks like:
These are with another item added featuring the horizontal scroll and working ListViewGroup
collapse button:
AutoResizeColumns
is required if group collapse changes effective text width, otherwise the vertical scroll bar hides the collapse buttons.