Click here to Skip to main content
Click here to Skip to main content

3D gadgets for your WPF Desktop

, 20 Sep 2006
Rate this:
Please Sign up or sign in to vote.
Have you ever imagined having 3D objects float on your desktop? Lets see how we can achieve such a result using only C# and WPF.

Sample image

Introduction

This article describes how you can create 3D gadgets for your desktop, using only C# and WPF. A gadget should integrate with the desktop, always staying on top of all open windows so that the user can use the shortcuts it offers. Let's see how we can achieve such results. The demo project contains the iPod model seen in this picture. If you first want to learn how to build something simpler, read on and you won't be disappointed.

Splitting the problem

First, lets split this seemingly impossible problem into bits:

  1. Creating a 3D environment
  2. Adding functionality to the 3D objects we create
  3. Transforming the window so that it is transparent and always stays on top

1. Populating a Viewport

The WPF control that hosts 3D objects is called a Viewport. We will create a Viewport using XAML markup, the preferred way in .NET 3.0. The code is given below:

<Viewport3D Name="mainViewport" ClipToBounds="True">
<Viewport3D.Camera>
<PerspectiveCamera Position ="0,500,500" LookDirection ="0,-100,-100">
</PerspectiveCamera>
</Viewport3D.Camera>

The camera was added because, with no light, we cannot see any of the objects that we create. To fully describe the camera we must set its Position (where the camera is placed in the 3D environment) and the LookDirection (the direction where the camera is looking )

Now let's add a simple object to the Viewport:

<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<GeometryModel3D x:Name ="pyramid">
<GeometryModel3D.Geometry>
<MeshGeometry3D
Positions="0,0,37.5 -50,-50,-37.5 50,-50,-37.5 50,50,-37.5
-50,50,-37.5 0,0,-37.5 50,-50,-37.5 50,50,-37.5 -50,50,-37.5
-50,-50,-37.5 -50,-50,-37.5 50,-50,-37.5 50,-50,-37.5 -50,50,-37.5
-50,50,-37.5 -50,-50,-37.5 "
TriangleIndices="0,2,1,0,3,6,0,4,7,0,9,8,10,11,5,12,3,5,3,13,5,14,15,5"
Normals=""
/>
</GeometryModel3D.Geometry >
<GeometryModel3D.Material >
<DiffuseMaterial Brush ="White"/>
</GeometryModel3D.Material>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>

The actual 3D object is composed of triangles. Each triangle is specified as a succession of Points; to create another object starting from this one, you have to:

  • replace the points in the Positions collection
  • specify triangles in the TriangleIndices collection.

Sample Image - GADGET.jpg

But don't worry, there are easier ways to do it. The simplest way, for us graphically challenged developers is importing an existing model; the iPod model in the first screenshot is imported from a 3D Max model.

To import a 3D Studio Max model,

(*.3DS), of which there is an abundance on the internet, you can use ZAM 3D which is free to try and located here, or you can use an online free converter and its associated *.dll from here. This free online converter might not be updated for .NET 3.0 RC1 so you might have to replace some spaces with commas, but the rest works OK. In case you're wondering, the iPod model comes from the sample models that ZAM 3D provides.

After we import our model, we find that if we import several, all the models are on top of each other, so we must move each model to another place in the Viewport. We do this by using a Transformation. We want to translate the object so we write:

<ModelVisual3D.Transform>
<TranslateTransform3D OffsetX ="-200"/>
</ModelVisual3D.Transform>
</ModelVisual3D>

Adding animation

To make things more interesting, we will add a simple animation; don't overdo it in your commercial app as animation distracts the user from his work and he also gets tired more quickly.

To animate elements in XAML we use Storyboards; so that we can use the animation multiple times. We add it to the Windows Resources, like this:

<Window.Resources>
<Storyboard x:Key="RotateStoryboard">
<ParallelTimeline RepeatBehavior="Forever" Storyboard.TargetName="myRotate" 
                    Storyboard.TargetProperty="Angle" >
<DoubleAnimation From="0" To="360" Duration="0:0:30"/>
</ParallelTimeline>
</Storyboard>
</Window.Resources>

The animation must start when the window is loaded, so we use a OnLoad handler to start it, like this:

void Window1_Loaded(object sender, RoutedEventArgs e)
{
    Storyboard s;
    s = (Storyboard)this.FindResource("RotateStoryboard");
    this.BeginStoryboard(s);
}

2. Adding mouse handlers for the 3D objects

To add mouse handlers for 3D objects we first have to find out which object in the ViewPort was clicked (or any other mouse action). To do this, we use the built-in hittesting engine of the ViewPort, like this:

void mainViewport_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    System.Windows.Point mouseposition = e.GetPosition(mainViewport);
    Point3D testpoint3D = new Point3D(mouseposition.X, mouseposition.Y, 0);
    Vector3D testdirection = new Vector3D(mouseposition.X, 
                        mouseposition.Y, 10);
    PointHitTestParameters pointparams = new PointHitTestParameters
                            (mouseposition);
    RayHitTestParameters rayparams = new RayHitTestParameters(testpoint3D, 
                            testdirection);
    VisualTreeHelper.HitTest(mainViewport, null, HTResult, pointparams);
}

The code that actually handles the click on the objects follows. Some tips for actions that you probably should offer:

  • possibility to close the gadget, as there will be no Close button on the form - it is invisible. Use the this.Close function to close the window.
  • possibility to drag the gadget to another part of the screen, if it accommodates the user by covering an important part of the screen (the Titlebar of the window is also invisible so standard dragging is impossible). Use the DragMove function for achieving this result quickly.
  • to make a shortcut to a internet site, you can start a new process with System.Diagnostics.Process.Start giving the URL as a parameter.

    private HitTestResultBehavior HTResult
        (System.Windows.Media.HitTestResult rawresult)
    {
        RayHitTestResult rayResult = rawresult as RayHitTestResult;
        if (rayResult != null)
        {
            if (cube == rayResult.ModelHit)
            {
                string targetURL = @"http://www.codeproject.com";
                System.Diagnostics.Process.Start(targetURL);
            }
            if (cross == rayResult.ModelHit)
            {
                this.Close();
            }
            if (pyramid == rayResult.ModelHit)
            {
                DragMove();
            }
        }
    

3. Making the Window invisible

The standard definition of a Window in XAML looks like this:

<Window x:Class="WindowTransparency.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowTransparency" Height="314" Width="813"

To add transparency, just add these XAML tags:

WindowStyle="None" Background="Transparent" AllowsTransparency ="True"

We also want two more things: the form must not be visible in the taskbar and must always be on top. The corresponding tags are:

ShowInTaskbar="False" Topmost="True"

Notes on using the code

The code was built on Vista RC1, using the .NET 3.0 RC1 framework and Orcas development tools (http://www.microsoft.com/downloads/details.aspx?FamilyID=d1336f3e-e677-426b-925c-c84a54654414&DisplayLang=en).

It should also work under Windows XP, as I did not use any Vista-only feature (that I know of ) but it is not tested yet.

Conclusion

You can use this code to create something simple, like a 3D Mac OS X bar, or add a dancer like the ones that you can download from Microsoft, but this time in 3D. The possibilities are endless.

Notice how easy it is to do these things in XAML, just as simple as in a plain old VB6 application, simpler even than in .NET 2.0. WPF allows developers to achieve great visual effects without hassle, increasing productivity, but at the cost of having to learn how to properly use its power.

History

  • Update 22/09/2006 : Added iPod demo project and screenshot.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Dragos Sbirlea
Software Developer
Romania Romania
Dragos is currently a student at the Polytechnic University of Bucharest. He likes keeping up to date with new and amazing technologies and he hopes he will one day master the mechanisms behind modern day programming languages so that he can write the best possible code from a performance and maintainability point of view.
 
He keeps track of the things he learns on a daily basis on his blog, at http://picobit.wordpress.com/ .

Comments and Discussions

 
GeneralMy vote of 5! Pinmemberkanbang21-Aug-09 15:51 
Generalgood Pingroupzhujinlong1984091317-Apr-09 1:10 
QuestionHow to save Model3D object to jpg file? Pinmemberwmjcomcn24-Oct-08 0:20 
GeneralAlt tab Pinmemberhamid_m24-Mar-08 1:57 
Generalblocking my application form minimizing (Please help) Pinmemberdmnreddy30-Oct-07 12:45 
GeneralThanks for saving me basic research time PinmemberGerald Gibson Jr17-Mar-07 15:54 
GeneralRe: Thanks for saving me basic research time PinmemberDragos Dumitru Sbarlea17-Mar-07 21:59 
GeneralVS2005 and .Net3 PinmemberCyron4322-Sep-06 9:13 
GeneralRe: VS2005 and .Net3 PinmemberDragos Dumitru Sbarlea22-Sep-06 11:40 
GeneralRe: VS2005 and .Net3 PinmemberCyron4322-Sep-06 12:13 
QuestionSimple? PinprotectorMarc Clifton20-Sep-06 12:07 
AnswerRe: Simple? PinmemberDragos Dumitru Sbarlea20-Sep-06 12:16 
GeneralNope Pinmembernorm .net20-Sep-06 10:53 
GeneralRe: Nope PinmemberDragos Dumitru Sbarlea20-Sep-06 11:29 
GeneralRe: Nope PinmemberAndrewVos25-Sep-06 19:56 
GeneralRe: Nope PinmemberDragos Dumitru Sbarlea25-Sep-06 21:51 
GeneralRe: Nope PinmemberAndrewVos25-Sep-06 21:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141216.1 | Last Updated 20 Sep 2006
Article Copyright 2006 by Dragos Sbirlea
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid