[Win32 API] Set text and background color of static label?

Did some reading but am so far unsuccessful. I am trying to set the background color and text color of a type "STATIC" window. Below is the code and what I've tried. I assume the solution is in WM_PAINT but I've put the same color changing code in there with no luck.

P.S. I did post this in "Beginners" forum with not a lot of activity. I assume this is not material for a beginners post. Sorry for that.

1
2
3
4
case WM_CREATE:
userLbl = CreateWindowEx(NULL, TEXT("Static"), TEXT("Username: "), WS_CHILD | WS_VISIBLE, 10, 20, 75, 20, hWnd, NULL, NULL, NULL);
		SetTextColor(userLbl, RGB(255, 255, 255));
		SetBkColor(userLbl, RGB(0, 0, 0));


I figured this would work, but no luck so far.
1
2
SetTextColor(userLbl, RGB(255, 255, 255));
SetBkColor(userLbl, RGB(0, 0, 0));
That's actually a bit complicated CGunn86. You need to handle the WM_CTLCOLORSTATIC message I believe, and return from the message the HBRUSH (you need to allocate it) for the background color....

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

Let me know how it goes.

freddie
@freddie1

Looks like this did it. Had to declare the "HDC hdcStatic" outside of the switch statement but otherwise seems to work. Is there a way to change the border of an edit window though? I can change the background of the text box but it changes its border as well.

1
2
3
4
5
	case WM_CTLCOLORSTATIC:
		SetTextColor(hdcStatic, RGB(255, 255, 255));
		SetBkColor(hdcStatic, RGB(0, 0, 0));
		return (INT_PTR)CreateSolidBrush(RGB(0, 0, 0));
		break;
Last edited on
I think you can just replace the hdcStatics with GetWindowDC(usrLbl)
https://msdn.microsoft.com/en-us/library/windows/desktop/dd144947(v=vs.85).aspx
@Homberto GetWindowDC(usrLbl) doesn't throw any syntax errors but doesn't seem to modify anything, so I guess that method doesnt work. The way I'm doing it now seems to work fine but I can't seem to just change the border color of the text field, just the text color or the background color.

I'm not entirely sure what return (INT_PTR)CreateSolidBrush(RGB(0, 0, 0)); is doing but all I see it doing is putting a solid colored line in whatever RGB value I give it, under the text field.
Last edited on
When you receive a WM_CTLCOLORSTATIC message, the wParam is a HANDLE to the 'internal' Device Context ( HDC ) Windows is using to draw the control. Therefore, you simply need to only recast it to calls that affect the background drawing. I use...

 
SetBkMode((HDC)wParam,TRANSPARENT);


Also, in your code as shown you are leaking an HBRUSH object every time Windows repaints your control. From MSDN on WM_CTLCOLORSTATIC...


If the application returns a brush that it created (for example, by using the CreateSolidBrush or CreateBrushIndirect function), the application must free the brush. If the application returns a system brush (for example, one that was retrieved by the GetStockObject or GetSysColorBrush function), the application does not need to free the brush.


Below is a simple SDK style app with comments and explanations showing what I believe to be the proper way to do this....

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
//Main.cpp
#ifndef UNICODE
    #define            UNICODE
#endif
#ifndef _UNICODE
    #define            _UNICODE
#endif
#include <windows.h>
#define IDC_LABEL1     2000


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_CREATE:
    {
        HINSTANCE hIns = ((LPCREATESTRUCT)lParam)->hInstance;
        HWND hCtl      = CreateWindowEx(0,L"static",L"Label1",WS_CHILD|WS_VISIBLE,40,40,80,30,hWnd,(HMENU)IDC_LABEL1,hIns,0);
        return 0;
    }
   case WM_CTLCOLORSTATIC:                             // Do not allow DefWindowProc() to process this message!
    {                                                  // When a WM_CTLCOLORSTATIC message comes through, return
        SetBkMode((HDC)wParam,TRANSPARENT);            // from the Window Procedure call without a DefWindowProc()
        return GetWindowLongPtr(hWnd,0*sizeof(void*)); // call. Instead return the HBRUSH stored as an instance
    }                                                  // variable as part of the WNDCLASSEX object.
   case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }
 }

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 wchar_t szClassName[]=L"Form1";
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 memset(&wc,0,sizeof(WNDCLASSEX));
 HBRUSH hBrush    = CreateSolidBrush(RGB(255,255,222));   // Create a non-standard color HBRUSH for main window
 wc.lpszClassName = szClassName;
 wc.lpfnWndProc   = fnWndProc;
 wc.cbSize        = sizeof(WNDCLASSEX);
 wc.hbrBackground = hBrush;
 wc.hInstance     = hInstance;
 wc.cbWndExtra    = sizeof(void*);
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,200,175,320,200,HWND_DESKTOP,0,hInstance,0);
 SetWindowLongPtr(hWnd,0*sizeof(void*),(LONG_PTR)hBrush); // Store HBRUSH as part of Window Class Structure
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }
 DeleteObject(hBrush);                                    // Delete dynamically allocated GDI resource

 return messages.wParam;
}


Also, just as an aside, its possible in a somewhat obtuse way to accomplish this by subclassing the label and overriding the WM_PAINT message.
Last edited on
@freddie1 Excellent response! Thank you. I'll pull your example code down and read it over. You answered a bonus question in that I wasnt sure if every objected created needed to be destroyed or if Windows handled that. So thanks for pointing that out as well.

You answered a bonus question in that I wasnt sure if every objected created needed to be destroyed or if Windows handled that.


When I wrote that the way I did I seemed to remember testing that once long ago, and I was pretty sure that's the way it worked. But it wouldn't be unreasonable either to assume Windows might cache it somehow and reuse it as you mentioned. So I thought I'd check it again by adding some output log statements to my program above to see if there are multiple WM_CTLCOLORSTATC messages in a run of the program, and it seems indeed that there are. So below is the modified program with the output log following in #if conditionals. I had minimized and restored the program twice, and it shows three calls to the WM_CTLCOLORSTATIC handling code...

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
//Main.cpp
#ifndef UNICODE
    #define            UNICODE
#endif
#ifndef _UNICODE
    #define            _UNICODE
#endif
#include <windows.h>
#include <cstdio>
#define IDC_LABEL1     2000
FILE* fp;


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_CREATE:
    {
        fprintf(fp,"  Entering fnWndProc() -- WM_CREATE\n");
        HINSTANCE hIns = ((LPCREATESTRUCT)lParam)->hInstance;
        CreateWindowEx(0,L"static",L"Label1",WS_CHILD|WS_VISIBLE,40,40,80,30,hWnd,(HMENU)IDC_LABEL1,hIns,0);
        fprintf(fp,"    hWnd = 0x%p\n",hWnd);
        fprintf(fp,"  Leaving fnWndProc() -- WM_CREATE\n\n");
        return 0;
    }
   case WM_CTLCOLORSTATIC:
    {
        fprintf(fp,"  Entering fnWndProc()  -- WM_CTLCOLORSTATIC\n");
        SetBkMode((HDC)wParam,TRANSPARENT);
        HBRUSH hBrush=(HBRUSH)GetWindowLongPtr(hWnd,0*sizeof(void*));
        fprintf(fp,"    hBrush = 0x%p\n",hBrush);
        fprintf(fp,"  Leaving fnWndProc()  -- WM_CTLCOLORSTATIC\n\n");
        return (LRESULT)hBrush;
    }
   case WM_DESTROY:
    {
        fprintf(fp,"  Entering fnWndProc() -- WM_DESTROY\n");
        fprintf(fp,"    hWnd = 0x%p\n",hWnd);
        PostQuitMessage(0);
        fprintf(fp,"  Leaving fnWndProc() -- WM_DESTROY\n\n");
        return 0;
    }
 }

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 wchar_t szClassName[]=L"Form1";
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 fp=fopen("Output.txt","w");
 fprintf(fp,"Entering WinMain()\n");
 memset(&wc,0,sizeof(WNDCLASSEX));
 HBRUSH hBrush    = CreateSolidBrush(RGB(255,255,222));
 fprintf(fp,"  hBrush = 0x%p\n\n",hBrush);
 wc.lpszClassName = szClassName;
 wc.lpfnWndProc   = fnWndProc;
 wc.cbSize        = sizeof(WNDCLASSEX);
 wc.hbrBackground = hBrush;
 wc.hInstance     = hInstance;
 wc.cbWndExtra    = sizeof(void*);
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,200,175,320,200,HWND_DESKTOP,0,hInstance,0);
 fprintf(fp,"  hWnd = 0x%p\n\n",hWnd);
 SetWindowLongPtr(hWnd,0*sizeof(void*),(LONG_PTR)hBrush);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }
 fprintf(fp,"  DeleteObject() = %d\n",DeleteObject(hBrush));
 fprintf(fp,"Leaving WinMain()");
 fclose(fp);

 return messages.wParam;
}

#if 0
Entering WinMain()
  hBrush = 0xffffffffe1100f9b

  Entering fnWndProc() -- WM_CREATE
    hWnd = 0x00000000001804ba
  Leaving fnWndProc() -- WM_CREATE

  hWnd = 0x00000000001804ba

  Entering fnWndProc()  -- WM_CTLCOLORSTATIC
    hBrush = 0xffffffffe1100f9b
  Leaving fnWndProc()  -- WM_CTLCOLORSTATIC

  Entering fnWndProc()  -- WM_CTLCOLORSTATIC
    hBrush = 0xffffffffe1100f9b
  Leaving fnWndProc()  -- WM_CTLCOLORSTATIC

  Entering fnWndProc()  -- WM_CTLCOLORSTATIC
    hBrush = 0xffffffffe1100f9b
  Leaving fnWndProc()  -- WM_CTLCOLORSTATIC

  Entering fnWndProc() -- WM_DESTROY
    hWnd = 0x00000000001804ba
  Leaving fnWndProc() -- WM_DESTROY

  DeleteObject() = 1
Leaving WinMain()
#endif
@Freddie1

I've tried to hack apart your code and implement it into my own (bits and pieces of it), with not a whole lot of success. I've reverted my code back to what compiles and runs (though with the funky edit fields having a color line under them with no border. Feel free to take a look at it and see what you can make of it.

winmain.cpp
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
#include <Windows.h>

HWND loginBox;
HWND passwordBox;
HWND loginBtn;
HWND exitBtn;
HWND userLbl;
HWND passLbl;

LRESULT CALLBACK WndProc(HWND hWnd, MSG Msg, WPARAM wParam, LPARAM lParam);

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR nCmdLine, int nCmdShow)
{
	MSG Msg;
	WNDCLASS wc = { 0 };
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.hInstance = hInstance;
	wc.lpszClassName = "Window Class";
	//wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));

	if (RegisterClass(&wc))
	{
		HWND hWnd = CreateWindowEx(0, wc.lpszClassName, "Test Window", WS_OVERLAPPED | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, NULL, NULL, hInstance, NULL);
		if (hWnd)
		{
			ShowWindow(hWnd, nCmdShow);
			UpdateWindow(hWnd);

			while (GetMessage(&Msg, 0, 0, 0) > 0)
			{
				if (IsDialogMessage(hWnd, &Msg))
				{
					//Already handled by dialog manager
				}
				else
				{
					TranslateMessage(&Msg);
					DispatchMessage(&Msg);
				}
			}
		}
		else
		{
			LPSTR Error01 = "Error Displaying Window";
			LPSTR Error01_Caption = "Display Window Error";
			MessageBox(NULL, Error01, Error01_Caption, MB_OK | MB_ICONERROR);
		}
	}
	else
	{
		LPSTR Error02 = "Error Registering Window Class";
		LPSTR Error02_caption = "Class Registration Error";
		MessageBox(NULL, Error02, Error02_caption, MB_OK | MB_ICONERROR);
	}

	return(0);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	int Result = 0;
	HDC hdcStatic = (HDC)wParam;
	HDC hdcEdit = (HDC)wParam;
	HDC hdcButton = (HDC)wParam;

	switch (Msg)
	{
	case WM_CREATE:
		userLbl = CreateWindowEx(NULL, TEXT("Static"), TEXT("Username: "), WS_CHILD | WS_VISIBLE, 10, 20, 75, 20, hWnd, NULL, NULL, NULL);
		loginBox = CreateWindowEx(NULL, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 100, 20, 150, 20, hWnd, NULL, NULL, NULL);
		passLbl = CreateWindowEx(NULL, TEXT("Static"), TEXT("Password: "), WS_CHILD | WS_VISIBLE, 10, 50, 75, 20, hWnd, NULL, NULL, NULL);
		passwordBox = CreateWindowEx(NULL, TEXT("Edit"), NULL, WS_CHILD | ES_PASSWORD | WS_VISIBLE | WS_TABSTOP, 100, 50, 150, 20, hWnd, NULL, NULL, NULL);
		loginBtn = CreateWindowEx(NULL, TEXT("Button"), TEXT("Connect"), WS_CHILD | WS_VISIBLE, 100, 80, 90, 25, hWnd, 1, NULL, NULL);
		exitBtn = CreateWindowEx(NULL, TEXT("Button"), TEXT("Exit"), WS_CHILD | WS_VISIBLE, 200, 80, 50, 25, hWnd, 2, NULL, NULL);

		SetFocus(loginBox);
		break;
	case WM_CTLCOLORSTATIC:
		SetTextColor(hdcStatic, RGB(109, 194, 222));
		SetBkColor(hdcStatic, RGB(0, 0, 0));
		return (INT_PTR)CreateSolidBrush(RGB(0, 0, 0));
		break;
	case WM_CTLCOLOREDIT:
		SetTextColor(hdcEdit, RGB(255, 255, 255));
		SetBkColor(hdcEdit, RGB(0, 0, 0));
		return (INT_PTR)CreateSolidBrush(RGB(109, 194, 222));
		break;
	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case 1:
			break;
		case 2:
			PostQuitMessage(0);
			break;
		}
		break;
	case WM_CLOSE:
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		Result = DefWindowProc(hWnd, Msg, wParam, lParam);
		break;
	}

	return (Result);
}
Give me a little time and I'll take a look at it.
Worked on it some. Believe I got it working using your code. Note I immediately got an error cuz your WndProc declare is wrong. You had this ...

 
LRESULT CALLBACK WndProc(HWND hWnd, MSG Msg, WPARAM wParam, LPARAM lParam);


The 2nd param is an unsigned int - not a MSG.

Also, the 10th parameter of your CreateWindowEx() function calls to create child window controls (the labels, edit controls, and buttons) should contain valid equate/defines such as...

#define IDC_LOGIN_EDITCONTROL 2000 or whatever

That way, you never need globally defined HWNDs in your program such as your ...

HWND loginBox;
HWND passwordBox;
HWND loginBtn;
HWND exitBtn;
HWND userLbl;
HWND passLbl;

These defines should allow you to obtain the HWND of any child window control with ...

GetDlgItem(hParent, ID_CHILD_WINDOW);

Here is the modified program...

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
#include <Windows.h>

#define IDC_USER_LABEL     2000
#define IDC_LOGIN_EDIT     2005
#define IDC_PASSWORD_LABEL 2010
#define IDC_PASSWORD_EDIT  2015
#define IDC_LOGIN_BUTTON   2020
#define IDC_EXIT_BUTTON    2025

LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR nCmdLine, int nCmdShow)
{
 MSG Msg;
 WNDCLASS wc = { 0 };

 wc.style         = CS_HREDRAW | CS_VREDRAW;
 wc.lpfnWndProc   = WndProc;
 wc.hInstance     = hInstance;
 wc.lpszClassName = "Window Class";
 wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
 RegisterClass(&wc);
 HWND hWnd = CreateWindowEx(0, wc.lpszClassName, "Test Window", WS_OVERLAPPED | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, NULL, NULL, hInstance, NULL);
 if(hWnd)
 {
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   while (GetMessage(&Msg, 0, 0, 0) > 0)
   {
      if(!IsDialogMessage(hWnd, &Msg))
      {
         TranslateMessage(&Msg);
         DispatchMessage(&Msg);
      }
   }
 }

 return(0);
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
 HINSTANCE hIns;
 int Result = 0;
 HWND hCtl;

 switch (Msg)
 {
   case WM_CREATE:
     hIns=((LPCREATESTRUCT)lParam)->hInstance;
     hCtl=CreateWindowEx(NULL, TEXT("Static"), TEXT("Username: "), WS_CHILD | WS_VISIBLE, 10, 20, 75, 20, hWnd, (HMENU)IDC_USER_LABEL, hIns, NULL);
     hCtl=CreateWindowEx(NULL, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 100, 20, 150, 20, hWnd, (HMENU)IDC_LOGIN_EDIT, hIns, NULL);
     hCtl=CreateWindowEx(NULL, TEXT("Static"), TEXT("Password: "), WS_CHILD | WS_VISIBLE, 10, 50, 75, 20, hWnd, (HMENU)IDC_PASSWORD_LABEL, hIns, NULL);
     hCtl=CreateWindowEx(NULL, TEXT("Edit"), NULL, WS_CHILD | ES_PASSWORD | WS_VISIBLE | WS_TABSTOP, 100, 50, 150, 20, hWnd, (HMENU)IDC_PASSWORD_EDIT, hIns, NULL);
     hCtl=CreateWindowEx(NULL, TEXT("Button"), TEXT("Connect"), WS_CHILD | WS_VISIBLE, 100, 80, 90, 25, hWnd, (HMENU)IDC_LOGIN_BUTTON, hIns, NULL);
     hCtl=CreateWindowEx(NULL, TEXT("Button"), TEXT("Exit"), WS_CHILD | WS_VISIBLE, 200, 80, 50, 25, hWnd, (HMENU)IDC_EXIT_BUTTON, hIns, NULL);
     SetFocus(GetDlgItem(hWnd,IDC_LOGIN_EDIT));
     break;
   case WM_CTLCOLORSTATIC:
     SetTextColor((HDC)wParam,RGB(109,194,222));
     SetBkMode((HDC)wParam,TRANSPARENT);
     return GetClassLong(hWnd, GCL_HBRBACKGROUND);
   case WM_CTLCOLOREDIT:
     SetTextColor((HDC)wParam, RGB(255, 255, 255));
     SetBkMode((HDC)wParam,TRANSPARENT);
     return GetClassLong(hWnd,GCL_HBRBACKGROUND);
   case WM_COMMAND:
     switch (LOWORD(wParam))
     {
       case IDC_LOGIN_BUTTON:
         break;
       case IDC_EXIT_BUTTON:
         PostQuitMessage(0);
         break;
     }
     break;
   case WM_DESTROY:
     DeleteObject((HBRUSH)GetClassLong(hWnd,GCL_HBRBACKGROUND));
     PostQuitMessage(0);
     break;
   default:
     Result = DefWindowProc(hWnd, Msg, wParam, lParam);
     break;
 }

 return (Result);
}


Let me know if it works.


@Freddie1 Ah yes this works much better and thanks for correcting some of the things I got wrong (such as my wndProc). Now I'm curious if what I'm trying to do is even possible. You did correct the underline underneath those text boxes but what I'm trying to do is outline the text boxes with a white border, so basically change its border color. Maybe that's a simple styling thing. I"ll look into it.

Thanks again!

Now I'm curious if what I'm trying to do is even possible.


When working at a low level such as Win Api, just about anything is possible. Just off the top of my head, a few things spring to mind....

You could try drawing white lines along the borders of your edit controls. I forget the exact names of the GDI functions, but you could look them up. I believe they are something like Draw() or DrawLine() or LineTo() or LineToEx() or something like that. You would create a White HBRUSH or use one of the stock Brushes to do it.

And you could always fool around with the styles as you said. Might hit on something that does it.

The most high powered option which would surely accomplish what you want is to create your own control (known in Windows terminology as a custom control). That would take you out of the beginner world into the world of advanced. But its surprisingly easy to do. You do have to understand pointers and memory allocations very well though. Labels are especially easy to do. Edit controls can be quite a bit more challenging, depending on how much of the functionality of Microsoft's edit control you wish to reproduce.

Last edited on
I think this might do it...

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
#ifndef UNICODE
    #define UNICODE
#endif
#ifndef _UNICODE
    #define _UNICODE
#endif
#include <windows.h>
#define IDC_USER_LABEL     2000
#define IDC_LOGIN_EDIT     2005
#define IDC_PASSWORD_LABEL 2010
#define IDC_PASSWORD_EDIT  2015
#define IDC_LOGIN_BUTTON   2020
#define IDC_EXIT_BUTTON    2025


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_CREATE:
     {
        HINSTANCE hIns;
        hIns=((LPCREATESTRUCT)lParam)->hInstance;
        CreateWindowEx(NULL, L"Static", L"Username: ", WS_CHILD | WS_VISIBLE, 10, 20, 75, 20, hWnd, (HMENU)IDC_USER_LABEL, hIns, NULL);
	CreateWindowEx(NULL, L"Edit", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 100, 20, 150, 20, hWnd, (HMENU)IDC_LOGIN_EDIT, hIns, NULL);
	CreateWindowEx(NULL, L"Static", L"Password: ", WS_CHILD | WS_VISIBLE, 10, 50, 75, 20, hWnd, (HMENU)IDC_PASSWORD_LABEL, hIns, NULL);
	CreateWindowEx(NULL, L"Edit", NULL, WS_CHILD | ES_PASSWORD | WS_VISIBLE | WS_TABSTOP, 100, 50, 150, 20, hWnd, (HMENU)IDC_PASSWORD_EDIT, hIns, NULL);
	CreateWindowEx(NULL, L"Button", L"Connect", WS_CHILD | WS_VISIBLE, 100, 80, 90, 25, hWnd, (HMENU)IDC_LOGIN_BUTTON, hIns, NULL);
	CreateWindowEx(NULL, L"Button", L"Exit", WS_CHILD | WS_VISIBLE, 200, 80, 50, 25, hWnd, (HMENU)IDC_EXIT_BUTTON, hIns, NULL);
	SetFocus(GetDlgItem(hWnd,IDC_LOGIN_EDIT));
        return 0;
     }
   case WM_CTLCOLORSTATIC:
     {
        SetTextColor((HDC)wParam,RGB(109,194,222));
        SetBkMode((HDC)wParam,TRANSPARENT);
        return GetClassLongPtr(hWnd, GCLP_HBRBACKGROUND);
     }
   case WM_CTLCOLOREDIT:
     {
        SetTextColor((HDC)wParam, RGB(255, 255, 255));
        SetBkMode((HDC)wParam,TRANSPARENT);
        return GetClassLongPtr(hWnd,GCLP_HBRBACKGROUND);
     }
   case WM_COMMAND:
     {
        switch(LOWORD(wParam))
        {
          case IDC_LOGIN_BUTTON:
            MessageBox(hWnd,L"You Clicked The Login Button!",L"Button Click Report!",MB_OK);
            break;
          case IDC_EXIT_BUTTON:
            MessageBox(hWnd,L"You Clicked The Exit Button!",L"Button Click Report!",MB_OK);
            DestroyWindow(hWnd);
            break;
        }
        return 0;
     }
   case WM_PAINT:
     {
        PAINTSTRUCT ps;
        POINT Pt;
        HDC hDC=BeginPaint(hWnd,&ps);
        HPEN hPen=(HPEN)GetStockObject(WHITE_PEN);
        HPEN hOriginalPen=(HPEN)SelectObject(hDC,hPen);
        MoveToEx(hDC,98,18,&Pt);
        LineTo(hDC,250,18);
        LineTo(hDC,250,40);
        LineTo(hDC,98,40);
        LineTo(hDC,98,18);
        SelectObject(hDC,hOriginalPen);
        EndPaint(hWnd,&ps);
        return 0;
     }
   case WM_DESTROY:
     {
        DeleteObject((HBRUSH)GetClassLongPtr(hWnd,GCLP_HBRBACKGROUND));
        PostQuitMessage(0);
        return 0;
     }
 }

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 MSG messages;
 WNDCLASS wc;
 HWND hWnd;

 memset(&wc,0,sizeof(WNDCLASS));
 wc.lpszClassName = L"Form1";
 wc.lpfnWndProc   = fnWndProc;
 wc.hInstance     = hIns;
 wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
 wc.hIcon         = LoadIcon(NULL,IDI_APPLICATION);
 RegisterClass(&wc);
 hWnd=CreateWindowEx(0,wc.lpszClassName,wc.lpszClassName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,400, 200,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    if(!IsDialogMessage(hWnd,&messages))
    {
       TranslateMessage(&messages);
       DispatchMessage(&messages);
    }
 }

 return messages.wParam;
}
@Freddie1 Fantastic! Thanks again. I've learned quite a bit from this post including proper ways to define my child members rather than creating global handles and various other bits of detail I missed or did incorrectly.

You sir have my thanks.
Glad to help really. I enjoyed playing with the code. The WM_CTLCOLORSTATIC thing isn't something I do very often, but it does occasionally come up. Its an excellent example of a Windows message where a zero isn't returned back to the operating system.

And you did well yourself. I can't really say I've seen many posts in C++ forums where ...

#include <iostream>
using namespace std

...weren't included. So your binaries won't be bloatware.

Don't know what resources you are using to learn Win Api coding, but I've posted some tutorial information here which you may find useful...

http://www.jose.it-berater.org/smfforum/index.php?topic=3389.0
@freddie1

Trying to change the color of the drawn lines

What you have using the white:
 
HPEN hPen = (HPEN)GetStockObject(WHITE_PEN);


What I've tried with no luck:
 
HPEN hPen = (HPEN)GetStockObject(CreateSolidBrush(RGB(255, 0, 0)));
The GetStockObject() function is used for retrieving quite a number of fairly standard GDI objects that Windows provides 'free', so to speak, i.e., you don't have to call the somewhat 'heavier' functions such as CreateSolidBrush(), CreatePen(), etc. And 'stock' objects don't need to be deleted with DeleteObject(). But there are relatively few of them - black, white, a few others I'd have to look up.

So what you have above doesn't really make sense. If you want some specific color such as a bluish white, you need to call I believe - CreatePen(). When done with it in the WM_PAINT code you need to DeleteObject() on it or guess what? Memory leak. That's bad. GDI memory leaks can bring Windows to its knees fast.
@freddie1 CreatePen(), great function. You were right in that this API is HUGE! So much to learn and unless you've heard some of these things, you'll never think to look them up.

I replaced your code:
 
HPEN hOriginalPen = (HPEN)SelectObject(hDC, hPen);


With this new code:
1
2
3
4
HPEN newPen;

newPen = CreatePen(PS_SOLID, 1, RGB(109, 194, 222));
HPEN hOriginalPen = (HPEN)SelectObject(hDC, newPen);


and it works fantastic. Now to destroy it just call DeleteObject() but where should I do this? I've tucked it at the end of WM_PAINT below your code for now. Let me know if this is proper.

1
2
3
4
		SelectObject(hDC, hOriginalPen);
		EndPaint(hWnd, &ps);
		DeleteObject(newPen);
		return 0;
Last edited on
To learn these things, in my opinion, one must just about study Charles Petzold's "Programming Windows". That's how I know as much as I do about it. And I don't know everything there is to know about it because I never really read the whole thing. Petzold is huge into graphics, which isn't really my strong suit. I'm more of a data processing, database kind of guy. But this stuff about CreateSolidBrush(), GetStockObject(), SelectObject(), DeleteObject(), etc., is kind of basic stuff anyone who dabbles in the WinApi has to know a little bit about. I believe the first time I was learning about this stuff was with fonts, which are also GDI objects. I needed a fixed font so my tables would have their columns and numbers line up right.

And there is something of a problem with that. Here at www.cplusplus.com there are countless posts by folks warning other folks not to attempt to learn Windows Programming through the WinApi, but rather to adopt a class framework, such as .NET, MFC, wxWidgets, QT, etc. And in fact Microsoft created the .NET framework because they fully realized that the average coder wasn't capable of using the Windows Api without leaking memory and resources and generally mucking things up.

This situation you are asking about is a perfect example of that. A Device Context is a sort of sub-object of the Window of which it is a part. The SelectObject() function is used to replace the object currently selected into a device context, and its return is the old object you are replacing. This MUST be saved because when you are done in WM_PAINT the old object must be selected back into the device context. What you have above looks right to me. I might add that you'll sometimes (oftentimes) see this idiom...

DeleteObject(SelectObject(hDC,hOriginalPen));

How that works is that the SelectObject() call as a parameter to DeleteObject() replaces the original GDI Object back into the Device Context per the requirements, and returns the new one just temporarily put in. And the DeleteObject() call acts on that. Pretty slick actually.

So I guess the question one must continually ask oneself, is whether one is better than the average coder. As Clint Eastwood said in "Dirty Harry", 'a man's gotta know his limitations'. :)
Topic archived. No new replies allowed.