If you’re an old windows forms or GDI/Win32 person, you may be tempted to override OnRender for more complex UIs.
A long long time ago I wrote how to create a custom control in Windows Forms. I showed the technique for calling Invalidate() to call OnPaint again if MouseIsOver changed.
This technique turns out to be a bad idea for WPF and Silverlight. To understand why it’s good to know how WPF is different from Windows Forms/GDI/GDI+. Wikipedia does a great job of explaining the under-the-hood details of the compositing system. When you write code in OnRender, you’re essentially adding to the list of geometries that WPF will later use to send instructions to the video card. When you’re overriding OnPaint and using GDI/GDI+, you’re sending drawing instructions to the card.
This effectively means the rendering engine in WPF is tied to the layout engine – in fact Arrange calls Render. This was not the case in windows forms – the layout code was separate from the painting code (PerformLayout() would only call OnPaint if you called Invalidate() or set ResizeRedraw, and even then it was windows that would respond with the WM_PAINT message once everything else was processed).
I’ve scribbled atop the MSDN diagram to add a way of thinking about it…
Doctored diagram from MSDN…
Although the DrawingContext draw methods appear similar to the draw methods of the System.Drawing.Graphics type, they function very differently: DrawingContext is used with a retained mode graphics system, while the System.Drawing.Graphics type is used with an immediate mode graphics system. When you use a DrawingContext object’s draw commands, you are actually storing a set of rendering instructions (although the exact storage mechanism depends on the type of object that supplies the DrawingContext) that will later be used by the graphics system; you are not drawing to the screen in real-time. For more information about how the Windows Presentation Foundation (WPF) graphics system works, see the Visual Layer Overview. – (MSDN Fine Print)
When is using OnRender ok?
It is ok to use OnRender for things that won’t change, but what you render changes on MouseOver or SizeChanged, you’ll may be causing a layout perf problem for your app.
The way to avoid the OnRender tax is to predefine your UI in a template, then for the bits that change, use triggers within the template to change it.
Next up: how to create a custom control in WPF using Cider.