Introduction
This article presents a very clean and simple method to render an OpenGL window on a Windows Forms using Managed C++. This method requires no add-ins, third party DLLs, or ActiveX components, often demonstrated with C# Windows Forms. This implementation is in pure .NET using Managed C++. This article assumes the reader has a working knowledge of OpenGL and is using Microsoft Visual Studio .NET 8.0.
Background
Presenting an OpenGL window on an MFC view was very straight forward and easy to implement. The advent of Windows Forms however presents some problems. The concept of the window Device Context does not really exist in Windows Forms. There are some implementations of OpenGL on Windows Forms already out there but these rely on DLLs or ActiveX components containing MFC or Win32 SDK calls. These implementations also assume that the programmer is using C#. Whilst there are some advantages of using C#, I suspect that, like myself, I don't want to rewrite my existing applications in C# just to take advantage of Windows Forms. I also suspect that many C++ programmers don't want or can't move to C#, for corporate reasons or whatever. The implementation here is using pure Managed C++ and .NET.
The implementation
Firstly, we need a new Windows Forms application to work with. Create a new a new Windows Forms application (File -> New -> Project -> Visual C++ -> CLR -> Windows Forms Application).
We will now build an OpenGL class that can be used on several Windows Forms. This can be easily converted into a control that can be added to the Design Time Designer, but I'm not covering that.
Create a new header file for the class, I called my OpenGL.h and copy the following code snippets into it. I will comment below the snippet what the code actually does: -
#pragma once
#include <windows.h>
#include <GL/gl.h>
using namespace System::Windows::Forms;
namespace OpenGLForm
{
public ref class COpenGL:
public System::Windows::Forms::NativeWindow
All we've done here is include the necessary headers for OpenGL and decoare a managed C++ class for OpenGL.
{
public:
COpenGL(System::Windows::Forms::Form ^ parentForm,
GLsizei iWidth, GLsizei iHeight)
{
CreateParams^ cp = gcnew CreateParams;
cp->X = 100;
cp->Y = 100;
cp->Height = iWidth;
cp->Width = iHeight;
cp->Parent = parentForm->Handle;
cp->Style = WS_CHILD | WS_VISIBLE |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
this->CreateHandle(cp);
m_hDC = GetDC((HWND)this->Handle.ToPointer());
if(m_hDC)
MySetPixelFormat(m_hDC);
}
This constructor takes parameters of a parent form, the width of the OpenGL area and the height of the OpenGL area.
The System::Windows::Forms::CreateParams
structure allows us to create a Win32 window. We specify WS_CLIPSIBLINGS | WS_CLIPCHILDREN
so that our OpenGL display won't be clipped.
virtual System::Void Render(System::Void)
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f) ;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
This is an overridden function that simply fills the OpenGL area with black.
System::Void SwapOpenGLBuffers(System::Void)
{
SwapBuffers(m_hDC) ;
}
This is called directly after the OpenGL rendering to put the contents of the OpenGL buffer into our window.
private:
HDC m_hDC;
HGLRC m_hglrc;
These are the pointers we need to interact with the Form.
protected:
~COpenGL(System::Void)
{
this->DestroyHandle();
}
When destroying the Form, we dispose of our window.
GLint MySetPixelFormat(HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
32,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
GLint iPixelFormat;
if((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
{
MessageBox::Show("ChoosePixelFormat Failed");
return 0;
}
if(SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
{
MessageBox::Show("SetPixelFormat Failed");
return 0;
}
if((m_hglrc = wglCreateContext(m_hDC)) == NULL)
{
MessageBox::Show("wglCreateContext Failed");
return 0;
}
if((wglMakeCurrent(m_hDC, m_hglrc)) == NULL)
{
MessageBox::Show("wglMakeCurrent Failed");
return 0;
}
return 1;
}
};
Standard function to set a pixel format for a Win32 SDK / MFC Device Context. See OpenGL websites for more on this.
If you copy all of the snippets it should build fine, don't forget to add openGL32.lib, gdi32.lib, and User32.lib to your project (Project -> Properties -> Configuration -> All Configurations -> Linker -> Input).
The test container
Open the code view to your form and add the header to your OpenGL class and add a member variable that is a pointer to the class in your Form. Next, change your Form's constructor: -
OpenGL = gcnew COpenGL(this, 640, 480);
If you now override your Paint()
function (Forms Designer -> Properties -> Events -> Appearance -> Paint) and call your OpenGL renderer from this:
private: System::Void timer1_Tick(System::Object^ sender,
System::EventArgs^ e)
{
UNREFERENCED_PARAMETER(sender);
UNREFERENCED_PARAMETER(e);
OpenGL->Render();
OpenGL->SwapOpenGLBuffers();
}
Run your application! That's it, simply! OpenGL on a Windows Forms!
Points of interest
This method works because the System::Windows::Forms::NativeWindow
provides a low-level encapsulation of a window handle and a window procedure. This allows us to treat the class as a HWND
(see my user control).
I've changed the demo slightly to show it is more likely to be used. I did this because I've purposefully kept the OpenGL simple (I've not even setup the viewport or viewing frustrum but that is entirely application specific, my article is to show the method of creating an OpenGL view on a Windows Forms).
History
- 2006/10/18 - Submitted article to CodeProject.