Text dissapearing when resizing or minimizing window.

Started by
8 comments, last by SteveHatcher 9 years, 5 months ago

Hello all,

I have a simple win32 window that opens a window and accepts some user input based on this code

http://msdn.microsoft.com/en-us/library/windows/desktop/ms648398%28v=vs.85%29.aspx

The last section "Processing Keyboard Input" contains the code I used.

It all appears to be working fine, except whenever the window is resized, or minimized, any text that has been entered disappears. I have been searching for a long time on how to fix this but cannot figure it out.

The full program I am using is below. This program can be copied into visual studio and compiled, make sure the character set is set to "Use Multi-Byte". Any help would be greatly appreciated. Thanks.


// Program to allow a user to type into a window

#define WIN32_LEAN_AND_MEAN
#define BUFSIZE 65535 
#define SHIFTED 0x8000 

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <Strsafe.h>

// Function prototypes
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
bool CreateMainWindow(HINSTANCE, int);
LRESULT WINAPI WndProc(HWND, UINT, WPARAM, LPARAM);

// Global variables
HINSTANCE hInst;
HDC hdc;                          // device context  
RECT rect;					// output rectangle for DrawText
PAINTSTRUCT ps;                   // client area paint info 

// Constants
static TCHAR CLASS_NAME[] = _T("win32app");
static TCHAR APP_TITLE[] = _T("Win32 Guided Tour Application");	// title bar text
const int WINDOW_WIDTH = 400;									// width of window
const int WINDOW_HEIGHT = 400;									// height of window

#define TEXTMATRIX(x, y) *(pTextMatrix + (y * nWindowCharsX) + x) 

//=============================================================================
// Application entry point
//=============================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;

	// Create the window
	if (!CreateMainWindow(hInstance, nCmdShow))
		return false;

	// Main message loop
	int done = 0;
	while (!done)
	{
		// Look for quit message
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				done = 1;

			// Decode and pass messages on to WndProc
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int)msg.wParam;
}

//=============================================================================
// Window event callback function
//=============================================================================
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

	HBITMAP hCaret;                   // caret bitmap 

	static char *pTextMatrix = nullptr;  // points to text matrix 
	static int  nCharX,               // width of char. in logical units 
		nCharY,               // height of char. in logical units 
		nWindowX,             // width of client area 
		nWindowY,             // height of client area 
		nWindowCharsX,        // width of client area 
		nWindowCharsY,        // height of client area 
		nCaretPosX,           // x-position of caret 
		nCaretPosY;           // y-position of caret 
	static UINT uOldBlink;            // previous blink rate 
	int x, y;                         // coordinates for text matrix 
	TEXTMETRIC tm;                    // font information 

	switch (msg)
	{
	case WM_CREATE:
		// Select a fixed-width system font, and get its text metrics.

		hdc = GetDC(hwnd);
		SelectObject(hdc,
			GetStockObject(SYSTEM_FIXED_FONT));
		GetTextMetrics(hdc, &tm);
		ReleaseDC(hwnd, hdc);

		// Save the avg. width and height of characters. 

		nCharX = tm.tmAveCharWidth;
		nCharY = tm.tmHeight;

		return 0;

	case WM_SIZE:
		// Determine the width of the client area, in pixels 
		// and in number of characters. 

		nWindowX = LOWORD(lParam);
		nWindowCharsX = max(1, nWindowX / nCharX);

		// Determine the height of the client area, in 
		// pixels and in number of characters. 

		nWindowY = HIWORD(lParam);
		nWindowCharsY = max(1, nWindowY / nCharY);

		// Clear the buffer that holds the text input. 

		if (pTextMatrix != NULL)
			free(pTextMatrix);

		// If there is enough memory, allocate space for the 
		// text input buffer. 

		pTextMatrix = (char*)malloc(nWindowCharsX * nWindowCharsY);

		if (pTextMatrix == NULL)
			//ErrorHandler("Not enough memory.");
			break;
		else
		for (y = 0; y < nWindowCharsY; y++)
		for (x = 0; x < nWindowCharsX; x++)
			TEXTMATRIX(x, y) = ' ';

		// Move the caret to the origin. 

		SetCaretPos(0, 0);

		return 0;

	case WM_KEYDOWN:
		switch (wParam)
		{
		case VK_HOME:       // Home 
			nCaretPosX = 0;
			break;

		case VK_END:        // End 
			nCaretPosX = nWindowCharsX - 1;
			break;

		case VK_PRIOR:      // Page Up 
			nCaretPosY = 0;
			break;

		case VK_NEXT:       // Page Down 
			nCaretPosY = nWindowCharsY - 1;
			break;

		case VK_LEFT:       // Left arrow 
			nCaretPosX = max(nCaretPosX - 1, 0);
			break;

		case VK_RIGHT:      // Right arrow 
			nCaretPosX = min(nCaretPosX + 1, nWindowCharsX - 1);
			break;

		case VK_UP:         // Up arrow 
			nCaretPosY = max(nCaretPosY - 1, 0);
			break;

		case VK_DOWN:       // Down arrow 
			nCaretPosY = min(nCaretPosY + 1, nWindowCharsY - 1);
			break;

		case VK_DELETE:     // Delete 

			// Move all the characters that followed the 
			// deleted character (on the same line) one 
			// space back (to the left) in the matrix. 

			for (x = nCaretPosX; x < nWindowCharsX; x++)
				TEXTMATRIX(x, nCaretPosY) = TEXTMATRIX(x + 1, nCaretPosY);

			// Replace the last character on the 
			// line with a space. 

			TEXTMATRIX(nWindowCharsX - 1, nCaretPosY) = ' ';

			// The application will draw outside the 
			// WM_PAINT message processing, so hide the caret. 

			HideCaret(hwnd);

			// Redraw the line, adjusted for the 
			// deleted character. 

			hdc = GetDC(hwnd);
			SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

			TextOut(hdc, nCaretPosX * nCharX, nCaretPosY * nCharY, &TEXTMATRIX(nCaretPosX, nCaretPosY), nWindowCharsX - nCaretPosX);

			ReleaseDC(hwnd, hdc);

			// Display the caret. 

			ShowCaret(hwnd);

			break;
		}

		// Adjust the caret position based on the 
		// virtual-key processing. 

		SetCaretPos(nCaretPosX * nCharX, nCaretPosY * nCharY);

		return 0;

	case WM_CHAR:
		switch (wParam)
		{
		case 0x08:          // Backspace 
			// Move the caret back one space, and then 
			// process this like the DEL key. 

			if (nCaretPosX > 0)
			{
				nCaretPosX--;
				SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1L);
			}
			break;

		case 0x09:          // Tab 
			// Tab stops exist every four spaces, so add 
			// spaces until the user hits the next tab. 

			do
			{
				SendMessage(hwnd, WM_CHAR, ' ', 1L);
			} while (nCaretPosX % 4 != 0);
			break;

		case 0x0D:          // Carriage return 
			// Go to the beginning of the next line. 
			// The bottom line wraps around to the top. 

			nCaretPosX = 0;

			if (++nCaretPosY == nWindowCharsY)
				nCaretPosY = 0;
			break;

		case 0x1B:        // Escape 
		case 0x0A:        // Linefeed 
			MessageBeep((UINT)-1);
			break;

		default:
			// Add the character to the text buffer. 

			TEXTMATRIX(nCaretPosX, nCaretPosY) = (char)wParam;

			// The application will draw outside the 
			// WM_PAINT message processing, so hide the caret. 

			HideCaret(hwnd);

			// Draw the character on the screen. 

			hdc = GetDC(hwnd);
			SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

			TextOut(hdc, nCaretPosX * nCharX, nCaretPosY * nCharY, &TEXTMATRIX(nCaretPosX, nCaretPosY), 1);
			InvalidateRect(hwnd, NULL, TRUE);
			ReleaseDC(hwnd, hdc);

			// Display the caret. 

			ShowCaret(hwnd);

			// Prepare to wrap around if you reached the 
			// end of the line. 

			if (++nCaretPosX == nWindowCharsX)
			{
				nCaretPosX = 0;
				if (++nCaretPosY == nWindowCharsY)
					nCaretPosY = 0;
			}
			InvalidateRect(hwnd, NULL, TRUE);
			break;
		}

		// Adjust the caret position based on the 
		// character processing. 

		SetCaretPos(nCaretPosX * nCharX, nCaretPosY * nCharY);

		return 0;

	case WM_PAINT:
		// Draw all the characters in the buffer, line by line. 

		hdc = BeginPaint(hwnd, &ps);

		SelectObject(hdc,
			GetStockObject(SYSTEM_FIXED_FONT));

		for (y = 0; y < nWindowCharsY; y++)
			TextOut(hdc, 0, y * nCharY, &TEXTMATRIX(0, y),
			nWindowCharsX);

		EndPaint(hwnd, &ps);

	case WM_SETFOCUS:
		// The window has the input focus. Load the 
		// application-defined caret resource. 

		hCaret = LoadBitmap(hInst, MAKEINTRESOURCE(120));

		// Create the caret. 

		//CreateCaret(hwnd, hCaret, 0, 0);
		CreateCaret(hwnd, (HBITMAP)NULL, 2, nCharY);

		// Adjust the caret position. 

		SetCaretPos(nCaretPosX * nCharX, nCaretPosY * nCharY);

		// Display the caret. 

		ShowCaret(hwnd);

		break;

	case WM_KILLFOCUS:
		// The window is losing the input focus, 
		// so destroy the caret. 

		DestroyCaret();

		break;

	case WM_DESTROY:
		PostQuitMessage(0);

		// Free the input buffer

		GlobalFree((HGLOBAL)pTextMatrix);
		UnregisterHotKey(hwnd, 0xAAAA);

		break;

	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);

	}

	return NULL;
}


bool CreateMainWindow(HINSTANCE hInstance, int nCmdShow)
{
	WNDCLASSEX wcex;
	HWND hwnd;

	// Fill in the window class structure with parameters that describe the main window
	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_APPLICATION));
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = CLASS_NAME;
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

	// Register the window class. 
	// RegisterClassEx returns 0 on error.
	if (!RegisterClassEx(&wcex))
	{
		MessageBox(NULL,
			_T("Call to RegisterClassEx failed!"),
			_T("Win32 Guided Tour"),
			NULL);

		return 1;
	}

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

	// Create window
	hwnd = CreateWindow(
		CLASS_NAME,
		APP_TITLE,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		WINDOW_WIDTH,
		WINDOW_HEIGHT,
		NULL,
		NULL,
		hInstance,
		NULL
		);

	if (!hwnd)
	{
		MessageBox(NULL,
			_T("Call to CreateWindow failed!"),
			_T("Win32 Guided Tour"),
			NULL);

		return false;
	}

	// The parameters to ShowWindow explained:
	// hwnd: the value returned from CreateWindow
	// nCmdShow: the fourth parameter from WinMain
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	return true;
}
Advertisement

In WndProc you have a switch(msg), one possible value for msg is "WM_SIZE", which is fired when the window is resized: http://msdn.microsoft.com/en-us/library/windows/desktop/ms632646%28v=vs.85%29.aspx

At the middle of that block of code you can find this lines:


// Clear the buffer that holds the text input. 

if (pTextMatrix != NULL)
	free(pTextMatrix);

If you remove those lines the text shouldn't dissapear, but I guess you'll have some other problems. So, maybe what you need to do is:

1 - Create the new matrix first

2 - Copy all the text from the old matrix to the new one

3 - Free the current matrix and replace the pointer with the new one

I guess that minimize and maximize buttons trigger the same msg, but if not, look for other places in the code where pTextMatrix is erased.

You should do all your drawing inside the WM_PAINT handler, otherwise it will be overwritten when the client area is redrawn.

In WndProc you have a switch(msg), one possible value for msg is "WM_SIZE", which is fired when the window is resized...

Hi, I think I understand, and commented out those 2 lines of code, but the text still disappears. Thanks

You should do all your drawing inside the WM_PAINT handler, otherwise it will be overwritten when the client area is redrawn.

Hi, I have found a few other posts mention this too, but I am not sure how to implement it. How should I structure the code to work like this?

Thanks



Prototype, on 18 Nov 2014 - 4:09 PM, said:
You should do all your drawing inside the WM_PAINT handler, otherwise it will be overwritten when the client area is redrawn.

Hi, I have found a few other posts mention this too, but I am not sure how to implement it. How should I structure the code to work like this?


It's already there in your code: 'case WM_PAINT', which is the section called when the OS wants to draw part of your window. That's where you put your drawing commands.



Prototype, on 18 Nov 2014 - 4:09 PM, said:
You should do all your drawing inside the WM_PAINT handler, otherwise it will be overwritten when the client area is redrawn.

Hi, I have found a few other posts mention this too, but I am not sure how to implement it. How should I structure the code to work like this?


It's already there in your code: 'case WM_PAINT', which is the section called when the OS wants to draw part of your window. That's where you put your drawing commands.

Hi,

Is this not already happening as the TextOut() function in the WM_PAINT case as below? Thanks.


	case WM_PAINT:
		// Draw all the characters in the buffer, line by line. 

		hdc = BeginPaint(hwnd, &ps);

		SelectObject(hdc,
			GetStockObject(SYSTEM_FIXED_FONT));

		for (y = 0; y < nWindowCharsY; y++)
			TextOut(hdc, 0, y * nCharY, &TEXTMATRIX(0, y),
			nWindowCharsX);

		EndPaint(hwnd, &ps);

In WM_SIZE the code


if (pTextMatrix != NULL) free(pTextMatrix); // If there is enough memory, allocate space for the  // text input buffer.
pTextMatrix = (char*)malloc(nWindowCharsX * nWindowCharsY); 

deletes all of the text you have entered, and then recreates pTextMatrix based on the number of characters you can fit in the client window. You need to move the allocation/de-allocation somewhere else (possibly allocate in WM_CREATE and de-allocate in WM_DESTROY).

Also, there should be a "break;" at the end of WM_PAINT, because at the moment the code falls straight into WM_SETFOCUS. It's a pretty shoddy code example from MSDN, to be honest.


deletes all of the text you have entered, and then recreates pTextMatrix based on the number of characters you can fit in the client window.

Just for clarity, I would add to that correct diagnosis (thumbs up): "The pTextMatrix is then filled with spaces, which are then displayed, rather than the previously entered text."

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Thanks guys,

I understand now why it is wiping. I have also a text input demo using this code:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms646268%28v=vs.85%29.aspx

and it does not wipe the text on a resize. They both process input in quite different ways (I think the caret code is based on older C style code). But the caret code has more functionality such as processing a backspace, where as this new one does not. So I will try to adapt this new one with full functionality. It is very hard because there are very few examples of this stuff, just whats on the mdsn mainly... Thanks for all your help

This topic is closed to new replies.

Advertisement