This article shows how to display OpenGL content hosted in a Windows Presentation Foundation (WPF) based application. While working on a new project, I came across this issue and I want to share what I found. I am not an expert on WPF or OpenGL, though.

The controls in this example are implemented using managed C++, because it makes using native OpenGL and Win32 libraries easier. This is a nice example of the usefulness of managed C++, I think. Note that you can create a wrapper for an existing native C++ control the same way. Tha Tao-Framework based version makes life even easier, not requiring any managed C++.

The sample application will show a WPF program in C#, which displays an OpenGL window (or control). This is similar to the situation encountered in CAD / CAM applications. Also, the window should close when pressing the ESC key. In WinForms and Win32, this problem is fairly easy to solve. There are excellent articles and samples out there (Thanks to Jeff Molofee aka NeHe). However, due to the fact that the internal structure of WPF is very different from that of the Win32 API or WinForms, we have to change a few things.

Fortunately, there is a very nifty framework out there called "Tao" which can be found at The Tao Framework. The Tao Framework renders this article quite superfluous. What remains is a very simple sample application which can be downloaded at the top of the article along with parts of the source of Tao. Note that Tao is distributed under a different license (MIT License). See the file "Copying" for details.

Now, if you really want to know how to do it manually, here we go: In this article, I assume that you have a basic idea of how to create an OpenGL-window using Win32 API. You haven't heard of PIXELFORMATDESCRIPTOR before? In this case, you might want to read NeHe's first tutorial. (See the Resources Section).

Also, some very basic WPF know-how will be useful (e.g. how to reference custom controls in another assembly from XAML). I suggest you read Sacha Barber's excellent introduction to WPF here on The Code Project. (Huge thanks to Sacha for his articles!)

In order to create an OpenGL-window, we need to have a dedicated HWND. In Win32, we can simply create one of our own. WindowsForms controls, on the other hand, each have their own HWND anyway, so we can simply use that. In WPF, however, there is only one HWND for the application (With some exceptions: menus, for example, have their own window). As we do not want to interfere with the rendering of WPF's controls, acquiring the application's HWND is not a good idea (if possible at all). So how do we get a window for OpenGL to render to?

Microsoft provides us with two simple classes made for WPF / Win32 interoperation. As the name suggests, these can be found in the namespace namespace System.Windows.Interop. These are the earlier mentioned WindowsFormsHost and HwndHost classes. We will host a WindowsFormsUserControl in the WindowsFormsHost, and a custom Win32 API window using the HwndHost. Note that WindowsFormsHost is actually derived from HwndHost. Let's look at the simpler case of using a WindowsForms UserControl in a WindowsFormsHost first:

(I won't write down the C# version every time in order not to bloat the article).

This method does little more than setting the OpenGL viewport and updating the projection matrix. As a matter of fact, I have chosen to use an orthogonal projection for reasons that I will explain shortly.

For perspective projections, the projection matrix must be recalculated when the window size changes, for example, using gluPerspective() or glFrustum(), that's why I left the code in this method.

Now we can go one inheritance level higher and mess around with HwndHost so we can use any Win32 control (or window). First, we can no longer insert the control in XAML. Instead, we create a placeholder in XAML, in this case, just a Border-control:

Implementing the control itself also is somewhat different from the WindowsForms-case:

Using the HwndHost, we create the HWND ourselves. To do that, we also need to register our Window class.

For drawing purposes, we only overwrite the OnRender() method. There is no Paint/PaintBackground distinction.

We have to care about the system's DPI setting manually.

Our Window now has its own WindowProc!

In principle, you could take a complete Win32 application and put it into the HwndHost. The procedure of creating a window is the same as under Win32, but it has to be performed in the overwritten BuildWindowCore() method.

virtual HandleRef BuildWindowCore(HandleRef hwndParent) override

Yet, meaningful interaction of WPF and Win32 is fraught with its own perils. More about that later.

This works exactly as it does in Win32. This, however, seems a little strange: The class HwndHost supplies us with a managed method called WndProc(). MSDN suggests to overwrite this, but I didn't manage to initialize the window this way.

When registering the Window class, one can specify the WNDPROC to be used. Leaving it empty resulted in strange access violations during initialization, while the following simple implementation proved to work out fine, thus rendering the overrideable WndProc() method irrelevant:

At this point, however, the window does not have focus. Unfortunately, that will prevent not only our WNDPROC to handle any key events, but it will also prevent HwndHost from forwarding the keyboard information to WPF. Thus, we have to acquire focus manually by a little more sophisticated version of MyMsgProc:

Since there are now more and more devices with a screen resolution of above 96 DPI, it becomes more important for applications to be DPI-Aware. To tell the truth, I never bothered about the DPI until I set it to 120 myself.

This is why I chose to use an orthogonal projection here: It enables us to check (by visual means) whether we correctly mapped the screen, or not.

In our case here, the problem becomes highly annoying: If you don't take the system's DPI setting into consideration, you will have a large border where you simply cannot draw to - your GL window is too small:

In order to avoid that, we get the system's DPI setting on initialization and multiply the new window size with it upon resize:

Although the presented techniques are very similar at a first glance, they are targeted at different things: The HwndHost is a class the actual control is derived from. On the other hand, the WindowsFormsHost is a WPF-Control which we can place in an XAML file - the actual control in this case must be a UserControl.

While the WindowsFormsHost allows the use of an arbitrary WinForms user control with very little effort, usage of the HwndHost can be quite tricky, especially when it comes to input handling. This is largely because it completely breaks the controls scheme of the GUI and, in the case of input events, even overrides the main (WPF) application. On the other hand, being able to combine Win32 and WPF with a few tricks is still marvellous!

One thing that bothers me is the exact behaviour of CS_OWNDC. I have read some articles on the net about it, but in the end I did not find an explanation that satisfied me. Removing it from the code doesn't seem to change anything, but I wonder what happens when we perform more complex rendering operations?

Another issue is performance. I did not talk about it in the article at all for a reason. My system is barely capable of displaying a transparency-enabled Vista desktop at full resolution... In my case, performance is a catastrophe! However, I believe that is largely due to a fill rate bottleneck of my old GeForce MX 5200. Also, we can't make a performance measurement using a timer that invalidates the control from time to time, of course!

Hi Alessio,thanks for the info - just tested this and I can confirm that behaviour. As stated in the preface, however, I strongly suggest to use the Tao-based version anyway - that is much easier, and it does work in Win7 64bit, too.

If you're interested in setting up an OpenGL window, that will help you, but if it's 64bit interop you're interested in, I'm afraid my answer is a little insufficient. Unfortunately, I don't have the resources right now to update the code/article to be 64bit compatible, sorry.

If you find out anything, please let me know!Thanks,Chris

"Obstacles are those frightening things you see when you take your Eyes off your aim" - Henry Ford

OnRender is not always called at the end of a screen resizing. It does not appeared to be a problem in your sample application since a timer is used to force constant update of the display. In an application where the image may change only in response to mouse event, it will become a problem when OnRender was not called at the end of a screen resizing. You can easily test the effect in your code by commenting out everything in updateTimer_Tick() function and run the application. You'll notice when you resize the window rapidly, the display does not always fill the entire client area at the end of the resizing.

Instead of doing actual rendering in OnRender, I tried to do it on WM_PAINT message in a WndProc override. I wonder if you have any insight to this.

Additional information: Could not load file or assembly 'OpenGLManagedCpp, Version=1.0.3210.34606, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An attempt was made to load a program with an incorrect format. Error at object 'windowsFormsHost1' in markup file 'WPFOpenGLApp;component/openglusercontrolwindow.xaml' Line 10 Position 14.

This is one of the typical mistakes when people use AnyCPU and don't test on x64 platforms. The program has been compiled so the .Net portion will run in 64-bit mode on x64 .Net systems and 32-bit mode on x32 systems. However, the DLL is only 32-bit.

To fix, go into the Build tab of WPFApplication1 Properties dialog and choose Platform target: x86then rebuild the application.

Excuse me for pointing out the obvious, but WPF was designed specifically to minimize, if not remove entirely, the need to reference Win32 and GDI libraries when displaying content. A large chunk of its rendering capabilities rely on DirectX calls, and especially now with Service Pack 1 for .NET 3.5 and VS2008 a WPF application will attempt to do as much of its display though DirectX hardware acceleration before it reverts to other methods to maintain optimal application speed.

Also, your code sample has practically no WPF code in it! One of the other main advantages of WPF is the ability to create a rich and functional UI with very little code-behind. The code sample for this article is almost entirely code-behind.

I'm sorry, but I think you completely missed the point of WPF and tried to jump into a technology without understanding why it should be used. I know there are people out there who have a need to render their content using OpenGL, but a WPF application is simply not the best container for such code.

of course you are right that WPF is designed (and well designed) for creating great user experiences using declarative code.

However, when migrating existing applications, it might be necessary to embed an OpenGL control in a WPF application. In the real world, money is often an issue so a complete rewrite of a complex 3D control might not be feasible - yet the client needs non-3D features which can be easily implemented using WPF. Therefore, it might be desireable to mix the old OpenGL control with new WPF code. That's why WindowsFormsHost exists. And that is what this article points out - no more, no less.

Also, please note that .NET SP1 has been released only two days ago, yet this article is some months old. Therefore, it is not really fair to use it as an argument.

Chris

"Obstacles are those frightening things you see when you take your Eyes off your aim" - Henry Ford

In general I agree with your argument. However I learned practically that WPF performance for high end graphics is not optimal yet (even with .net 3.5 SP1). At the same point we cannot give up all rich functionality of WPF provides for user interface. So, I feel this article provides good overview on understanding issues related to hosting OPENGL window in side a WPF. With this approach application can enjoy best of both worlds.

Along these lines I would like to point out the AirSpace problem with OpenGL and WindowsHost. What this means essentially is that you cannot move a WPF window *over* an OpenGL window, the OpenGl window will always stay top of the Z order.

This means an OpenGL window cannot really be used in the Rich Overlapping/Dragging world of WPF. This solution is a very nice one for moving an OpenGL window based App with Minimal WPF integration but for a deeper UI you may need to move to DirectX.

-bc

BTW There is some research with FBO and PBO objects which I am looking into that may solve the problem at the expense of performance.