Wednesday, July 4, 2007

Don't use CreateGraphics.

Unless you have a really good reason to, always draw all your UserControls inside OnPaint.

I really hate it when controls flicker. Flickering cannot be solved by getting a newer processer. It is not a problem with the speed of a video card. In computer graphics, it is often faster and easier to simply erase part of a window and redraw it rather than figuring out which individual pixel needs to be updated. Flickering is caused when a background colour is painted before the foreground graphics is painted. The problem is that on windows, each time you paint a pixel you're painting directly to video memory. The rendering of the background color (usually white) is visible for a split second before the foreground pixels are painted over the background.

One big mistake that people make when they write Windows Forms user-controls with DoubleBuffering enabled is that they try to draw onto a control outside of the OnPaint method. This is usually accomplished by creating a graphics context using Control.CreateGraphics in some other event handler and then drawing on the resulting context. This will bypass the inbuilt double buffering support in Windows Forms as you will be drawing directly to video memory rather than to an offscreen buffer.

If a control needs to be updated in response to some event/action, the state of the control should be changed by changing the appropriate variables and then a combination of the Control.Invalid, Control.Update, and Control.Refresh methods should be used to inform the control to redraw itself.

One side-effect of people using CreateGraphics (apart from control flickering), is that they sometimes draw graphics on the control and forget to draw it inside the OnPaint method. This means that the control will not draw itself properly if the control needs to be updated because of external causes (for example, if a window that was once overlapping the control moves).

I've seen this kind of mistake in a simple clock control. In response to a timer event, the developer calls CreateGraphics and paints the new time. The OnPaint method is never overridden. This appears to work most of the time but if a window is dragged over and then off the control, the custom drawn graphics for the control will be missing until the next timer event fires. Composition in Vista with composition (Aero) turned on actually prevents this from actually being much of a problem but I still think it's a nasty way to program controls.

NotifyingWeakReference

WeakReferences in C# and Java are nice but sometimes it'd be nice if there was an easy way to get notified when the referenced object is collected.

For Platform.NET I implemented a WeakReference type that has a ReferenceCollected event.

The type actually inherits from a generic class named WeakReference which isn't listed below but the point of this post is to demonstrate the relatively simple technique used.

Caveats:
  • Don't use the type if you plan on having hundreds of NotifyingWeakReference instances. Each NotifyingWeakReferences will create an object with a finalizer which slows down the garbage collection process.
  • The event will be raised sometime after the NotifyingWeakReference is invalidated but not immediately.

public class NotifyingWeakReference<T>
: WeakReference<T>
where T : class
{
public virtual event EventHandler ReferenceCollected;

protected virtual void OnReferenceCollected(EventArgs eventArgs)
{
if (ReferenceCollected != null)
{
ReferenceCollected(this, eventArgs);
}
}

private class GarbageCollectionListener
{
private NotifyingWeakReference<T> m_NotifyingReference;

public GarbageCollectionListener(NotifyingWeakReference<T>
notifyingReference)
{
m_NotifyingReference = notifyingReference;
}

~GarbageCollectionListener()
{
if (m_NotifyingReference.Target == null)
{
m_NotifyingReference.OnReferenceCollected(EventArgs.Empty);
}
else
{
new GarbageCollectionListener(m_NotifyingReference);
}
}
}

public NotifyingWeakReference(T value)
: base(value)
{
new GarbageCollectionListener(this);
}
}
PS. Does anyone else wish C# had runtime-level support for Java-like SoftReferences? I emulate them by using TimedReferences (WeakReferences that hold strong references for a specified amount of time after the reference target was last accessed).

Windows Media Store Crashes and disableUpdateDiscSvc

If you use a third party guide provider for Vista Media Center then you probably use the disableUpdateDiscSvc registry key. There is a bug in Vista that causes the Windows Media Store Update Manager to crash if the key/setting is enabled.

This bug is known by Microsoft (KB935685) but the fix is only available for download by calling Microsoft.

Luckily someone has uploaded the hotfix to www.xpmediacenter.com.au.