Animating Images in a 3D ItemsControl

March 30, 2008

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:

ImageViewer3D (Before)

Now here is the same ItemsControl using my 3D panel :

ImageViewer3D (After)

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:

ImageViewer3D (ItemsControl using Panel3D)

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:

ImageViewer3D (Panel3D 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:

ImageViewer3D (Viewport3D Scene XAML)

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:

ImageViewer3D (The Bridge Between 2D and 3D)

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:

ImageViewer3D (Panel3D Model Creation)

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.


Watch my presentation at the WPF Bootcamp 2008

March 29, 2008

A few months ago I flew out to Redmond so that I could speak at Microsoft’s WPF Bootcamp.  I gave a presentation about implementing the Model-View-Whatever pattern in WPF, which was received very well by the attendees.  After giving that presentation I solidified the material into my ‘Using MVC to Unit Test WPF Applications‘ article on CodeProject.  This seminal work is the foundation of the structural skinning architecture seen in Podder.

The entire WPF Bootcamp 2008 was videotaped and recently published online.  Karsten announced it a few days ago in this post.  All of the videos and associated downloads are hosted in a Silverlight application (of course), which you can view hereNOTE: That page comes up empty for me in FireFox, so be sure to open in it Internet Explorer.

You can find my presentation under Day 3.  It is called “Hello, Real World!” and runs for about one hour.  I highly suggest you check out some of the other presentations, too.  There were many interesting things discussed during the bootcamp.

In case you want to download the entire video file,which is quite large, you can grab them individually from this page.  My video is around 330MB so it might take a while to download it, but if you’re just dying to fill up that new external hard drive…  :)


An article about the Podcast Management Conundrum

March 20, 2008

I published an article to CodeProject which discusses the solution to a tricky problem. It discusses the way that I implemented a piece of functionality in Podder that can be displayed in either the main window or a separate dialog window. This took me a while to get right, so I thought it might be beneficial to explain how it works.

Here it is: http://www.codeproject.com/KB/WPF/podder3.aspx

Please kick it on DotNetKicks.com for me!


Understanding Routed Commands

March 18, 2008

This blog post explains what routed commands are, and why you should use them. My explanation does not cover all of the details, nor does it show all of the various uses of routed commands. For a more comprehensive review of commanding, but with a very different (and somewhat misleading) perspective on the issue, be sure to check out the official documentation here.

Many demonstrations of routed commands explain them in the context of text editing, which is just one of the many ways to use them. In fact, so many explanations about routed commands focus on how to use them in text editing scenarios that many people I have met assume routed commands have no practical use outside of text editing. That is an incorrect, yet understandable, interpretation of one of WPF’s coolest features.

What is a routed command? Well, the answer to that question is too complicated to explain without first taking a step back and explaining some other things. To understand what a routed command is, we will examine what “routed” means, and then what “command” means. Once we have those two ideas defined, we can combine them and see what happens.

Routed

In WPF, the visual elements that constitute a user interface form a hierarchical tree structure. This “element tree” has many purposes, and is a core part of how WPF operates. The root node in the tree is a container, such as a Window or a Page. Everything seen within that top-level container is a descendant element in its element tree. There are two “views” of this element tree: the visual tree and logical tree. To learn more about that concept, I recommend you read this article.

In general, the term “route” refers to a path between two points in a network. Take that idea and apply it to the WPF element tree, and now you have a path between visual elements in a user interface. Now imagine an electric current quickly flowing through that route, zapping each element it passes through. When an element is zapped by the electric current, it has the opportunity to come to life and do whatever it wants (i.e. execute an event handler). That is pretty much how I think about routed events, except my complete mental model involves unicorns, robots, and laser beams, as seen in the diagram below:

The best way to imagine routed events

…just kidding.

One could provide a more mundane definition of a routed event as a notification that travels a route between two elements in the WPF visual tree. They are not really events, in the standard sense of .NET events. People wrap routed events with standard .NET events, out of convention, just to make them more usable and “.NETish.” But in reality, routed events are not events. They are a feature of the WPF framework, intimately tied to the WPF visual tree.

Routed commands use routed events, as do many other parts of WPF, so it is important to understand them thoroughly. If you are still hazy on the idea of routed events, check out the blog post I wrote about the topic here.

Command

The Command pattern is nothing new. It means that you create an object that knows how to do some task, and then the entire application relies on that object to do that task. For example, if I am creating an image editing application, I could create a command object called “CreateNewImage” or something like that. When the user either clicks the “New” menu item, or clicks the “New” toolbar button, or hits Ctrl + N, etc., the same CreateNewImage command object would create a new image.

In WPF, we have the ICommand interface, which makes it easy to create command objects that WPF classes can use. When you implement ICommand, you place all the knowledge of what needs to be done to accomplish the task in the command. You can then assign that command to, say, a Button’s Command property so that the command will execute when the user clicks the Button.

The ICommand interface has three members that all commands must define. It has two methods and one event. The CanExecute method determines if the state of the application currently supports the execution of the command. If CanExecute returns true, then the Execute method can be invoked, which is what actually performs the execution logic baked into the command. If CanExecute returns false, all controls whose Command references that command object are disabled so that the user cannot attempt to execute the command at that time. This is a very convenient feature, especially if multiple controls in the user interface all are bound to that command.

If the command determines that it’s “can execute” status changes, it can raise the CanExecuteChanged event, defined by the ICommand interface. This lets WPF know that it should call the command’s CanExecute method again, to query the new status.

Routed + Command = RoutedCommand

So what happens when you mix routed events with commands? Well, you get a very powerful way to think about how an application represents and implements its features. You get routed commands.

A routed command is a command object that does not know how to accomplish the task it represents. It simply represents the task/feature. When asked if it can execute and when told to execute, it simply delegates that responsibility off to somewhere else.

But Josh, to whom does it delegate?

The answer is, confusingly enough, it does not know whom.

A routed command does not determine if it can execute and what to do when executed. Instead, some routed events travel through the element tree, giving any element in the UI a chance to do the command’s work for it. It truly is just a semantic identifier: a named entity that represents a feature of a program.

When a routed command is asked if it can execute, the routed PreviewCanExecute and CanExecute events tunnel down and bubble up the element tree. These events give all elements between the root of the tree (such as a Window) and the source element that references the command a chance to determine if the command can execute. When the command is told to execute, the PreviewExecuted and Executed routed events travel the same event route, checking to see if anybody cares to react to the event. If so, they can run a handler for the event, if not, the event finishes zapping the elements and nothing happens.

Who Cares?

You might be wondering why it is a good idea to use routed commands at all. Why bother? What’s wrong with just hooking a Button’s Click event and doing things the “normal” way?

Well, you do not have to use routed commands if you do not want to. You certainly can just hook a Button’s Click event and go to town. By extension, why bother with a data layer and a business layer? Why not just stick your whole application into Window1.xaml.cs and be done with it? That would be much easier, right?

Yep, sounds ’bout right to me.

My point is, there are very real advantages to adhering to the loose coupling proffered by routed commands. Let’s review them.

First, all controls using the same RoutedCommand will automatically be disabled when the command cannot execute. If you have ever used a program where clicking on enabled buttons does not actually do anything, you will appreciate this.

Second, there is less event handler code to write since most of the wiring is provided for you by the commanding system. You do not have to add event handlers for each UI element that executes the same command, whereas using events directly off of UI elements requires many handlers that all basically do the same thing.

Third, if you use my implementation of Model-View-Controller or Structural Skinning, using routed commands is an absolute must. Learn more about MVC here and Structural Skinning here.

Fourth, using routed commands makes it possible to decouple the Software Engineering team from the Visual Design team. The developers don’t have to worry about what type of element is consuming application functionality, just as long as the UI executes the right commands all is well. This also frees the designers from having to worry about such details so that they can focus on creating a great user experience.

Fifth, but certainly not last, using routed commands as part of your design process forces you to map functional requirements to commands up front. This process of taking a list of required features and translating them into RoutedCommand objects with meaningful names is invaluable to help the team as a whole understand the system they are about to build. It also creates a set of short terms that people can use to refer to features.

Show me Some Code

I threw together a quick demo project in Visual Studio 2008, showing the simplest possible usage of a custom routed command. Download the code here: Routed Command Demo Be sure to rename the file extension from .DOC to .ZIP and then decompress the file.


Podder v2 has been released!

March 5, 2008

Podder 2 (new skin)

After a lot of work, fun, and learning, I’m thrilled to announce that the next version of Podder has been released!! Podder is my WPF podcast player. I recently published a large article about the new version of Podder, how structural skinning works, the new binaries, new source code, a Silverlight screencast, and even an interview between Craig Shoemaker (host of Pixel8 and Polymorhpic Podcast) and Grant Hinkson (the Visual Designer who made Podder’s new skin).

Download Podder v2 here. (NOTE: You must change the file extension from .DOC to .ZIP and then decompress. Requires .NET Framework 3.5 to run.)

View the article and download the source code here.

Please kick it on DotNetKicks.com for me!

Watch the screencast here.

Listen to the interview here.

Grant really did an incredible job on the new skin. It was an amazing experience to work so closely with an accomplished Visual Designer.


Craig Shoemaker unveils Pixel8

March 4, 2008

Ever since I rejoined Infragistics, I have been in almost daily contact with Craig Shoemaker. We do not work in the same group, or even work on the same projects, but I just really enjoy chatting with him since he’s an all-around great guy. In case you are not already familiar with Polymorphic Podcast, Craig’s popular .NET podcast, I recommend you check it out sometime.

Today Craig announced his new podcast, Pixel8. This new podcast, produced by Infragistics, is all about user experience. I am very excited about this because I think user experience is a very important aspect of any successful user-facing application. You can learn more about Pixel8 via this brief introductory video:


Follow

Get every new post delivered to your Inbox.

Join 288 other followers