Windows Primer 2

Introduction

Private Data

Full-screen and Windowed

Input Handling

Constructor

Using D3DWindow

Wrapping Up

Lesson Downloads

Introduction

In the last lesson, simple window creation was covered. It wasn't a bad method, but it was shoe-horning a C approach into .Net. Since .Net is designed to be object oriented, a more logical way to approach things is to inherit from a Form to create our own Direct3D-friendly window. That's what this lesson covers.

One key feature this class will support is toggling between full-screen and windowed mode. The goal is to keep things simple though. We don't want a monolithic class, just a simple well-defined window class. The class is named D3DWindow.

Private Data

This class requires 2 data members: a full-screen indicator and a client size.

   private bool m_fullscreen = false;
   private Size m_win_size = new Size(640,480);
   Private _fullScreen As Boolean = False
   Private _winSize As Size = New Size(640, 480)

By default it will be windowed mode with a client size of 640x480. A Windows.Form already has a ClientSize member but we need to remember what the requested size was because we overwrite it when switching to full-screen. This way the app remembers what size it should be when it returns to windowed mode.

This data is declared as private. We want the user to set these, but we also need to take action based on those values. To do this we'll make use of "properties". The get property is trivial for both, it just returns the current value. The set properties do a little work though.

The FullScreen property, when set, checks if the current mode is the same as what is being requested. If it is, it returns immediately since there's nothing to do. Otherwise it calls GoFullScreen or GoWindowed to make the appropriate changes to the window. The internal value for m_full_screen isn't set here because it gets set in GoWindowed and GoFullScreen, so it isn't necessary here.

   public bool FullScreen{
      get{
         return m_fullscreen;
      }

      set{
         //If already in that mode, do nothing

         if (m_fullscreen == value) {
            return;
         }

         if (value) {
            GoFullscreen();
         }else{
            GoWindowed();
         }
      }

   }
   Public Property FullScreen() As Boolean
      Get
         Return _fullScreen
      End Get

      Set(ByVal Value As Boolean)
         'If already in that mode, do nothing

         If _fullScreen <> Value Then
            If Value Then
               _GoFullscreen()
            Else
               _GoWindowed()
            End If
         End If
      End Set
   End Property

To manage the m_win_size member, we hide/replace the ClientSize property of Windows.Form. As a Direct3D application the client size is important while the overall size (including borders), isn't. NOTE: This class is designed to have the size changed only through the ClientSize property. Using any other method (such as the Size property) is unsupported. It would be easy to add, but this class is designed to be very simple.

   public new Size ClientSize{
      get{
         return m_win_size;
      }
      set{
         base.ClientSize = value;
         m_win_size = value;
      }
   }
   Public Shadows Property ClientSize() As Size
      Get
         Return _winSize
      End Get
      Set(ByVal Value As Size)
         MyBase.ClientSize = Value
         _winSize = Value
      End Set
   End Property

The only thing of note here is that when we set the ClientSize we must call base.ClientSize, otherwise we end up calling our own ClientSize property and get locked into recursion until the stack overflows.

Full-screen and Windowed

There is nothing really new here. This code does the same thing that the code in the previous lesson did, it just does it as part of the window rather than operating on a window that was passed to it. The only new thing here is the extra steps it takes to make the transition from windowed to full-screen as seamless as possible. It does this by checking if the window is currently visible. If it is visible, this window is hidden, all changes are made, and then it is shown again. If the window wasn't visible then none of that is required.

Input Handling

D3DWindow has 2 input handlers: OnClick and OnKeyDown. OnClick simply shuts the window down on a mouse click, just another way to exit the program.

   protected override void OnClick(System.EventArgs e){

      this.Close();

   }
   Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
      Me.Close()
   End Sub

OnKeyDown acts on 3 keys:

'F'
Switches to Full-screen.
'W'
Switches mode to Windowed mode.
Escape
This signals the window to close and the application to shutdown.
   protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e){

      switch(e.KeyCode) {
         case Keys.F:
            FullScreen = true;
            break;
         case Keys.W:
            FullScreen = false;
            break;
         case Keys.Escape:
            this.Close();
            break;
         default:
            base.OnKeyDown(e);
            break;
      }

   }
   Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)
      Select Case e.KeyCode
         Case Keys.F
            Me.FullScreen = True
         Case Keys.W
            Me.FullScreen = False
         Case Keys.Escape
            Me.Close()
         Case Else
            MyBase.OnKeyDown(e)
      End Select
   End Sub

Constructor

There's only 1 little thing left to cover in the D3DWindow class: the Constructor. And there is very little to it. It sets the background colour and starting position. It turns off AutoScale (which is probably not necessary). Finally it calls GoWindowed.

NOTE: After the constructor has finished, the window is still not visible. This makes it easy for apps inheriting from D3DWindow to tweak whatever settings you like without the window visibly changing.

  public D3DWindow() {

      //This makes the window auto-scale based on font size.  Since we aren't adding

      //controls we probably don't need to turn it off, but it doesn't hurt.

      AutoScale = false;

      //Background colour

      BackColor = System.Drawing.Color.Black;

      //Center this window

      StartPosition = FormStartPosition.CenterScreen;

      //Use Windowed as default

      GoWindowed();

   }
   Public Sub New()
      'This makes the window auto-scale based on font size.  Since we aren't adding

      'controls we probably don't need to turn it off, but it doesn't hurt.

      Me.AutoScale = False

      'Background colour

      Me.BackColor = System.Drawing.Color.Black

      'Center this window

      Me.StartPosition = FormStartPosition.CenterScreen

      'Use Windowed as default

      MyClass._GoWindowed()
   End Sub

Now that we've covered D3DWindow, we can show it being used in our sample app.

Using D3DWindow

Normally we would inherit our app from System.Windows.Forms.Form. Now we inherit from DrunkenHyena.D3DWindow. Since this application is in the DrunkenHyena namespace already it doesn't have to be specified, but we do so for clarity.

   public class Windows_Primer_2 : DrunkenHyena.D3DWindow
   Public Class WindowsPrimer2
      Inherits DrunkenHyena.D3DWindow

We declare 2 variables to hold our application name and our desired client size. Our application name (a string) is declared as const, but our client size is declared read only. This is because reference types declared as const cannot be initialized to arbitrary values. For our purposes here const and readonly are equivalent.

   protected readonly Size m_size = new Size(800,600);
   protected const string m_name = "Windows Primer Part 2";
   Protected ReadOnly _size As Size = New Size(800, 600)
   Protected Const _name As String = "Windows Primer Part 2"

With all of the heavy lifting handled by D3DWindow, the code inside our application constructor is only 5 lines long.

   public Windows_Primer_2(){

      //Set icon in top-left corner of Window

      System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Windows_Primer_2));
      this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));

      //Set our desired client size

      this.ClientSize = m_size;

      //Set window title

      this.Text = m_name;

      //Query user to see if they want full-screen or Windowed mode

      this.FullScreen = Utility.AskFullscreen(m_name);

   }
   Public Sub New()
      MyBase.New()

      'Set icon in top-left corner of Window

      Dim resources As System.Resources.ResourceManager = _
         New System.Resources.ResourceManager(GetType(WindowsPrimer2))
      Me.Icon = CType(resources.GetObject("$this.Icon"), Icon)

      'Set our desired client size

      Me.ClientSize = _size

      'Set window title

      Me.Text = _name

      'Query user to see if they want full-screen or Windowed mode

      Me.FullScreen = Utility.AskFullScreen(_name)
   End Sub

Wrapping Up

Hopefully you see the benefits of this method as opposed to the procedural method used in the previous tutorial. With very little effort we now have a nice base to build on for future tutorials.

Lesson Downloads

Back