This blog post shows a simple application in which we create a custom 3D panel that hosts some images in an ItemsControl. That panel displays a Viewport3D in its adorner layer and manages the relationship between the ItemsControl’s 2D visuals and their corresponding 3D visuals. Once all that is in place, we see how to animate the items in 3D space to create some very snazzy effects.
Every once in a while life throws you a bone. I woke up on this fine Sunday morning to discover that my good buddy, Sacha Barber, was looking for some help to fix a very strange bug. I did not know quite what his app was supposed to do, but he was displaying some images in a custom 3D panel and the images were not showing up when added from the code-behind.
I never did quite figure out how to fix that bug. However, while looking it over, my imagination fired up and I started creating a new project that was inspired by Sacha’s work. I decided to host an ItemsControl’s items in a custom panel (by setting its ItemsPanel property) and then display a Viewport3D in the panel’s adorner layer to achieve 3D visualizations of a 2D list of elements! Luckily, Sacha was cool with me writing a blog post about this exciting topic before he publishes his larger project. Thanks Sacha!!
In a nutshell, here is an ItemsControl full of images without using my custom 3D panel:
Now here is the same ItemsControl using my 3D panel :
What you cannot see in the screenshot above is that the images move to the back or front of the list along an animated path. The demo app moves the items at a regular interval, so it looks very cool. :D
Here is the XAML for the ItemsControl in the demo app’s Window:
The main point of interest here is the fact that the ItemsPanel property is set to a template that generates an instance of my Panel3D class. That panel hosts a Viewport3D in its adorner layer, enabling it to render a 3D scene “on top of itself,” so to speak. Panel3D configures the Viewport3D in its constructor:
Panel3DAdorner simply hosts a Viewport3D within a DockPanel, whose Background is set to White. Having an opaque parent element enables it to effectively hide the panel underneath it, so that all you see is the 3D scene. Here is the XAML used to configure the Viewport3D:
The trick behind all of this is how we map a 2D visual, provided by the ItemsControl, to a 3D visual displayed by a Viewport3D in the adorner layer. That logic exists in Panel3D’s OnVisualChildrenChanged method override, as seen below:
When that method needs a 3D model built for a 2D element, it calls the Build3DModel method. The technique used here (which I took from Sacha’s code) is to paint a DiffuseMaterial with a VisualBrush whose Visual property references the 2D object in the panel. The GeometryModel3D contained by the ModelVisual3D then displays that DiffuseMaterial. You can see that method below:
I won’t get into how the animations work because it is quite a bit of not-so-interesting code. The basic idea is that when the Panel3D is asked to move its items it animates the TranslateTransform3D and Opacity of each 3D object in the scene. The logic uses a little trick to figure out where to move each item and what opacity level it should have, but I leave that code as a challenge for the reader to decrypt. The code related to moving the items can be found in the MoveItems method of Panel3D, if you’re interested.
Download the Visual Studio 2008 source code here: ImageViewer3D (Source Code) NOTE: Please be sure to rename the file extension from .DOC to .ZIP and then decompress the file. This is a necessary workaround for a limitation imposed by WordPress.