This article introduces and examines
ContentControl3D; a Windows Presentation Foundation (WPF) control that makes it easy to add animated 3-dimensional (3D) rotations to any user interface.
ContentControl3D can host any UI content on its front and back sides, and provides numerous ways to configure the animated rotations between the two sides. In addition to reviewing how to use the control, we will also see how certain aspects of it were implemented.
ContentControl3D is part of the Thriple project on CodePlex.com.
The Good, the Bad, and the Practical
WPF offers powerful support for creating 3D user interfaces. If you are knowledgeable about 3D programming concepts, vector mathematics, and have come to grips with the complex WPF 3D object model, the sky is the limit. That’s the good news.
The bad news, however, is that most developers are not 3D experts, and don't have the time, inclination, etc. to climb the rather formidable learning curve. Most of us are far too busy trying to meet our deadlines to experiment with the relatively low-level programming model that WPF exposes for creating and manipulating abstractions of entities in 3D space.
This is a rather unfortunate situation. The WPF platform offers so much potential for incorporating 3D into user interfaces, but the programming model is too complicated and low-level to make it practical for most developers to leverage it. This is precisely the reason why I decided to create
ContentControl3D lowers the barrier to entry for WPF 3D programming. You can use this control in your applications, even purely from XAML, almost as easily as the standard
ContentControl that comes with WPF. All of the complexities around using the 3D programming model are encapsulated in
ContentControl3D, so you don't have to know anything about 3D programming to make use of it in your applications.
Why Use 3D?
At this point, you might be wondering why you should consider using 3D in your applications. It’s certainly not necessary. If used in poor taste it can lead to confusing and tacky user interfaces. However, there are ways that you can tastefully incorporate 3D into a user interface, such that it improves the user experience.
The most common and compelling reason to use
ContentControl3D is space conservation. Many applications have a lot of data visualization and input screens to show to the user. The physical size of an application’s user interface is constrained by the size of the user’s monitor(s), or whatever size the application’s windows happen to be. This often creates contention for screen real estate amongst various pieces of a UI.
In addition to the layout problem, there is also the problem of presenting the user with too much information at once. If you try to cram as much “stuff” into a user interface as possible, the user might become confused and frustrated when trying to find what he or she is looking for. It is usually better to present as little information as necessary at all times, and disclose more detailed information upon request.
The problems caused by contention for screen real estate, and the cognitive dissonance created by information overload, can be mitigated by placing certain parts of a user interface on the back side of a
ContentControl3D. When the user has a need to view details about something, or enter into edit mode, etc. the
ContentControl3D can smoothly flip over to show that to the user. When the user no longer needs to view that piece of the user interface, the control can flip over to the front side and hide the unnecessary details. The image below, which contains two screenshots from the ‘Binding to a ViewModel’ demo application, shows this idea put to use.
The Thriple Project
ContentControl3D is part of my CodePlex project, called Thriple. You can download the latest source code and demo apps from that site. Here are some links to pages of the Thriple project:
- Home – landing page with introductory material
- Source Code – download the latest source code of the Thriple library and sample projects
- Discussions – ask a question or read what others have asked about Thriple
- Releases – read about system requirements and known issues
The ContentControl3D API
The interesting public members of
ContentControl3D, and some related enumerations, are seen in the following diagram:
The number of public members exposed by
ContentControl3D is not very large. The overarching design principle was to make this control very easy to understand and use, yet still have it be powerful and flexible enough to support, what I assume to be, the most common use cases. Every member of the control’s public interface was implemented to satisfy the requirements of one or more demo application; all of which are included in the source code package on CodePlex. You can learn more about these features, and how to use them, by looking at the samples and their XAML or C# files. As such, this article does not review every public member of the control.
Simple Usage – Direct Content
There are a few ways to put content onto both sides of
ContentControl3D. The simplest technique is to directly add UI elements to the
BackContent properties. Since both of those properties are of type
Object, you could assign them data objects instead of UI elements, but let’s start off with the easiest use case first. Here’s an example:
Running this simple example results in a user interface that contains a “Front Side” button that, when clicked, flips over to reveal the “Back Side” button. The following series of screenshots depicts this.
It is important to notice in the XAML above that the
Button controls have their
Command property set to reference the static
RotateCommand object, which is exposed by the
ContentControl3D class. That is the command you should use from XAML to cause the control to flip over. The UI element that you happen to use to execute that command, and where it is located in the UI, is up to you. For example, the ‘Content Templates’ sample uses the
Hyperlink element to execute the
RotateCommand, as seen below:
Advanced Usage – Content Templates
As mentioned at the end of the previous section, there is a sample application called ‘Content Templates.’ That is an example of how to supply
ContentControl3D with UI elements to display on both the front and back sides, via the use of
DataTemplates. Since the standard
ContentControl in WPF exposes
ContentControl3D follows suit and exposes
BackContentTemplate properties. Those two properties can be used in the exact same way that you use
ContentTemplate, respectively. In case you are wondering, I did not add a
BackContentTemplateSelector property, which would be equivalent to the
ContentTemplateSelector property of
ContentControl, because I had no need for it when writing the sample applications.
The following XAML shows how to create the same UI that we saw in the previous section, only this time a
DataTemplate is used to provide an explanation for what to show on both sides of the control.
In this example, both sides are rendered by the same
DataTemplate. You are free to assign different templates to each side, if necessary. It is also possible to use one
DataTemplate that renders each side differently, by triggering against the
IsOnFrontSide attached property. This technique is used in several of the sample applications, such as the ‘Content Templates’ sample. The following XAML snippet comes from a
DataTemplate in that sample:
<Trigger Property="thriple:ContentControl3D.IsOnFrontSide" Value="False">
<Setter TargetName="grid" Property="Background">
Rotation Easing Modes
As mentioned earlier in this article,
ContentControl3D can be used to create a simple and intuitive way to conserve screen real estate and prevent the user from looking at too much information. That, in and of itself, is a compelling reason to use the control. However, it has more to offer than just the ability to rotate in 3D space. It can rotate with style!
The Thriple library contains a class called
EasingDoubleAnimation. That class derives from the standard
DoubleAnimation class, and adds in the ability to use one of Robert Penner’s easing equations to create a more “natural” animation between two
double values. If you aren't familiar with the Penner equations, I suggest you check it out here.
EasingDoubleAnimation to rotate the 3D surface. If you set the
EasingMode property to something other than the default value of ‘
None’, an easing equation is used to “guide” the surface during the animation. The
RotationEasingMode enumeration values have friendly names that I made up, like ‘
Slap’ and ‘
RoundhouseKick’. Those values then map to more obscure easing equation names from Penner’s work, such as ‘
QuadEaseOut’ and ‘
Using an easing mode for the rotation can add some extra character and energy to a user interface. Like all snazzy things, it should be used judiciously, not superfluously. You can test out the various easing modes by opening the ‘Easing Modes’ sample, or the ‘Property Explorer’ sample in the ContentControl3D_Demo project.
This section discusses how some aspects of
ContentControl3D work, so feel free to skip this section if you aren't interested in the implementation details.
ContentControl3D class derives from
ContentControl. It has a control template which, when expanded into live UI elements, looks like this in Mole:
The descendant elements of the two
ContentPresenters are whatever elements you happen to assign to the
BackContent dependency properties, or whatever elements are generated via
DataTemplates. As you can see, the visual tree is not too large, and consists mostly of WPF 3D elements. The objects shown on each side of the
ContentControl3D are hosted in
Viewport2DVisual3D elements, which are WPF’s way of hosting interactive 2D elements in 3D space. Let’s take a look at the declaration of the
Viewport2DVisual3D that contains the back side content, from the control template:
TextureCoordinates="0,1 1,1 1,0 0,0"
Positions="-1,-1,0 1,-1,0 1,1,0 -1,1,0"
<AxisAngleRotation3D Angle="180" />
<Border BorderBrush="Transparent" BorderThickness="1">
One important point to notice is that the back side is initialized to be rotated by 180 degrees on the Y axis, due to the
Viewport2DVisual3D for the front side has an
AxisAngleRotation3D too, but its
Angle is initialized to 0. This means that the two sides of the 3D surface begin life “facing away” from each other, back to back. All subsequent rotations of the two sides cause the
Angle of their
AxisAngleRotation3D to change by 180. Here is an abridged version of the method in
ContentControl3D that rotates the surface:
public void Rotate()
if (_viewport.Camera == null || _viewport.Camera.IsFrozen)
_viewport.Camera = this.CreateCamera();
PerspectiveCamera camera = _viewport.Camera as PerspectiveCamera;
DoubleAnimation frontAnimation, backAnimation;
this.PrepareForRotation(out frontAnimation, out backAnimation);
Point3DAnimation cameraZoomAnim = this.CreateCameraAnimation();
this.IsRotating = true;
When the rotation animation completes, this method is executed:
void OnRotationCompleted(object sender, EventArgs e)
AnimationClock clock = sender as AnimationClock;
clock.Completed -= this.OnRotationCompleted;
this.IsRotating = false;
this.IsFrontInView = !this.IsFrontInView;
_isRotationPending = false;
Note that the
IsRotating dependency property is set to
true when the rotation begins and
false when it ends (which makes sense!), but the
IsFrontInView dependency property only changes state after the animation completes. This is useful information to know when writing triggers that target those properties.
ContentControl3D makes it very easy to add 3D into your WPF applications. When used properly, it can make an otherwise crowded, confusing, dull user interface much more clean and engaging. Be sure to explore the sample projects included in Thriple to learn about all of the features available in the control, and how to use them.
You can download the latest Thriple source code from here.
- March 22, 2009 – Published the article