Click here to Skip to main content
15,881,139 members
Articles / Desktop Programming / WPF
Tip/Trick

How to "Rotate" a WPF Window?

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
1 Mar 2016CPOL6 min read 25.9K   360   7   8
In response to an interesting Quick Questions & Answers Inquirer's request, I'm showing how to create an impression of window rotation in a simple way

The Question

This short tip is written in response to this question: http://www.codeproject.com/Questions/1081789/How-to-spin-window-in-wpf.

Being a pure decorative trick, not a very important one, it still may deserve a short article and a ready to use code sample. I answered the question (http://www.codeproject.com/Answers/1081829/How-to-spin-window-in-wpf#answer1), but using my answer might not be as easy as one may think at first glance. First of all, the problem can be naturally solved use true WPF 3D model (https://msdn.microsoft.com/library/ms747437%28v=vs.100%29.aspx), but to show tips and tricks, it would be more interesting to show something which is a really easy trick. But for that, we cannot rotate a window (and we cannot do it with 3D ether), and we cannot do real rotation, but just a dirty imitation of the rotation, but still good enough for the purpose, just good enough. So, how to prepare such a window?

Not a Real Window, a "Pseudo-Window"

First of all, it's good to use not the real main window of an application, but a temporary "effect" window, which would be shown while a main one is hidden, and then that "effect" window can be closed and discarded. Main reason for that is that we cannot rotate a real window with all its non-client areas; and the reason for that is because those non-client areas don't really belong to WPF; and WPF is based on DirectX, which has really nothing to do with Windows API, which rules in the area of non-client areas (pun unintended). So, we have to have at least two different windows.

Another approach is to execute Application.Run twice in the entry point method (Main): first with "rotating" window, and then with a main one. I think it's a matter of taste. In the code sample I provide, the entry point method is exposed, so anyone can use this trick. However, I'll show the variant playing with the visibility of the main window.

So, let's get to a rotating window, the one without client areas. This is how it can be prepared:

C#
public partial class RotatingWindow : Window {

    public RotatingWindow(Window mainWindow) {
        this.mainWindow = mainWindow;
        InitializeComponent();
        AllowsTransparency = true;
        Background = Brushes.Transparent;
        WindowStyle = WindowStyle.None;
    } //class RotatingWindow

    // ...

} //class RotatingWindow

Note that in the code shown above, I passed a reference to the main window to this window. I'll need it later, to pass to a thread, which should perform its "rotating" work and later restore the main window's visibility, which is the only purpose of this reference.

Now, we cannot rotate not the window (one reason is: an attempt to assign an instance to its property RenderTransform will throw an exception), so we need to rotate some content inside it, in my example, an instance of a Grid. We need at least two of them: one should be made transparent, and inner one has to be colored. When we scale down an outer one, the inner one will show the background. For the code shown below, however, the inner content does not matter at all; I put some random controls in it; it's only important to know that the immediate child of a window is some grid taking all its client area.

Not Rotation, "Pseudo-Rotation": an Element Viewed at an Angle

We need to show rotation the way that all the elements of the window content would be seen at an angle, all controls, all characters shown in all fonts, and so on. Suppose we want to show the rotation of around a center axes, then the transform can be an instance of ScaleTransform for a grid:

C#
grid.RenderTransform = transform; // we can do it only once

// ...

// after that, we can only modify transform parameters:
double step = System.Math.PI * DefinitionSet.RotationCount / DefinitionSet.StepCount;

// ...

transform.ScaleX = System.Math.Cos(x * step);

Showing the rectangular UI element at an angle, I assumed that we don't want to show it in space perspective. Oddly enough, this is not a transform supported by standard WPF transforms (and not by System.Drawing transforms). Such transform is not affine (which also sounds a bit surprising) and cannot be created, but it's tricky enough; you would have to do all yourself, up to the pixel level with all the interpolation problems. But if you agree to limit the view with a simple parallel projection, which can also create fair rotation impression, you can use the transform.

Now, we know how to render a thing; how to animate it?

The Animation Thread

In case of any complication in an animation scenario, I prefer doing things based on a low-level approach, using my own non-UI thread. This time, the complication is minor: to make rotation impression just a bit real, we certainly need to demonstrate near-uniform rotation motion, so the angle should be a linear function of time. That said, our scale factor should be not a linear function of time. As it can be seen from the above fragment, we simply need to make this factor a cosine function of time. Besides, we need to dispatch all changes to the UI to the UI thread, which is done using the Dispatcher object:

C#
using System.Windows;
using System.Windows.Media;
using System.Threading;

// ...

internal class ThreadWrapper {

    internal ThreadWrapper(Window window, Window mainWindow, ScaleTransform transform) {
        this.transform = transform;
        this.window = window;
        this.mainWindow = mainWindow;
        Thread = new System.Threading.Thread(Body);
    } //ThreadWrapper

    Thread Thread;

    internal void Start() { this.Thread.Start(); }

    void Body() {
        double step = System.Math.PI * DefinitionSet.RotationCount / DefinitionSet.StepCount;
        for (int x = 0; x < DefinitionSet.StepCount * DefinitionSet.RotationCount; ++x) {
            window.Dispatcher.BeginInvoke(new System.Action(() => {
                transform.ScaleX = System.Math.Cos(x * step);
            }));
            Thread.Sleep(DefinitionSet.Delay);
        } //loop
        window.Dispatcher.BeginInvoke(new System.Action(() => {
            mainWindow.Visibility = Visibility.Visible;
            window.Close();
        }));
    } //Body

    ScaleTransform transform;
    Window window, mainWindow;

} //class ThreadWrapper

Here, the class ThreadWrapper is a class I put forward a while ago (http://www.codeproject.com/Answers/155852/How-to-pass-ref-parameter-to-the-thread#answer2), in this case, is just a convenient and safe way of passing parameters to the thread, which is actually done through the "this" reference of the non-static (instance) thread method. In other cases, it's a good way to encapsulate thread interlocking mechanisms, events, and a lot more:
http://www.codeproject.com/Answers/223412/change-paramters-of-thread-producer-after-it-start#answer1,
http://www.codeproject.com/Answers/485734/MultiThreadingplusinplusC-23#answer4.

What Else?

In my original answer, I mentioned that the rotating content should mimic the behavior of a "standard" window, with all its non-client area. How? Apparently, these non-client areas can be pictured as some images in a client area, just to mimic the title bar, close button, etc. Below, in my comment, I added one more idea: at first moment, we could show a "normal" window, and when it is already rendered, we could make a screenshot of a part of the screen occupied by this real window and remember it. (There are more than enough questions and answers on taking screenshots with WPF.) Then this screenshot could be shown on a "rotating window" while the main window used to get a screenshot is hidden.

In this article, however, I thought it would be much more interesting to show a transform on the real WPF content, which is not as trivial as showing a picture.

Building the Sample Project

Please run provided batch file "build.bat", to build the projects into two binary directories (for Release and Debug) placed as sub-directories of the directory of the solution.

I intentionally created the sample project for Visual Studio 2008 and for .NET v.3.5 target, to create a common denominator usable by nearly all Windows users and developers. Just a reminder of those who were not aware of it: a .NET project of this kind does not need any Visual Studio to build; the C# and VB.NET compilers, MSBuild.EXE and other required tools are included with just the (freely redistributed) .NET Framework.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
Physics, physical and quantum optics, mathematics, computer science, control systems for manufacturing, diagnostics, testing, and research, theory of music, musical instruments… Contact me: https://www.SAKryukov.org

Comments and Discussions

 
GeneralWPF has got new meaning... Pin
Maciej Los2-Mar-16 6:13
mveMaciej Los2-Mar-16 6:13 
GeneralRe: WPF has got new meaning... Pin
Sergey Alexandrovich Kryukov2-Mar-16 8:27
mvaSergey Alexandrovich Kryukov2-Mar-16 8:27 
QuestionNo screenshots? Pin
Andre Sanches (alvs)1-Mar-16 14:23
Andre Sanches (alvs)1-Mar-16 14:23 
AnswerWhy screenshot? I just added the batch build, no Visual Studio needed Pin
Sergey Alexandrovich Kryukov1-Mar-16 14:27
mvaSergey Alexandrovich Kryukov1-Mar-16 14:27 
GeneralRe: Not a problem, wait up... (Re: No screenshots?) Pin
Andre Sanches (alvs)1-Mar-16 14:32
Andre Sanches (alvs)1-Mar-16 14:32 
GeneralRe: Not a problem, wait up... (Re: No screenshots?) Pin
Sergey Alexandrovich Kryukov1-Mar-16 14:37
mvaSergey Alexandrovich Kryukov1-Mar-16 14:37 
GeneralCool! and Familiar Pin
Matt T Heffron1-Mar-16 14:05
professionalMatt T Heffron1-Mar-16 14:05 
GeneralRe: Cool! and Familiar Pin
Sergey Alexandrovich Kryukov1-Mar-16 14:16
mvaSergey Alexandrovich Kryukov1-Mar-16 14:16 

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

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