Creating a Window

  CU_MDX_WindowCreation.zip (11.0 KiB, 3,643 hits)

Creating a window with C# and Windows Forms is about as simple as it gets. In fact, you don’t even have to write any code! If you’re using an IDE like Visual Studio or Visual C# Express, when you create a new C# Windows Application, all the window creation code is generated for you. If you compare this code to Win32 window creation code, you’ll see that this is a lot simpler. Hurray for .NET. Compared to Visual Studio .NET 2003, VS 2005 creates more files and makes use of the new C# 2.0 partial class construct, which just allows you to split a class into multiple files. The Main function is generated in Program.cs:

using System; 
using System.Collections.Generic; 
using System.Windows.Forms; 

namespace CUnit {
static class Program {
///
/// The main entry point for the application. 
///

  [STAThread] 
  static void Main() {
    Application.EnableVisualStyles(); 
    Application.SetCompatibleTextRenderingDefault(false); 
    Application.Run(new Form1()); 
  } 
} 
}

The first two methods, Application.EnableVisualStyles and Application.SetCompatibleTextRenderingDefault, aren’t really important when using DirectX, so I’m going to delete them. Application.Run is where all the excitement happens. This is where our window is created using the Windows Form, Form1:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 

namespace CUnit {
public partial class Form1 : Form {
  public Form1() {
    InitializeComponent(); 
  } 
} 
}

The Form class controls every aspect of the created window. We’ll usually want to name it something more exciting (or descriptive) than Form1, but Form1 will do for now. If we were making a generic Windows Application, we would fill this class up with lots of Windows Forms event handlers and such, but we’re here to make more exciting programs with DirectX. Currently, the only content in this file is a call to InitializeComponent, which is defined in the Form1 partial class file, Form1.Designer.cs:

namespace CUnit {
partial class Form1 {
  ///
  /// Required designer variable. 
  ///
  private System.ComponentModel.IContainer components = null; 

  ///
  /// Clean up any resources being used. 
  ///
  /// true if managed resources should be disposed; otherwise, false. 
  protected override void Dispose(bool disposing) {
    if (disposing && (components != null)) {
      components.Dispose(); 
    } 
    base.Dispose(disposing); 
  } 

#region Windows Form Designer generated code 
  ///
  /// Required method for Designer support – do not modify 
  /// the contents of this method with the code editor. 
  ///
  private void InitializeComponent() {
    this.components = new System.ComponentModel.Container(); 
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 
    this.Text = “Form1″; 
  } 

#endregion 
} 
}

The InitializeComponent method contains the code generated by the Form Designer in Visual Studio/C# Express. As a result, we usually never have to mess with any of the code in this file. The code in InitializeComponent simply sets up the characteristics of the Form and any GUI controls in the Form. Since we can use the Form Designer to generate all this code automatically, we can close this file and never look at it again.

Visual Studio also generates a few other non-coding files, which we can safely ignore. They are used for creating resources and saving settings. We won’t be using them anytime soon, so we’ll just pretend they don’t exist.

If we wanted, we could continue on writing our spiffy managed DirectX program from here, but we’re going to add some modifications that I read about on Tom Miller’s blog, who is the development lead for Managed DirectX.

namespace CUnit {
static class Program {
///
/// The main entry point for the application. 
///

  [STAThread] 
  static void Main() {
    using (Form1 form = new Form1()) {
      Application.Idle += new EventHandler( form.OnApplicationIdle ); 
      Application.Run( form ); 
    } 
  } 
} 
}

Normally, Windows Forms only execute code when a message is passed onto the message queue. Since we’re making real-time games, we want the Form to continuously process our game whether a message is on the queue or not. We can do this by hooking the Idle event of the application. Whenever no messages are in the message queue, the application is idle and OnApplicationIdle will be called automatically. This way, we will still be able to perform our game processing even when no message are on the queue.

public void OnApplicationIdle(object sender, EventArgs args) {
while (AppStillIdle) {
  // UpdateFrame(); 
  // RenderFrame(); 
} 
}

The OnApplicationIdle method continuously updates and renders frames while the application is still idle. Since we haven’t written any UpdateFrame or RenderFrame methods yet, those calls are commented out. Whenever there is a message waiting to be processed, we want to exit out of this loop to handle those messages.

private bool AppStillIdle {
  get {
    NativeMethods.Message msg; 
    return !NativeMethods.PeekMessage( out msg, IntPtr.Zero, 0, 0, 0 ); 
  } 
}

The AppStillIdle property checks if there is a message on the message queue by calling the PeekMessage native method. PeekMessage is not a .NET method. It’s actually a Win32 function. We can access a Win32 function by importing User32.dll:

using System; using System.Runtime.InteropServices; namespace CUnit { /// Native methods public class NativeMethods { /// Windows Message [StructLayout(LayoutKind.Sequential)] public struct Message { public IntPtr hWnd; public uint msg; public IntPtr wParam; public IntPtr lParam; public uint time; public System.Drawing.Point p; } [System.Security.SuppressUnmanagedCodeSecurity] [DllImport(“User32.dll”, CharSet=CharSet.Auto)] public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags); } }[/chsarp]

The Message struct and the PeekMessage functions are constructs from the native Win32 API. They are used when determining whether a message is on the queue or not. You don’t really need to understand all the Security and DllImport mumbo jumbo now, just know that this code imports functions and constructs from external DLLs.

Once you have all this down, you’re good to go! On to the next tutorial.