(NOTE: You might find Karsten's pragmatic perspective a useful prelude before we dive into the artictural underpinnings.)

In the September CTP release of the Windows Presentation Foundation (formerly code-named Avalon) we made 3D a first class citizen in the Visual tree with the introducion of Visual3D.  Up until this point 3D existed solely as content attached to a 2D Visual .  3D users migrating to the September CTP will encounter the Visual/Content split for the first time.  This has lead to some confusion about when nested ModelVisual3Ds should be used to group models together vs. a Model3DGroup.  Below is a short description of Visuals and Content and how they fit together.  At the end is a concrete example with guidelines on when to use a ModelVisual3D vs. a Model3DGroup.

What is a Visual?

A Visual (or Visual3D) is a low-level node in the composition tree.  For people coming from a Win32 background a Visual is frequently compared to an HWND:

  • A Visual has a graphical representation.
  • A Visual keeps track of its dirty regions.
  • If dirty, a Visual automatically re-renders each frame.
  • Visuals can be composed into trees.
  • A Visual can only be parented once.  (i.e., you can not share/reuse Visuals)
  • Visuals can be positioned relative to their parent via a Transform.
  • You can hit test a Visual.

There are however some notable differences between Visuals and HWNDs.  Probably the most notable is that WPF is a retained graphics architecture.  Visuals do not call back to user code to repaint when invalidated.  Instead you specify a Visual’s content (i.e., a description of what it should render).  If the Visual is dirtied for any reason it automatically repaints the dirty portion from the content specification on the next frame.  A sample Visual tree is pictured below:

VisualTree[1]

Most anything that renders in the WPF derives from Visual.  For example, Controls derive from FrameworkElement which derives from UIElement which derives from Visual.

What is Content?

Content is a description of what a Visual should render.  There are a few different APIs for specifying 2D content.  Currently in 3D the only way to specify content is via a graph of Model3Ds attached to a ModelVisual3D.  Like Visuals, you can build graphs of content using DrawingGroup (2D) or Model3DGroup (3D).  You can also position content via a Transform.  However, despite these superficial similarities there are important differences between Visuals and content:

  • Visuals are optimized to be scene nodes.  Content are serialized rendering instructions for scene nodes.
  • Visuals are the base class for Shapes and Controls.  Content classes are sealed.
  • Visuals are single-use.  Content can be shared/reused between Visuals.
  • Visuals support services like hit testing.  Content are serialized rendering instructions and nothing else.

Where Do Visuals End and Content Begin?

The distinction between Visuals and content is usually clear.  The one area which can be a bit subjective is whether a set of drawing primitives should be grouped together in content or by a tree of Visuals.  To give a concrete example, lets say you have a spaceship mesh that you need to appear three places on screen.  Should you use a single Visual that renders 3 spaceships?

Or 3 Visuals which render one spaceship each?

The answer depends.  If a cluster of 3 spaceships are flying together in tight formation then treating them as a single model may be reasonable, especially if you want several of these clusters cruising around the screen.  Or if you have a piece of clip-art featuring three tiny animated spaceships dog fighting around the 3D logo of your BSG fan site you might want to treat the clip-art, spaceships and all, as a single piece of content.

More typically you will want to each spaceship to be a separate node in the Visual tree.  Although you could use DrawingGroup and Model3DGroup to compose everything in your scene (assuming you do not need any Visual services like hit testing), this was not really the intent of DrawingGroup and Model3DGroup.  The purpose of DrawingGroup and Model3DGroup is to group together the pieces of a single shareable/reusable drawing or model.