Drawing rectangle after keyboard input

closed account (Gy7oizwU)
Hi

I am relatively new at Win32 API programming but have done DOS based applications. I am trying to create a GUI application that when i push a button, it draws a rectangle. When i hit the key to draw the rectangle, nothing happens.I think i am missing something to refresh the screen? I have a code snippet included. Any help would be much appreciated. Thanks

case WM_KEYDOWN:
switch(wParam)
{
case VK_UP:
hdc = BeginPaint(hWnd, &ps);
Rectangle(hdc,100, 200, 150, 300);
EndPaint(hWnd, &ps);
}
case WM_LKEYDOWN:
{
startpaintting;
hdc=fshwnd(&%¤#%¤&blabla)
restankkel(42,1337,666,-5);;;;;
endpaintting;
}

try something like that? :P
Last edited on
AFAIK, BeginPaint / EndPaint should only be used in WM_PAINT's handler.

You have two options here:

The more preferable solution: Make a state change that marks when the rectangle needs to be drawn. Then Invalidate the rect to tell Windows that you need to redraw the window.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
case WM_KEYDOWN:
  switch(wParam)
  {
  case VK_UP:
    drawrectangle = true;
    InvalideRect( hWnd, your_rect );
    break;
  }break;

case WM_PAINT:
  BeginPaint(...);
  if(drawrectangle)
    DrawYourRectangle();
  EndPaint(...);
  break;
}


The less preferable solution: Use GetDC() / ReleaseDC() instead of BeginPaint() / EndPaint()

The reason this is less preferable is because it will only draw the rectangle one time. If the window needs to be redrawn (for instance if another window covers it, or you move it off screen, or whatever) the rectangle will disappear.

When the window needs to be redrawn, windows will send you WM_PAINT messages. So you need to be able to draw the window as you want it to appear from WM_PAINT -- so you should try to keep all of your drawing code in WM_PAINT.
closed account (Gy7oizwU)
Hi

Thanks for the help. I understand what i have to do but i can't seem to get it to work. I am stuck with the InvalidateRect part. I've created a function to draw the rectangle and call in in WM_PAINT. I'm definitely not doing something right. Please help.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
case WM_KEYDOWN:
		switch(wParam)
		{
		    case VK_UP:
		    drawrectangle = true;
		    InvalidateRect(hWnd, RECT, TRUE);
		    break;
		}
		break;

case WM_PAINT:
		BeginPaint(hWnd, &ps);
		if(drawrectangle=true)
			DrawMyRectangle(hdc);
		EndPaint(hWnd, &ps);
		break;

void DrawMyRectangle(HDC hdc)
{
	Rectangle(hdc,69,63,98,120);

}
RECT is a struct, not an object. You have to fill the RECT with the actual coords of the rectangle:

1
2
3
4
5
6
7
RECT rect;
rect.left = whatever;
rect.top = whatever;
rect.right = whatever;
rect.bottom = whatever;

InvalidateRect(hWnd, &rect, TRUE);


EDIT: Also I think you can shortcut it:

 
RECT rect = {left, top, right, bottom};


Or if you're lazy, you can use NULL to invalidate the entire client area (ie: the whole window, not just a specific rectangle):

 
InvalidateRect(hWnd,NULL,TRUE);



Also -- line 13 should have == (comparison), not = (assignment)
Last edited on
closed account (Gy7oizwU)
Ok, so i've corrected the InvalidateRect function but i still can't get t to draw the rectangle. I've added all my code so you can point out where i go wrong. Most of the code is given when creating the project. Really appreciate your help so far, I'm still learning.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include "stdafx.h"
#include "Rotating Rectangles.h"

#define MAX_LOADSTRING 100

void DrawMyRectangle(HDC hdc, RECT rect);

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name

RECT rect = {100, 150, 30, 190};

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_ROTATINGRECTANGLES, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_ROTATINGRECTANGLES));

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ROTATINGRECTANGLES));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_ROTATINGRECTANGLES);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	bool drawrectangle = false;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
	case WM_KEYDOWN:
		switch(wParam)
		{
			case VK_UP:
		    drawrectangle = true;
			InvalidateRect(hWnd, &rect, TRUE);
			break;
		}
		break;

	case WM_PAINT:
		BeginPaint(hWnd, &ps);
		if(drawrectangle==true)
			DrawMyRectangle(hdc, rect);
		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

void DrawMyRectangle(HDC hdc, RECT rect)
{
	Rectangle(hdc,rect.bottom, rect.left, rect.right, rect.top);
}
you made drawrectangle local to WndProc. You'll need to give it a broader scope.
closed account (Gy7oizwU)
I've made drawrectangle a global variable but now when i compile the program, it says: Run-Time Check Failure #3 - The variable 'hdc' is being used without being initialized. If i make HDC a global variable, it runs but nothing happens when i push the up key.
Last edited on
Making hdc global doesn't solve anything. You still have the same problem.

The variable 'hdc' is being used without being initialized


This means exactly what it says. You never assign hdc to anything.

IIRC, BeginPaint() returns the HDC, so you'd do this:

1
2
3
HDC hdc = BeginPaint(...);
DrawStuff(hdc);
EndPaint(...);
Topic archived. No new replies allowed.