Winapi IsDialogMessage() doesn't navigate to multiline edit - winapi

I have window with some edit controls, but IsDialogMessage() skips multiline edit. Can anyone guide me on how to change this?
n=WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
if(!readonly)
n|=WS_TABSTOP;
if(multi)
n=WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|WS_VSCROLL;
if(readonly)
n|=ES_READONLY;
obj=CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", n,(int)x, (int)y, (int)w, (int)h, parent, (HMENU)id, GetModuleHandle(0), 0);
message loop:
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) break;
if(msg.hwnd!=hwnd){
HWND obj;
obj=GetParent(msg.hwnd);
if(obj){
if(IsDialogMessage(obj,&msg))
continue;
}
TranslateMessage(&msg);
}
DispatchMessage(&msg);
}

On the if(!readonly) n |= WS_TABSTOP line you correctly set the tab stop style for your control.
On the very next line, in case of multi, you override n with a set of flags that do not contain the style WS_TABSTOP.
Switch the order of the first two ifs.

Complement to jlahd's answer:
Having a if(!readonly) and some lines further a if(readonly) is bad style. ANd it's also better do have a elsefor the test on multi. Like below:
if(multi)
n=WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|WS_VSCROLL;
else
n=WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
if(!readonly)
n|=WS_TABSTOP;
else
n|=ES_READONLY;
obj=CreateWindowEx(....

Related

Different behaviour between dialogbox and createdialog is not understood

I am creating a dialog in a DLL and while DialogBox creates it - but seems hard to be stopped programmatically, CreateDialog only shows the borders (and title) of the dialog and "does not respond" after creating
I am adding some functionality to an existing program - not under my control. Control of this added functionality - for some external device - requires the user to do some settings particular to settings for this device, not covered by controls in the main program. For this I add a DLL with a small dialog, one with three control elements. The dialog is started with the DialogBox function in a separate thread and funtions well, apart from termination: the use of the main program may want to switch from one device to another without having to explicity quit the dialog by touching some button or control on the dialog.
Therefore I figured it would be better to use CreateDialog since that gives a handle.
However, when doing that - of course adding ShowWindow (handle, WS_SHOW) to make the dialog visible, only the raw widget appears, no controles visible and suffering from "does not respond"
` // DialogBox (hInstance,`
` widgetHandle = CreateDialog (hInstance,`
` MAKEINTRESOURCE(IDD_DIALOG1), NULL, ialog1Proc);`
` err = GetLastError ();`
` fprintf (stderr, "Last Error = %d\n", err);`
` if (err == 0) {`
` ShowWindow (widgetHandle, SW_SHOW);`
` while (running)`
` Sleep (100);`
` }`
Since - when using CreateBox, the contours of the widget - and the title of the dialog - are visible, I assume that the resource is seen correctly,
but it is as such unusable.
Therefore two questions
a. Is my assumption that CreateBox can be used in a thread in a DLL wrong
b. I'm perfectly willing to use DialogBox here, but then I need some advice on how to terminate the dialog (box) programmatically, i.e. from elsewhere in the DLL.
Any help would be appreciated here
DialogBox creates its own message loop and does not return until the dialog is closed. It is essentially CreateDialog + a message loop.
CreateDialog returns after creating the window and it expects you to process messages for it.
HWND hDlg = CreateDialog(...);
ShowWindow(hDlg, SW_SHOW);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsWindow(hDlg)) break;
if (IsDialogMessage(hDlg, &msg)) continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
If your only requirement is to be able to close the dialog from a different thread you can pull it off with DialogBox as well:
HWND g_hDlg = 0; // Assumes you only have one dialog instance open at the time
.. MyDlgProc(HWND hDlg, UINT Msg, ..)
{
switch(Msg)
{
case WM_INITDIALOG:
g_hDlg = hDlg;
return TRUE;
case WM_DESTROY:
g_hDlg = 0;
break;
case WM_CLOSE:
EndDialog(hDlg, 0);
break;
}
return FALSE;
}
EXTERN_C void WINAPI CloseDialog()
{
if (g_hDlg) SendMessage(g_hDlg, WM_CLOSE, 0, 0);
}

How do I tell what messages to handle when subclassing a control?

I want to subclass the edit control into a specific case of a masked edit - something that accepts five characters of user input, and displays colons after the first and third characters. I can imagine two basic approaches to this.
I could have the text that the edit control stores be the text I want displayed. In this case, I would need to set the text to L" : : " to begin with, and override the messages that detect user input so I could copy it into the correct slots in that string. However, I don't know how to be sure which messages those are. I assume WM_KEYDOWN is one, but if there are others, and I don't think of them all, input that triggers the messages I missed would incorrectly defer to the edit control's default handling.
Alternatively, I could have the text that the edit control stores be the text the user enters - no colons. In that case, I would need to override the way the control is displayed so I could to generate a string that includes colons based on the stored text, and draw that when drawing the control. I assume this would mean replacing the WM_PAINT handling. The problem with that is that it would seem to require redefining everything about how the control looks myself, when the text is the only part I want to change. I'm not confident I could do that perfectly, and I would certainly rather not.
How should I approach this?
Edit: I've tried overriding WM_PAINT like this:
INT_PTR CALLBACK MaskedEditProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
if (message == WM_PAINT)
{
WCHAR userInput[6];
Edit_GetText(hwndDlg, userInput, 6);
WCHAR displayString[]{L" : : "};
int userInputLength{ Edit_GetTextLength(hwndDlg) };
switch (userInputLength)
{
case 5:
displayString[6] = userInput[4];
case 4:
displayString[5] = userInput[3];
case 3:
displayString[3] = userInput[2];
case 2:
displayString[2] = userInput[1];
case 1:
displayString[0] = userInput[0];
}
Edit_SetText(hwndDlg, displayString);
DefSubclassProc(hwndDlg, message, wParam, lParam);
Edit_SetText(hwndDlg, userInput);
return TRUE;
}
return DefSubclassProc(hwndDlg, message, wParam, lParam);
}
This seems to basically work, except for some reason it causes the displayed text to flicker.
Edit 2: I set the control's text to L"0:00:00" from its parent window, and gave it the following window procedure:
INT_PTR CALLBACK MaskedEditProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (message)
{
case WM_CUT:
return 0;
case WM_PASTE:
return 0;
case WM_KEYDOWN:
if (wParam == VK_DELETE)
{
WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
switch (caretPosition)
{
case 7:
return 0;
case 1:
case 4:
SendMessage(hwndDlg, EM_SETSEL, caretPosition + 1, caretPosition + 2);
break;
default:
SendMessage(hwndDlg, EM_SETSEL, caretPosition, caretPosition + 1);
}
return DefSubclassProc(hwndDlg, WM_CHAR, '0', 0);
}
case WM_CHAR:
if (wParam == '\b')
{
WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
switch (caretPosition)
{
case 0:
return 0;
case 2:
case 5:
SendMessage(hwndDlg, EM_SETSEL, caretPosition - 2, caretPosition - 1);
break;
default:
SendMessage(hwndDlg, EM_SETSEL, caretPosition - 1, caretPosition);
}
return DefSubclassProc(hwndDlg, WM_CHAR, '0', 0);
}
else if (iswdigit(wParam))
{
WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
switch (caretPosition)
{
case 1:
case 4:
SendMessage(hwndDlg, EM_SETSEL, caretPosition + 1, caretPosition + 2);
break;
default:
SendMessage(hwndDlg, EM_SETSEL, caretPosition, caretPosition + 1);
}
}
}
return DefSubclassProc(hwndDlg, message, wParam, lParam);
}
This seems to work as intended, though my backspace key has always been broken, so I haven't tested that part. The control is also set not to accept non-digit input, so I don't think the fact that I don't handle those here should break anything.
I suggest you handle only WM_CHAR (and possibly custom messages related to your masking). Leave the arrow keys alone, let the edit control handle them and in your WM_CHAR handler query for the caret placement. In your WM_CHAR when the placement would normally reach a colon send a EM_SETSEL that skips to the next character location.

Win32 Edit Control Caret Placement Offset

I gave an English explanation of my problem below but it is a visual issue so if you don't want to read it all just look at the picture at the bottom).
I'm working on building a reverse polish notation calculator for my class and I just completed having the button controls on my GUI be able to append their values to the edit control which works fine, but the caret is doing something weird and I can't find any information on it.
I send a custom message to the edit control in which it finds the length of the current text in the control and then places the caret at the end of the text so I can then add what text needs to be added (it is right aligned with ES_RIGHT), which again works just fine, but when the caret is in the right most place it can be, it is placed practically right through the middle of most any number.
This only seems to happen in the right most place the caret can be (i.e. anywhere else the caret sits directly to the right of the preceding char, as it should) and I have tried replacing the caret all the way to the right using code, placing it using my keyboard/mouse, and tried adjusting the dimensions of the window in hopes that it was just an offset of the width I defined for it that caused the last place to be off slightly, but the problem persists and it makes it hard to read the last char in the text field.
Relevant Code:
LRESULT CALLBACK EditBoxClass::WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_COMMAND:
break;
case WM_APPEND_EDIT:
/* Get current length of text in the box */
index = new int( GetWindowTextLength (hWnd) );
SetFocus( hWnd );
/* Set the caret to the end of the text in the box */
SendMessage( hWnd, EM_SETSEL, (WPARAM)index, (LPARAM)index );
/* "Replace" the selection (the selection is actually targeting
nothing and just sits at the end of the text in the box)
with the passed in TCHAR* from the button control that
sent the WM_APPEND_EDIT message */
SendMessage( hWnd, EM_REPLACESEL, 0, lParam );
break;
}
return CallWindowProc( EditClassStruct.GetOldProc(), hWnd, msg, wParam, lParam );
}
Picture of problem:
After facing the same problem and presenting my first approach in this answer, I'll now provide two well working solutions. I think there is no other way to fix this glitch properly (unless you're a Microsoft programmer who is responsible for this part of the WinAPI).
I was wondering how to fix this problem on edit controls created with ES_MULTILINE but this glitch seems to be only a problem on single-line edit controls (tested on Windows 7 64-bit). Enabling Visual Styles is also helpful but the problem still remains (the offset is at least not so obvious).
Explanation
Normally, when the caret is at the farthest right position it's x value (provided by GetCaretPos ()) should be equal to the rect.right value provided by EM_GETRECT (when the edit control was created with ES_RIGHT). Due to unknown reasons this is not the case. So you have to check if the caret position is at least in the near of the rect.right value (but not farther away than the last letter is wide). So you have two possibilities to fulfill this task:
You must calculate the width of the outer right character using GetTextExtentPoint32 (), subtract it from the rect.right value provided by calling SendMessage () with EM_GETRECT and check whether the x position of the caret is bigger than the result or not OR
You must calculate the margin between the rect.right value and the outer right caret position (3 in my case) and use this value as a hardcoded offset to do a simple check.
After those steps (regardless which one you have chosen) you have to reposition the caret when necessary.
1. Approach (recommended)
case WM_LBUTTONDOWN: {
TRACKMOUSEEVENT tme = {sizeof (tme), TME_LEAVE, hwnd, HOVER_DEFAULT};
TrackMouseEvent (&tme);
}
break;
case WM_KEYDOWN:
case WM_MOUSELEAVE:
case WM_SETCURSOR: {
DefSubclassProc (hwnd, message, wParam, lParam);
DWORD end;
SendMessage (hwnd, EM_GETSEL, (WPARAM) NULL, (LPARAM) &end);
int len = GetWindowTextLength (hwnd);
if (end < len || len <= 0)
return TRUE;
wchar_t *buffer = new wchar_t[len + 1];
GetWindowText (hwnd, buffer, len + 1);
wchar_t lastChar[] = {buffer[len - 1], '\0'};
delete[] buffer;
SIZE size;
HDC hdc = GetDC (hwnd);
if (hdc == NULL)
return TRUE;
GetTextExtentPoint32 (hdc, lastChar, 1, &size);
ReleaseDC (hwnd, hdc);
POINT pt;
RECT rect;
GetCaretPos (&pt);
SendMessage (hwnd, EM_GETRECT, (WPARAM) 0, (LPARAM) &rect);
if ((rect.right - size.cx) <= pt.x)
SetCaretPos (rect.right, pt.y);
return TRUE;
}
break;
2. Approach (improved original version)
case WM_LBUTTONDOWN: {
TRACKMOUSEEVENT tme = {sizeof (tme), TME_LEAVE, hwnd, HOVER_DEFAULT};
TrackMouseEvent (&tme);
}
break;
case WM_KEYDOWN:
case WM_MOUSELEAVE:
case WM_SETCURSOR: {
DefSubclassProc (hwnd, message, wParam, lParam);
POINT pt;
RECT rect;
GetCaretPos (&pt);
SendMessage (hwnd, EM_GETRECT, (WPARAM) 0, (LPARAM) &rect);
if ((rect.right - pt.x) <= 3)
SetCaretPos (rect.right, pt.y);
return TRUE;
}
break;
You have to subclass the edit controls. Then use this code in their window procedures and enjoy.
In both cases, tracking the mouse event is not absolutely necessary but recommended to completly avoid this glitch. Calling DefSubclassProc () will ensure that the cursor is changed on mouse over.
This may or may not be the cause, but you are misusing EM_SETSEL. You are dynamically allocating (and leaking) an int on the heap and passing a pointer to it as the message parameters, but EM_SETSEL does not expect or use pointers to begin with. So get rid of the dynamic allocation.
Also, the default window proc is not going to know how to handle your WM_APPEND_EDIT message, so there is no point in passing the message to CallWindowProc().
Try this instead:
case WM_APPEND_EDIT:
{
/* Get current length of text in the box */
int index = GetWindowTextLength( hWnd );
SetFocus( hWnd );
/* Set the caret to the end of the text in the box */
SendMessage( hWnd, EM_SETSEL, (WPARAM)index, (LPARAM)index );
/* "Replace" the selection (the selection is actually targeting
nothing and just sits at the end of the text in the box)
with the passed in TCHAR* from the button control that
sent the WM_APPEND_EDIT message */
SendMessage( hWnd, EM_REPLACESEL, 0, lParam );
return 0;
}
That being said, try using EM_GETRECT/EM_SETRECT to expand the right edge of the edit control's formatting rectangle by a few pixels. That should give the caret some extra room to work with.

Determining if a Window Has a Taskbar Button

I am looking for a way to check if a given window has a taskbar button. That is, given a handle to a window, I need a TRUE if the window is in the taskbar, and FALSE otherwise.
Conversely, I am wondering if there is a way to get a handle to the window that belongs to a given taskbar button, which I suppose would require a way to enumerate through the taskbar buttons.
(The first former is the part that I need, and the latter part is optional.)
Thanks a lot.
Windows uses heuristics to decide whether or not to give a taskbar button to a window, and sometimes there is a delay before it can decide, so doing this 100% accurately is going to be quite hard. Here's a rough start on the rules. There are modern style flags that make it easy to know, but when those styles are missing the taskbar is reduced to guessing.
First off, you will need both of the the window style flags.
LONG Style = GetWindowLong(hwnd, GWL_STYLE);
LONG ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
Now the rules, there are three rules that are certain.
if ExStyle & WS_EX_APPWINDOW, then TASKBAR
if ExStyle & WS_EX_TOOLWINDOW, then NOT_TASKBAR
if Style & WS_CHILD then NOT_TASKBAR
The rest are guesses:
Style & WS_OVERLAPPED suggests TASKBAR
Style & WS_POPUP suggests NOT_TASKBAR especially if GetParent() != NULL
ExStyle & WS_EX_OVERLAPPEDWINDOW suggests TASKBAR
ExStyle & WS_EX_CLIENTEDGE suggests NOT_TASKBAR
ExStyle & WS_EX_DLGMODALFRAME suggests NOT_TASKBAR
I'm sure that there are other rules for guessing, and in fact that the guessing rules have changed from version to version of Windows.
Toplevel window
WS_EX_APPWINDOW -> taskbar, no matter the other styles!
OWNER must be NULL (GetWindow(window, GW_OWNER))
no: WS_EX_NOACTIVATE or WS_EX_TOOLWINDOW:
order is important.
second question: in windows xp/vista it was possible to get into the process of the taskbar and get all window ID´s:
void EnumTasklistWindows()
{
int b2 = 0;
TBBUTTON tbButton;
DWORD dwProcessId = 0, dwThreadId = 0;
HWND hDesktop =::GetDesktopWindow();
HWND hTray =::FindWindowEx(hDesktop, 0, ("Shell_TrayWnd"), NULL);
HWND hReBar =::FindWindowEx(hTray, 0, ("ReBarWindow32"), NULL);
HWND hTask =::FindWindowEx(hReBar, 0, ("MSTaskSwWClass"), NULL);
HWND hToolbar =::FindWindowEx(hTask, 0, ("ToolbarWindow32"), NULL);
LRESULT count =::SendMessage(hToolbar, TB_BUTTONCOUNT, 0, 0);
dwThreadId = GetWindowThreadProcessId(hToolbar, &dwProcessId);
shared_ptr<void> hProcess (OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId), CloseHandle);
if (NULL == hProcess.get())
{
return;
}
memset(&tbButton, 0, sizeof(TBBUTTON));
for (int i = 0; i < count; i++)
{
memset(&tbButton, 0, sizeof(TBBUTTON));
shared_ptr<void> lpRemoteBuffer (
VirtualAllocEx(hProcess.get(), NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE),
bind<BOOL>(VirtualFreeEx, hProcess.get(), _1, 0, MEM_RELEASE));
if (NULL == lpRemoteBuffer.get())
{
return;
}
SendMessage(hToolbar, TB_GETBUTTON, i, (LPARAM) lpRemoteBuffer.get());
b2 = ReadProcessMemory(hProcess.get(), lpRemoteBuffer.get(),
(LPVOID) & tbButton, sizeof(TBBUTTON), NULL);
if (0 == b2)
{
continue;
}
BYTE localBuffer[0x1000];
BYTE *pLocalBuffer = localBuffer;
DWORD_PTR ipLocalBuffer = (DWORD_PTR) pLocalBuffer;
pLocalBuffer = localBuffer;
ipLocalBuffer = (DWORD_PTR) pLocalBuffer;
DWORD_PTR lpRemoteData = (DWORD_PTR) tbButton.dwData;
ReadProcessMemory(hProcess.get(), (LPVOID) lpRemoteData, (LPVOID) ipLocalBuffer,
sizeof(DWORD_PTR), NULL);
HWND windowHandle;
memcpy(&windowHandle, (void *) ipLocalBuffer, 4);
if (windowHandle != NULL)
{
trace ("adding button: %x\n", windowHandle);
}
}
}
this not possible with windows 7 anymore. so you need to loop over all toplevel windows.
This MSDN article has some good information about when and why the Shell decides to create a taskbar button for a window:
The Shell creates a button on the taskbar whenever an application creates a window that isn't owned. To ensure that the window button is placed on the taskbar, create an unowned window with the WS_EX_APPWINDOW extended style. To prevent the window button from being placed on the taskbar, create the unowned window with the WS_EX_TOOLWINDOW extended style. As an alternative, you can create a hidden window and make this hidden window the owner of your visible window.

limit of 64 ownerdraw createwindow buttons

I want to create an array of 256 colored buttons with the owner draw extended style to a dialog box created with the visual studio dialog design tool. I added a loop to the WM_INITDIALOG message handler in the dialog procedure to do this:
for (i=0; i<=255; i++)
{
int xp, yp;
HWND status;
xp = rect_pos.left+16*(i%16);
yp = rect_pos.top+16*(i>>4);
status = CreateWindow (
TEXT("button"),
"\0",
WS_CHILD|WS_VISIBLE|BS_OWNERDRAW|BS_PUSHBUTTON,
xp,
yp,
15,
15,
hDlg,
(HMENU) 5000+i, // id used to report events
hInst,
NULL
);
if (status == NULL)
xp =7;
}
I added a message handler for the WM_CTLCOLORBTN message.
case WM_CTLCOLORBTN:
{
int zz;
zz = GetWindowLong ((HWND) lParam, GWL_ID); // window identifier
zz -= 5000;
if ((zz >= 0) && (zz <= 255))
{
HBRUSH BS;
SetTextColor ((HDC) wParam, Collector.Color);
SetBkColor ((HDC) wParam, Collector.Color);
return ((LRESULT) Collector.Brush);
}
break;
}
It more or less works but only the first 64 buttons are displayed. I intend to use a different brush to color each button but for debug puproses, I substituted a single well defined brush. I've debugged the code and satisfied myself the x/y coordinates are proper for each button and that the ID provided in the hMenu createwindow call is proper. I watched all 256 buttons get colored in the WM_CTLCOLORBTN handler. I included a check to make sure the createwindow call does not return failure (NULL). I can get either 4 rows of 16 buttons or 4 columns of 16 buttons by interchanging the x/y parameters on the createwindow call.
If I remove the BS_OWNERDRAW bit from the createwindow call, all 256 buttons are drawn.
It's as if there a limit of 64 buttons with BS_OWNERDRAW :-(
Any help would be greatly appreciated!
TIA, Mike
Are you handling the WM_DRAWITEM message in conjunction with the BS_OWNERDRAW style?
In your case, it seems surprising to me that any buttons are displayed while using the BS_OWNERDRAW style, while BS_PUSHBUTTON is set.
As mentioned in the following link to the documentation for BS_OWNERDRAW, you need to handle WM_DRAWITEM and avoid specifying any other BS_ button styles.
Button Styles from MSDN
Also curious is that the WM_CTLCOLORBUTTON message may be received and then ignored for buttons containing the BS_PUSHBUTTON style. Check out the following link for the documentation on that window message.
WM_CTLCOLORBUTTON from MSDN
From what I can see in your code snippet, most likely you will want to do the following:
Set BS_OWNERDRAW when creating the child buttons.
Handle WM_DRAWITEM on the dialog and draw the button in its correct state. Note that you don't have to handle WM_CTLCOLORBUTTON, just use the Brushes and Fonts and modify the DC as you wish inside your WM_DRAWITEM handler.
Also, depending on your application, you might benefit from making your own window class to represent a grid of buttons on your own, and just drawing the items to taste. This is preferable if you're just displaying internal state and not really looking for the user to manage or interact with a grid of buttons.
Thanks to all who gave advice and help. I now have this working to my satisfaction. I have successfully colored the buttons and surrounded them with a black outline if selected or if they have the input focus. I'll add some code snippets below to show the final state of things but here is synopsis. First, I believe there is some legacy code in my system which makes owner drawn buttons respond to the WM_CTLCOLORBTN for the first child 64 buttons created. Second, I believe the only thing one needs to do is create the buttons, respond properly to the WM_DRAWITEM and WM_COMMAND/BN_CLICKED messages.
Here are the code snippets from my dialog box handler.
In the WM_INITDIALOG code -- create the buttons
for (i=0; i<=255; i++)
{
int xp, yp;
HWND status;
xp = rect_pos.left+16*(i&0x0F);
yp = rect_pos.top+16*(i>>4);
status = CreateWindow
(
TEXT("button"),
"\0",
WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_OWNERDRAW,
xp,
yp,
15,
15,
hDlg,
(HMENU) (5000+i), // id used to report events
hInst,
NULL
);
if (status == NULL)
xp =7;
SetFocus (status);
}
Respond to the WM_DRAWITEM message
case WM_DRAWITEM: // Owner drawn botton
{
LPDRAWITEMSTRUCT lpDrawItem;
HBRUSH BS, BS_Old;
HPEN PN_Old;
int sz=15;
int cntl;
cntl = LOWORD (wParam) - 5000;
lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
if (lpDrawItem->CtlType != ODT_BUTTON)
return FALSE;
BS = CreateSolidBrush (ColorRef[cntl]);
if (lpDrawItem->itemState & (ODS_SELECTED | ODS_FOCUS))
{
sz = 14;
PN_Old = (HPEN) SelectObject(lpDrawItem->hDC, GetStockObject(BLACK_PEN));
}
else
PN_Old = (HPEN) SelectObject(lpDrawItem->hDC, GetStockObject(NULL_PEN));
BS_Old = (HBRUSH) SelectObject(lpDrawItem->hDC, BS);
Rectangle (lpDrawItem->hDC, 0, 0, sz, sz);
SelectObject(lpDrawItem->hDC, PN_Old);
SelectObject(lpDrawItem->hDC, BS_Old);
DeleteObject (BS);
return true;
}
and finally in the WM_COMMAND code
if (HIWORD(wParam) == BN_CLICKED)
{
if ((LOWORD(wParam) >= 5000) && (LOWORD(wParam) <=5255))
{
Color[0] = ColorRef[LOWORD(wParam)-5000] & 0xFF;
Color[1] = (ColorRef[LOWORD(wParam)-5000] >> 16) & 0xFF;
Color[2] = (ColorRef[LOWORD(wParam)-5000] >> 8 ) & 0xFF;
InvalidateRect (hDlg, NULL, TRUE);
goto Set_Color;
}
}

Resources