Getting started with Windows GDI graphics applications in C++

A guide to getting started with Windows graphics applications for the very first time. The Windows Graphics Device Interface (GDI) forms the basis of drawing lines and objects, and from this device contexts. I will not go into any detail about these concepts in this post. I just want to show a simple means of getting started with things like the drawing of lines and objects in Windows applications. There is nothing stopping you from reading further on the subject to increase your understanding.

I have used Microsoft Visual Studio 2003 as the means of demonstration, but this should also be be reasonably applicable to newer versions of Visual Studio, give or take some superficial differences in layouts, wizards etc.

Step 1: Create a Windows dialog application

In File → New → Project, select a new MFC Application:

In the Application Type, choose the dialog-based application:

In the User Interface Features section, I unchecked all boxes where possible, to keep the code to a minimum:

Ditto the Advanced Features section, while leaving any other settings as defaults. You can always experiment with the settings at a later stage. For now the focus is just on getting started with Windows graphics. Click on Finish when you are done:

Step 2: Set up your dialog

In Visual Studio, build your application. When running the solutin in Debug or Release mode, you are presented with a simple dialog similar to this one, containing the OK/Cancel buttons added as defaults, a text label and little else:

In Visual Studio, go to the Resource View and edit the dialog that was created by double-clicking on the dialog icon:

In the dialog editor, I remove the text label that says “TODO: Place dialog controls here:

Step 3: Write / modify the GDI code

In this demonstration, I want the sample objects to be drawn when the user clicks on ‘OK’. While you are still in the dialog editor, double-click on the OK button. This will create and direct you to the OnBnClickedOk section of code.

void CGraphicDlg::OnBnClickedOk()
{
	// TODO: Add your control notification handler code here
	OnOK();     
}

In the dialog header file (called GraphDlg.h in my example), I add an additional boolean flag called display as shown:

// GraphicDlg.h : header file
//
#pragma once

// CGraphicDlg dialog
class CGraphicDlg : public CDialog
{
// Construction
public:
	CGraphicDlg(CWnd* pParent = NULL);	// standard constructor

// Dialog Data
	enum { IDD = IDD_GRAPHIC_DIALOG };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support

// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedOk();
private:
	bool display;
};

In the GraphicDlg class constructor, I set the initial value of display to false:

CGraphicDlg::CGraphicDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CGraphicDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	display = false;
}

Going back to OnBnClickedOk, display is set to true, in order to tell the application that when the user click OK, it will display the graphics. RedrawWindow forces the application to redraw the window so that the display is refreshed and the lines, objects get draw. Not having this in place usually results in the graphics objects not being drawn when you want them to:

void CGraphicDlg::OnBnClickedOk()
{
	display = true;

	// Re-draw the window	
	RedrawWindow( 0, 0, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE );	
}

Step 4: Draw the graphical objects

In order to draw the objects of your choosing, it is necessary to modify the OnPaint() routine. For this example, I show how to use the device contexts in to draw some arbitrary black lines and ellipses. Feel free to read further into how to use other kinds of shapes, colors, brushes, texts etc. Once you have the basic, it is a matter of refining your application to suit your own needs. Here is the modified OnPaint():

void CGraphicDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		if ( display )
		{
			// device context for painting
	        CPaintDC dc(this);

			CPen pen( PS_SOLID, 
				      1, 
				      RGB( 0, 0, 0 ) );
			dc.SelectStockObject(BLACK_BRUSH);	

			// Draw some lines...					   
			dc.MoveTo( 100, 50 );
		    dc.LineTo( 200, 52 );	
			dc.MoveTo( 105, 47 );
		    dc.LineTo( 202, 60 );
			dc.MoveTo( 85,  85 );
		    dc.LineTo( 178, 64 );
			dc.MoveTo( 75,  99 );
		    dc.LineTo( 128, 66 );

			// Draw some ellipses
			dc.Ellipse( 250, 118, 254, 114 );	
			dc.Ellipse( 255, 138, 260, 133 );	
			dc.Ellipse( 260, 148, 265, 153 );	
			dc.Ellipse( 240, 177, 248, 173 );	
			dc.Ellipse( 259, 158, 263, 162 );	
		}

		CDialog::OnPaint();
	}
}

That is pretty much all there is to it. When running the application and clicking on OK, the lines and blobs get drawn as shown:

Visual Studio 2003 project downloadable from here.