OnRender is not as cheap as OnPaint

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.

Comments (4)

  1. Phil Wright says:

    Welcome back to the world of blogging.

    If you need recommendations on topics to blog I can think of plenty around Silverlight and WPF design time experiences. Such as explaining the common details of Blend3/VS2010 design time experience for control developers.

  2. jfoscoding says:

    Thanks! Keep the ideas coming – what particular areas do you think you’d need most help with?

  3. Phil Wright says:

    In particular how to create a great design time experience for a Silverlight and WPF pair of controls. I have created a control that has a version in both technologies.

    1, How do you get a Silverlight/WPF control to be automatically added to the toolbox so they appear as another toolbox tab alongside the standard ones. I want to do this at install time ideally. I read somewhere that VS2010 has a different method of doing this than previous versions.

    2, How to provide custom property editors that work in blend and VS2010.

    3, How to add a custom adorner for blend and VS2010.

    4, How to add extra XAML that is auto generated by the designer when you drop that control on a designer in blend/VS2010.

    5, List of best practices for creating custom controls.

    I’m sure I can think of more once you run out!

  4. jfoscoding says:

    The short answer is that the Microsoft.Windows.Design.Interaction and Microsoft.Windows.Design.Extensibility APIs are now shared by VS and Blend.  

    The Property Editing API and the Adorner API are now both shared.  The Adorner API has been simplified (you can use a AdornerHorizontal/VerticalAlignment + Margin combo if you find that easier than the  AdornerPlacementCollection).

    If you have some existing VS 2008 (Orcas) designers written against Cider, you will need to rev them for Dev10, as the ModelItem apis had to change to support both WPF and Silverlight.

    There are some older documents here for VS 2008


    I’ll work on proper answers for the rest – it will take me a while though as I would like to give you the answer based on a more current release as the answers are less likely to have changed.