Introduction
The focus of this article is to extend and improve the .NET ListView
control.
The current ListView
control is very useful, but has a nasty flicker problem associated with it.
My goal is to eliminate or significantly reduce this flicker.
The solution provided in this article only works with Windows XP with an associated manifest file.
This is the first of three articles I am writing to improve the ListView
control.
The others will not require XP.
In addition, other things I will discuss in this article do not require XP.
It flickers
There are two situations in which you will encounter flickering.
Dynamically adding items
If you attempt to add multiple items while using an ImageList
with multiple icons
(in a for
or foreach
loop), and you would like these items to display as
they are being added, you need to use Refresh()
or Update()
(you can use others like
Application.DoEvents()
).
Any of the above mentioned will cause an annoying flicker.
To avoid the flicker, you do not place Refresh()
, Update()
etc inside
the for
loop.
This will cause the ListView
to be blank until all of the items are added.
At times, this is the best way to go, but if doing this requires a significant amount of time, it is not a
good idea as the user will think the program has locked up.
Resizing the main form with a ListView docked
When a ListView
is Docked, as in Windows Explorer (and you have a good amount of items),
resizing the main form will cause all of the items to flicker.
Update: I originally had three. Third one being updating/changing an item, but you just need to
use Update()
rather than Refresh()
to fix this.
Why does it flicker?
When you add a new item to the ListView
, the control invalidates itself.
This causes the background to be erased, and all items to be redrawn again.
It should only invalidate the bounds of the new item, causing only the item to be drawn, but it doesn't.
This means that calling Refresh()
and Update()
will have the same effect,
where you would expect Update()
to only update the new or changed items. In my next article,
I will discuss a couple of techniques to allow only the newly added item to be redrawn.
Another reason it flickers is because it is not truly double-buffered. You can extend the
ListView
class, and add SetStyle(ControlStyles.DoubleBuffer,
true)
, but it has no effect. The technique described here shows you how to double buffer the
ListView
.
DoubleBuffering the ListView
If you are familiar with programming the ListView
(or CListCtrl
)
using only the Win32 API, or MFC, you will notice that you can set particular styles for the
ListView
. Most of these styles are already set in the .NET
ListView
, and are customizable (for example, the ViewModes
,
how icons will be aligned.. will they be auto-arranged, etc).
There are also "Extended" Styles for the ListView
.
These styles add a few extras such as using One-Click activate with hot-tracking (similar to Windows 98 Explorer
in web-mode, where clicking the item once will launch the item, "entering" the item will cause it to be selected"
etc..) There is also an extended style to allow gridlines to be drawn around
items in Details/Report mode (This is already implemented in .NET, when you choose details view and show gridlines).
In Windows XP, there is an extended-style called LVS_EX_DOUBLEBUFFER
.
This is what we want. Unfortunately it is only available for Common Controls version 6, which means we need
Windows XP as well as a manifest file (or embedded resource). Additionally there is a style called
LVS_EX_BORDERSELECT
which tells the ListView
(in Large Icon mode) to
highlight the border of the icon rather than to paint over it (This is available with Common Controls version 4.71,
so this will work without XP).
So now our goal is to actually make use of these extended styles.
This is pretty easy to do if you're familiar with the SendMessage
Windows function.
We import this function from user32.dll, and use it to retrieve and set extended styles. Here's the code:
public void SetExStyles()
{
LVS_EX styles = (LVS_EX)SendMessage(this.Handle,
(int) LVM.LVM_GETEXTENDEDLISTVIEWSTYLE, 0,0);
styles |= LVS_EX.LVS_EX_DOUBLEBUFFER | LVS_EX.LVS_EX_BORDERSELECT;
SendMessage(this.Handle,
(int) LVM.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (int) styles);
}
We start by retrieving any existing Extended Styles. We then add
DOUBLEBUFFER
and BORDERSELECT
, and use SendMessage
to add these extra styles to the current ListView
.
This method should be called AFTER the ListView
's handle has been created.
If not, it will not work as the Handle
has not been defined yet.
In other words, do not make a call to this in your main Form's constructor.. it should happen
after the entire form has loaded.
NOTE: LVS_EX
is an enumerated type of int. Each constant in here defines the proper
ExtendedStyle
.
LVM
is also an enumerated type representing ListView
messages.
Once you've made a call to this method, you can make a loop similar to this to add items to this ListView
:
for (int i=0; i < 500; i++)
{
listviewXp.Items.Add("item name", iconIndex);
listviewXp.Update();
}
Now you will be able to see the items as they are being drawn, and you will no longer get the nasty flicker.
In addition, if your ListView
is docked, you can resize your form without the flicker problem.
You can also call listview.Refresh()
or listview.Update()
after you have made a change to an
existing item and the change will be made without flicker.
Running and using the sample
Using the sample demo
Be sure you are using Windows XP and have both the exe and manifest file in the same directory.
Run it, and press the button to watch the items appear without flicker.
If you are not using XP, you will still see flicker, but you will be able to see the
BorderSelect
mode.
Compiling the sample app
Open up Visual Studio .NET and compile it.
Before you run it, copy the manifest file to the Debug or Release directory where the exe is.
Using ListViewXP in your own app
Just copy the ListViewXP.cs file and add it to your app. Note that if you want the double buffering effect,
you must provide the manifest file. You can copy the one I provided and rename it to YourAppName.exe.manifest.
You may use and distribute this class freely, but if you do so, please include a comment that includes my name
and a link to this article. Also, if you do use it, please let me know how it worked for you.
Conclusion
Using the SendMessage
function from user32.dll allows us to extend the .NET ListView
,
so we can add DoubleBuffering support in XP to eliminate flicker.
Additionally, we can use this function to add other extended ListView
styles,
and this technique can be applied to other controls as well. Hope you enjoyed the article.