Monday, September 2, 2013

In Windows 7, list control doesn't repaint its columns during column resizing

The expected behavior is that list columns should continuously repaint themselves while you're resizing them, e.g. by dragging a column separator in the list header. The symptom of the problem is that repainting is instead deferred: during the drag, a vertical line appears in the list, and moves horizontally to indicate the column width, but the list doesn't repaint its columns until the left mouse button is released.

This occurs regardless of what theme is used (Aero, Basic, etc.) It also occurs regardless of the "Show window contents while dragging" setting, located in System Properties/Advanced/Performance. In XP, there were two instances of this setting: one in System Properties/Advanced/Performance, and another in Display Properties/Appearance/Effects. The display setting had priority. In addition to controlling resizing behavior for entire windows, this setting also controlled whether list controls repainted their columns during column resizing. Display properties were drastically reorganized in Windows 7, and it seems that this setting no longer exists as such.

The expected column resizing behavior can be restored by using version 6.0 of the Common Controls. No other method appears to work. In other words, there does not appear to be any way to get normal list column resizing WITHOUT also using Common Controls 6.0. This is a problem, not only because some (myself included) find Common Controls 6.0 quite ugly, but also because Common Controls 6.0 introduce many subtle GUI changes which may in some cases break existing code.

Even worse, in Windows 7, adding an external manifest that references Common Controls 6.0 won't work if the app has an internal (embedded) manifest. This is because manifest priority behavior was changed, starting with Vista. Prior to Vista, external manifests had priority over (i.e. overrode) internal ones, but since Vista this behavior is exactly reversed, meaning an app with an embedded manifest now completely ignores any external manifest.

So the options are as follows:

1. Change the app's embedded manifest to specify Common Controls 6.0.

2. Build the app without an embedded manifest, and ship it with an external manifest file that specifies Common Controls 6.0.

The problem with option #1 is that users of the app will get the Common Controls 6.0 look whether they like it or not, and it they don't like it, there's no easy way to change it. In theory they could modify the app's internal manifest via mt.exe or third-party tools but in practice this is tricky and few will bother.

So option #2 is more flexible: if the user doesn't want the Common Controls 6.0 look, they can simply delete the external manifest file, and pay the price: list controls won't repaint their columns during column resizing. But option 2 is also more fragile, e.g. if the manifest gets lost or damaged, list control behavior changes quietly and mysteriously. Also, shipping an app with no embedded manifest may have other unknown ramifications.

Option #1 can be accomplished by simply adding the following line to stdafx.h (all on one line):

#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")

Note that the app wizard in NET2005 and later automatically adds the above pragma to stdafx.h, BUT it does so conditionally, i.e. within an #ifdef UNICODE, so that the pragma is only output if the app uses Unicode. This is because MS allegedly doesn't support Common Controls 6.0 for MBCS (ANSI, non-Unicode) apps. In practice using Common Controls 6.0 in an MBCS app seems to work OK. According to some sources the Rich Edit control would cause problems, but I haven't tested that.

This conditional around the pragma causes a confusing symptom: if you create a stock MFC app with NET2008, it will resize list columns normally, but if you change the app's character set to MBCS, suddenly the list columns will NOT resize normally. This is purely an artifact of the app wizard code in stdafx.h: Removing the Unicode conditional from the Common Controls 6.0 pragma eliminates this problem.

For option #2, a minimal but effective manifest file would be as follows:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="*"
    name="AnalSoftware.WaveShop.WaveShop" 
    type="win32"
/>
  <dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="*"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
  </dependency>
</assembly>