Simple Window

Win32++

Simple Window

Description

A window typically occupies a rectangular portion of the screen. It is managed by the Windows operating system, and is capable of responding to events and redrawing itself. A window has a handle (often referred to as a HWND) through which a program can control the window or send it messages.

There are many different types of windows. In this section we will focus on a simple application window. The following sections will focus on more specialized widows, such as dialogs, toolbars, status bars, edit controls and so forth.

In Win32++, the CWnd class is used to create and control windows. We inherit a class from CWnd and override OnPaint to control the way our window looks, and override WndProc to control how it responds to messages. Refer to the description of CWnd in the Class Library section of this documentation for a description of the functions which can be overridden.

Components

The following diagram illustrates the components of a typical application window.

Typically an application will draw over the client area of the window, but leave the drawing of the non-client area to the operating system. Note that a child window (a window contained within another window) might not have a visible non-client area.

This diagram was created from an image capture of the window created by the "Simple" sample (one of the samples that ship with Win32++).

Creating a Window

Its important to note that the CWnd's window isn't created when the CWnd is constructed. The window is created some time later, when either the Create or the CreateEx function is called.

Normally the Create function is used to create the window. This function allows us to specify the parent window, if any.  It also calls PreRegisterClass to set the window class parameters, and PreCreate to set the creation parameters before creating the window.  The PreRegisterClass and PreCreate can be overridden to set these parameters prior to window creation, as shown below.

void CView::PreRegisterClass(WNDCLASS &wc)
{
  // Set the background brush, class name and cursor
  wc.hbrBackground = m_hBrush;
  wc.lpszClassName = _T("Scribble Window");
  wc.hCursor = ::LoadCursor(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDC_CURSOR1));
}

Additional notes for PreRegisterClass:

  • The lpszClassName must be set for this function to take effect.
  • The lpfnWndProc is always CWnd::StaticWindowProc.
  • The styles that can be set here are "class" styles. These are a different set of styles to those set by CREATESTRUCT (used in PreCreate).
  • Default class creation parameters are not set when PreRegisterClass is used, so the following settings might prove useful:
    • wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    • wc.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
    • wc.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
void CView::PreCreate(CREATESTRUCT &cs)
{
  // Set the extra style to provide a sunken effect
  cs.dwExStyle = WS_EX_CLIENTEDGE;
}

Additional notes for PreCreate:

  • The cs.lpszClass defaults to the class name specified in PreRegisterClass, or "Win32++ Window" otherwise.
  • The styles that can be set here are "window" styles. These are a different to the set of "class" styles used in PreRegisterClass.
  • A default style of WS_VISIBLE and possibly WS_OVERLAPPEDWINDOW is used if no style is specified. Be sure to add these styles if required when specifying the window style.

Create is the function which would normally be used to create a window, but sometimes it is more convenient to take more direct control over the various window creation parameters.  CreateEx is the function which allows the various window parameters to be set directly when the window is created.

The OnCreate function is called during window creation. This function can be overridden to perform addition tasks during window creation, such as creating child windows.  The OnInitialUpdate function is called once window creation is complete, so this function can be overridden to perform tasks after the window has been successfully created.

Painting the Window

In order to paint on the window, we override OnDraw and put our drawing code there. OnDraw is called automatically whenever all, or a part of the window needs to be redrawn. OnDraw provides us with a pointer to the client area's device context. All normal drawing for a window is done via a device context.

The following code loads a text string from a string resource and centers it on the client area of the window.

void CView::OnDraw(CDC* pDC)
{
  // OnDraw is called automatically whenever a part of the window needs to be redrawn.

  // Centre some text in our view window
  CRect rc = GetClientRect();
  pDC->DrawText(LoadString(IDW_MAIN), rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
}

Handling Messages

Each CWnd handles the messages for its window.  Override WndProc to specify how the CWnd should handle messages.  Any unhandled messages are passed on to the default window procedure.

A WndProc function could look something like this:

LRESULT CView::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  // This function is our message procedure. We process the messages for
  // the view window here.  Unprocessed messages are passed on for
  //  default processing.

  switch(uMsg)
  {
  case WM_DESTROY:
    OnDestroy();
    return 0;	// return a value. No default processing

  case WM_SIZE:
    OnSize();
    break;	// and also do default processing for this message
  }

  // pass unhandled messages on for default processing
  return WndProcDefault(uMsg, wParam, lParam);
}