As I mentioned in my last post, one of the guiding principles for the design of 3D in WPF is the consistent and powerful integration with 2D vector graphics, media, imaging, UI, and documents. And, for quite some time, I’ve wanted to write a little demo to show off specific aspects of this.
Here’s the premise: You know those nice user interfaces we’ve all been looking at for the last 25 years – be they Windows, MacOS, Motif, NeXT, OpenLook, Irix, etc? They’ve only improved over the years, looking nicer and nicer. But we’ve always only viewed them head-on, and only see what they have to offer from one side. What if we were able to walk around and look at them from an angle? Or from behind them? What do our familiar interfaces look like when we do that?
For instance, consider this sample interface (clearly I’m not a UI designer, so if someone wants to provide me with some XAML for a more pleasant interface with similar variety, please send it my way):
But what happens if we escape from Flatland, just by a little bit? Looks like our UI actually exists in 3-space after all, with different depth to the different UI elements. Who knew?
And rotating farther the other way, we start to see what’s behind our UI elements:
What does this knowledge let us do with our interfaces? Well, for one thing, transitioning from one UI state into another state can always be a pleasing operation when done well. However, with 2D UI’s, the stuff that makes up transitions is typically limited to 2D transformations like rotation in the plane, skewing, translation, etc. Once we “realize” that our 2D UI’s actually exist in 3-space, we can create much more interesting transitions. This next screenshot is from just such a transition (but I strongly recommend you run the app to see the 3D “implode” transition in action – it’s far better than looking at a static picture):
Uhh, what’s going on here?
ParallaxUi Decorator
The above are screenshots from this XAML Browser Application (.xbap). Run it yourself! [4/2/07 - Problems with this XBAP have been reported on 64bit Windows Vista, and on XP with IE7. Both are being investigated.] Play with the different controls up top to change view and invoke transitions. The “implode” transitions are random, so doing them a bunch of times shows a great deal of variety. (The XBAP is embedded in an HTML frameset in order to restrict the size of the surface, since lower end machines running WPF don’t perform well with 3D on very large surfaces.)
The app centers around a custom WPF Decorator that I call ParallaxUi. It’s called this, because the parallax effect occurs when an observer moves relative to a set of objects that he perceived to have a certain relationship to each other, only to find that their relationship changes with his motion. Wikipedia has a good explanation of parallax. This is the motion that’s observed in the app when the rotation slider is moved, and our UI starts to rotate in 3-space, revealing that its components have depth placement to them.
In WPF, a Decorator surrounds a single child element, and in XAML, the use of the custom ParallaxUi looks like this:
<Page …
xmlns:plxui="clr-namespace:ParallaxUi;assembly=ParallaxUi" >
...
<!-- Just adding the ParallaxUi decorator enables the transitions we see here. -->
<plxui:ParallaxUi Name="plx" RevolutionsFactor=… SpeedFactor=… >
<!-- This child is the UI that's controlled by the ParallaxUi Decorator -->
<DockPanel> <-- more XAML comprising the UI under “parallax” view --> ...
</DockPanel>
</plxui:ParallaxUi>
</Page>
The exposed ParallaxUi element, named “plx” here, exposes properties that control the behavior of the “implode”, as well as a Rotation3D property to let the user of the element set up arbitrary 3D rotations. It also exposes methods to invoke both a 3D transition and a 2D transition. Here’s how the ParallaxUi Decorator works:
That’s the basics of how this works. I really wanted to post it to further drive home how simple it is to do 2D/3D sorts of interplay, and hopefully help catalyze some interesting and cool uses along those lines. That’s all for this post, but I plan on a few followup posts to a) post the code after I get a little bit of a chance to clean it up; and b) talk about some of the more interesting technical challenges in getting this app to work, so that others can leverage those as well.