How to enable buttons when scroll bar hits bottom with Win32? - winapi

I'm writing a license agreement dialog box with Win32 and I'm stumped. As usual with these things I want the "accept/don't accept" buttons to become enabled when the slider of the scroll bar of the richedit control hits bottom, but I can't find a way to get notified of that event. The earliest I've been able to learn about it is when the user releases the left mouse button.
Is there a way to do this?
Here's what I tried so far:
WM_VSCROLL and WM_LBUTTONUP in richedit's wndproc
EN_MSGFILTER notification in dlgproc (yes the filter mask is getting set)
WM_VSCROLL and WM_LBUTTONUP in dlgproc.
EN_VSCROLL notification in dlgproc
I got so desperate I tried polling but that didn't work either because apparently timer messages stop arriving while the mouse button is down on the slider. I tried both:
timer callback (to poll) in dlgproc
timer callback (to poll) in richedit's wndproc

You need to sub-class the edit box and intercept the messages to the edit box itself. Here's an artical on MSDN about subclassing controls.
EDIT: Some code to demonstrate the scroll bar enabling a button:
#include <windows.h>
#include <richedit.h>
LRESULT __stdcall RichEditSubclass
(
HWND window,
UINT message,
WPARAM w_param,
LPARAM l_param
)
{
HWND
parent = reinterpret_cast <HWND> (GetWindowLong (window, GWL_HWNDPARENT));
WNDPROC
proc = reinterpret_cast <WNDPROC> (GetWindowLong (parent, GWL_USERDATA));
switch (message)
{
case WM_VSCROLL:
{
SCROLLINFO
scroll_info =
{
sizeof scroll_info,
SIF_ALL
};
GetScrollInfo (window, SB_VERT, &scroll_info);
if (scroll_info.nPos + static_cast <int> (scroll_info.nPage) >= scroll_info.nMax ||
scroll_info.nTrackPos + static_cast <int> (scroll_info.nPage) >= scroll_info.nMax)
{
HWND
button = reinterpret_cast <HWND> (GetWindowLong (parent, 0));
EnableWindow (button, TRUE);
}
}
break;
}
return CallWindowProc (proc, window, message, w_param, l_param);
}
LRESULT __stdcall ApplicationWindowProc
(
HWND window,
UINT message,
WPARAM w_param,
LPARAM l_param
)
{
bool
use_default_proc = false;
LRESULT
result = 0;
switch (message)
{
case WM_CREATE:
{
CREATESTRUCT
*creation_data = reinterpret_cast <CREATESTRUCT *> (l_param);
RECT
client;
GetClientRect (window, &client);
HWND
child = CreateWindow (RICHEDIT_CLASS,
TEXT ("The\nQuick\nBrown\nFox\nJumped\nOver\nThe\nLazy\nDog\nThe\nQuick\nBrown\nFox\nJumped\nOver\nThe\nLazy\nDog"),
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | ES_DISABLENOSCROLL,
0, 0, client.right, client.bottom - 30,
window,
0,
creation_data->hInstance,
0);
SetWindowLong (window, GWL_USERDATA, GetWindowLong (child, GWL_WNDPROC));
SetWindowLong (child, GWL_WNDPROC, reinterpret_cast <LONG> (RichEditSubclass));
SetWindowLong (child, GWL_ID, 0);
child = CreateWindow (TEXT ("BUTTON"), TEXT ("Go Ahead!"), WS_CHILD | WS_VISIBLE | WS_DISABLED, 0, client.bottom - 30, client.right, 30, window, 0, creation_data->hInstance, 0);
SetWindowLong (window, 0, reinterpret_cast <LONG> (child));
SetWindowLong (child, GWL_ID, 1);
}
break;
case WM_COMMAND:
if (HIWORD (w_param) == BN_CLICKED && LOWORD (w_param) == 1)
{
DestroyWindow (window);
}
break;
default:
use_default_proc = true;
break;
}
return use_default_proc ? DefWindowProc (window, message, w_param, l_param) : result;
}
int __stdcall WinMain
(
HINSTANCE instance,
HINSTANCE unused,
LPSTR command_line,
int show
)
{
LoadLibrary (TEXT ("riched20.dll"));
WNDCLASS
window_class =
{
0,
ApplicationWindowProc,
0,
4,
instance,
0,
LoadCursor (0, IDC_ARROW),
reinterpret_cast <HBRUSH> (COLOR_BACKGROUND + 1),
0,
TEXT ("ApplicationWindowClass")
};
RegisterClass (&window_class);
HWND
window = CreateWindow (TEXT ("ApplicationWindowClass"),
TEXT ("Application"),
WS_VISIBLE | WS_OVERLAPPED | WS_SYSMENU,
CW_USEDEFAULT,
CW_USEDEFAULT,
400, 300, 0, 0,
instance,
0);
MSG
message;
int
success;
while (success = GetMessage (&message, window, 0, 0))
{
if (success == -1)
{
break;
}
else
{
TranslateMessage (&message);
DispatchMessage (&message);
}
}
return 0;
}
The above doesn't handle the user moving the cursor in the edit box.

I would recommend starting up Spy++ and seeing which windows messages are getting sent to where.
http://msdn.microsoft.com/en-us/library/aa264396(VS.60).aspx

Why not use the EM_GETTHUMB message. (Assuming Rich Edit 2.0 or later).
If you are lucky this bottom position will match EM_GETLINECOUNT.

Even though it is possible, I don't think you should do it that way - the user will have no clue why the buttons are disabled. This can be very confusing, and confusing the user should be avoided at all costs ;-)
That's why most license dialogs have radio buttons for accept/decline with decline enabled by default, so you actively have to enable accept.

Related

Win32 DateTime_SetMonthCalColor seems not work

I want to change the background color of a datetimepicker contorl's calender. According windows SDK ,there's a marco DateTime_SetMonthCalColor.
DateTime_SetMonthCalColor(hwnd,MSC_BACKGROUND,RGB(0,120,250));
But not work. I do this on windows control spy,send the same message DTM_SETMCCOLOR , no effect either. So what's wrong ?
MCSC_BACKGROUND sets the background color of the month calendar control itself, which only shows when the dropdown is resized to more than the default single month. The background of individual months is set with MCSC_MONTHBK instead.
The month calendar control gets created each time the dropdown is opened. In order to resize it, the parent window of the date-time picker control must process the DTN_DROPDOWN notification, like in the minimal example below.
HWND CreateDTC(HWND hWnd)
{
INITCOMMONCONTROLSEX icex = { sizeof(icex), ICC_DATE_CLASSES };
InitCommonControlsEx(&icex);
return CreateWindowExW(0, DATETIMEPICK_CLASS, L"DateTime",
WS_BORDER | WS_CHILD | WS_VISIBLE | DTS_SHOWNONE,
10, 10, 150, 25, hWnd, nullptr, hInst, nullptr);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hDTC = nullptr;
switch(msg)
{
case WM_CREATE:
hDTC = CreateDTC(hWnd);
DateTime_SetMonthCalColor(hDTC, MCSC_BACKGROUND, RGB(0, 120, 250)); // blue
DateTime_SetMonthCalColor(hDTC, MCSC_MONTHBK, RGB(120, 250, 0)); // green
break;
case WM_NOTIFY:
switch(((LPNMHDR)lParam)->code)
{
case DTN_DROPDOWN:
HWND hMC = DateTime_GetMonthCal(hDTC);
RECT rc;
MonthCal_GetMinReqRect(hMC, &rc);
MoveWindow(hMC, 0, 0, (5 * rc.right) / 2, (3 * rc.bottom) / 2, FALSE);
break;
}
break;
//...
}
return 0;
}

Open WinAPI Window on non-Main Thread

I'm building a real time graphics application and I've noticed that under certain conditions the operating system will post nonqueued messages that block my program. For example, this will happen during the entire time that a user is resizing the window.
To solve this problem, I would like to put the window on a different thread, but I read that this is a terrible idea because of the way windows messages work. Is it still a problem if I process all messages on the other thread, and never send or receive messages from the main thread? I'm thinking about setting up my own message queue with a mutex so I can pass the data I care about back and forth. If this won't work, are there any other ways to solve this problem?
Here's a simplified version of the code I'm running:
LRESULT CALLBACK WindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_SIZE) {
// This event gets called a lot
// Handle window resize
return 0;
} else {
return DefWindowProc(window, message, wParam, lParam);
}
}
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int numCmd) {
WNDCLASSA class = {};
class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
class.lpfnWndProc = WindowCallback;
class.lpszClassName = "Name";
class.hInstance = instance;
RegisterClassA(&class)l
HWND win = CreateWindowExA(0, class.lpszClassName, "Name",
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 400,
0, 0, instance, 0);
while(true) {
// Handle Events
MSG msg = {};
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Do Updating of State and Graphics Rendering Here
}
}
Thanks in advance!

Creating ComboBox in Win32 not working properly

I am creating a Win32 ComboBox for the first time. And I have a problem here.
When calling CreateWindow for the ComboBox, it calls the WndProc callback function again with the WM_CREATE message, so what happens is the ComboBox makes a child ComboBox, again and again like recursion.
Here is the code:
#include <stdio.h>
#include <conio.h>
#include <Windows.h>
#include <random>
#include <time.h>
#include <string>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = L"ComboBox";
const WCHAR *items[] = { L"Apple", L"Orange", L"Melon", L"Grape", L"Strawberry" };
HWND hwnd;
enum COMMAND_ID {
COMMAND_ID_CONTROL_COMBO_0
};
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
srand(time(NULL));
g_hInst = hInstance;
WNDCLASS wndClass;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WndProc;
wndClass.lpszClassName = lpszClass;
wndClass.lpszMenuName = NULL;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndClass);
hwnd = CreateWindow(
lpszClass,
lpszClass,
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
(HMENU)NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (true)
{
GetMessage(&msg, NULL, 0, 0);
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static HWND hCombo;
static WCHAR str[128];
switch (msg)
{
case WM_CREATE:
{
hCombo = CreateWindow(
L"combobox",
NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWN,
10, 10, 200, 200,
hWnd,
(HMENU)COMMAND_ID_CONTROL_COMBO_0,
g_hInst,
NULL);
for (int i = 0; i < 5; ++i)
{
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)items[i]);
}
SendMessage(hCombo, CB_SETCURSEL, 0, NULL);
}
break;
case WM_COMMAND:
{
switch (LOWORD(wparam))
{
case COMMAND_ID_CONTROL_COMBO_0:
switch (HIWORD(wparam))
{
case CBN_SELCHANGE:
{
int iCurSel = SendMessage(hCombo, CB_GETCURSEL, NULL, NULL);
SendMessage(hCombo, CB_GETLBTEXT, iCurSel, (LPARAM)str);
SetWindowText(hWnd, str);
}
break;
case CBN_EDITCHANGE:
GetWindowText(hCombo, str, 128);
SetWindowText(hWnd, str);
break;
}
break;
default:
break;
}
}
return 0;
}
return DefWindowProc(hWnd, msg, wparam, lparam);
}
And here is the result:
I tried to put some boolean flag to execute WM_CREATE only once, and it works, I mean only creating one ComboBox without any child in it.
But, it just looked like only a white window with a border mark, there's no arrow button or anything to dropdown page that the ComboBox is supposed to have.
This recursive case never happened when I was creating different controls like Buttons, CheckBoxes, ListBoxes, etc.
And the ComboBox created doesn't look like it has the proper shape, too.
Hope I am just missing something simple.
When calling CreateWindow for the ComboBox, it calls the WndProc
callback function again with the WM_CREATE message, so what happens
is the ComboBox makes a child ComboBox, again and again like
recursion.
WM_CREATE message is sent to the window procedure of the new window after the window is created. Your first WM_CREATE message is generated by this line hwnd = CreateWindow(). Then you create another window in first WM_CREATE message, so it will generate second WM_CREATE message. Because you use the same registered class ("ComboBox" / "combobox", it is not case sensitive) to create all these windows, all of them use the same one window procedure. So you receive WM_CREATE message again and again until CreateWindow fail to create a window and return NULL.
But, it just looked like only a white window with a border mark,
there's no arrow button or anything to dropdown page that the ComboBox
is supposed to have.
The root cause is you register a class with the same name as the existing system class: "ComboBox" / "combobox". This new registered class override the existing one. It is just a common window instead of a predefined Combobox control as #RemyLebeau pointed out.
An application can register an application local class having the same
name as a system class. This replaces the system class in the context
of the application but does not prevent other applications from using
the system class.
Refer to "How the System Locates a Window Class".
To make the Combobox display in expected shape, what you need to do is changing the lpszClass to a non-predefined one, for example, like "SimpleComboBoxExample".
It is suggested to use predefined macro of Combobox Class Name: WC_COMBOBOX instead of L"combobox".
More reference: "How to Create a Simple Combo Box".
You are not actually creating a Win32 ComboBox at all. You are registering your own class named "ComboBox", and then creating a window of that class, which creates a window of that class, which creates a window of that class, and so on recursively.
You need to change this line:
LPCTSTR lpszClass = L"ComboBox";
To a different unique name, such as "MyWindowClass".
On a side note, your message loop is structured wrong. It should look like this instead:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
And in your WndProc(), the return 0; statement underneath the WM_COMMAND handler is in the wrong place. It needs to be moved inside of the WM_COMMAND handler instead:
case WM_COMMAND:
{
switch (...)
{
...
}
return 0; // <-- moved here
}
//return 0; // <-- from here

How to put an EDIT control in a custom popup window using Win32 API?

I'm trying to add an EDIT control to a window used as a dropdown for a custom combobox-like control. Initially this dropdown window was implemented as a child (WS_CHILD) window of the desktop, which is similar to the "ComboLbox" window used by the real combobox. This worked just fine, however an EDIT window seems to just refuse to accept focus when it is put into such dropdown window. I.e. it is enabled and reacts to right mouse clicks, for example, but clicking on it or calling SetFocus() fails (the latter sets last error to ERROR_INVALID_PARAMETER).
Because of this, and also because of the way custom popup windows are implemented in many examples including Raymond Chen's fakemenu sample, I've changed the dropdown implementation to use WS_POPUP, with the main application window as owner. This has a known problem with stealing activation from the owner window when the popup is shown, however this can be addressed by returning MA_NOACTIVATE from WM_MOUSEACTIVATE handler for the popup window and it indeed works well initially, i.e. the owner window keeps activation when the popup shows up. But as soon as I click the EDIT control inside the popup, it calls, from its default window proc, SetFocus() to set the focus to itself, which deactivates the parent window.
My question is how can I prevent this from happening? I know that it can be done because WinForms ToolStripManager manages to allow editing text in a dropdown without deactivating the parent window and it also uses WS_POPUP style for the popup window. But how does it do it?
A solution was suggested in comments "prevent the host window from visibly appearing inactive by handling WM_NCACTIVATE" This should work as shown in the example below.
When menu window is opened, the host window (HostProc) will receive WM_NCACTIVATE message. Host will look for "menuclass", if menu class is found then host will return DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, lparam); to prevent the title bar for host window get painted inactive.
You also need to handle WM_NCACTIVATE in fake menu window. When menu window goes out of focus, WM_NCACTIVATE is received by MenuProc, at this point the menu can close itself.
#include <windows.h>
const wchar_t* menuclass = L"menuclass";
LRESULT CALLBACK MenuProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
CreateWindow(L"Edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 160, 30,
hwnd, NULL, NULL, NULL);
break;
case WM_NCACTIVATE:
{
if(!wparam)
{
//close the menu if its losing focus
PostMessage(hwnd, WM_CLOSE, 0, 0);
//tell parent to paint inactive, if user clicked on a different program
POINT pt;
GetCursorPos(&pt);
HWND hit = WindowFromPoint(pt);
HWND hparent = GetParent(hwnd);
if(hit != hparent && !IsChild(hparent, hit))
DefWindowProc(hparent, WM_NCACTIVATE, FALSE, 0);
}
break;
}
case WM_LBUTTONDOWN:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
//also handles other mouse/key messages associated with a menu...
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
LRESULT CALLBACK HostProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_NCACTIVATE:
//paint the window as active when custom menu starts
if(!wparam && FindWindow(menuclass, NULL))
return DefWindowProc(hwnd, WM_NCACTIVATE, TRUE, lparam);
break;
case WM_RBUTTONUP:
{
//show the custom menu
POINT pt;
GetCursorPos(&pt);
CreateWindow(menuclass, NULL, WS_VISIBLE | WS_POPUP | WS_BORDER,
pt.x, pt.y, 200, 400, hwnd, 0, 0, 0);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpfnWndProc = HostProc;
wcex.lpszClassName = L"hostwnd";
RegisterClassEx(&wcex);
wcex.lpfnWndProc = MenuProc;
wcex.lpszClassName = menuclass;
RegisterClassEx(&wcex);
CreateWindow(L"hostwnd", L"Right click for menu ...",
WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 600, 400, 0, 0, hInstance, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}

Detect Button Press in Tab Page

I am using the WinAPI to create a GUI utility. I have two Tabs and each tab has some buttons. I am creating the buttons with this function:
CreateWindowEx(NULL,"button", "Clear", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
150, 175, 100, 25, tab_window_2, (HMENU) CLEAR_DATA, instance_handle, NULL);
In the Windows proc function, I don't know how to detect when the above button is pressed. I also tried handling CLEAR_DATA in the WM_COMMAND switch construct, as follows.
switch ( message ) {
case WM_COMMAND:{
switch(LOWORD(wparam)) {
case CLEAR_DATA : break
}
}
How can I detect and handle those buttons being pressed?
I am creating the tab_window_2 tab as follows :
class frame_window {
private:
LPCSTR window_class_name;
HINSTANCE instance_handle;
HCURSOR cursor_arrow;
HWND window_handle;
HWND tab_handle;
HWND tab_window_1;
HWND tab_window_2;
HWND current_tab_window;
RECT client_rectangle;
public:
frame_window(LPCSTR window_class_identity) : window_class_name(window_class_identity) {
INITCOMMONCONTROLSEX common_controls;
common_controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
common_controls.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&common_controls);
int screen_width = GetSystemMetrics(SM_CXFULLSCREEN);
int screen_height = GetSystemMetrics(SM_CYFULLSCREEN);
instance_handle = GetModuleHandle(NULL);
WNDCLASS window_class = { CS_OWNDC, main_window_proc, 0, 0,
instance_handle, NULL,
NULL, NULL, NULL,
window_class_name };
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Create a standard frame window
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RegisterClass(&window_class);
window_handle = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
window_class_name,
"IR Remote and Barcode Demo",
WS_OVERLAPPEDWINDOW |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
100, 100, screen_width-1600,
screen_height-490, NULL, NULL,
instance_handle, NULL);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Get the size of the client rectangle for the window we have just created
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RECT client_rect;
GetClientRect(window_handle, &client_rect);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Create the tab control window.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tab_handle = CreateWindowEx(NULL, WC_TABCONTROL, NULL,
WS_CHILD | WS_VISIBLE,
10, 10, client_rect.right-client_rect.left-20,
client_rect.bottom-client_rect.top-10,
window_handle, NULL,
instance_handle, NULL);
// Create three tabs.
TCITEM tab_info;
memset(&tab_info, 0, sizeof(tab_info));
tab_info.mask = TCIF_TEXT;
tab_info.pszText = "tab#1";
tab_info.cchTextMax = 5;
SendMessage(tab_handle, TCM_INSERTITEM, 0, (LPARAM)&tab_info);
tab_info.pszText = "tab#2";
SendMessage(tab_handle, TCM_INSERTITEM, 1, (LPARAM)&tab_info);
RECT tab_rectangle;
GetClientRect(tab_handle, &tab_rectangle);
SendMessage(tab_handle, TCM_ADJUSTRECT, FALSE, (LPARAM)&tab_rectangle);
// Create the tab view windows
tab_window_1 = CreateWindowEx(NULL, "STATIC", " ",
WS_CHILD | WS_VISIBLE|SS_OWNERDRAW,
tab_rectangle.left+10, tab_rectangle.top+10,
tab_rectangle.right-tab_rectangle.left,
tab_rectangle.bottom -tab_rectangle.top,
tab_handle, (HMENU)1,
instance_handle, NULL);
SetParent(tab_window_1, window_handle);
current_tab_window = tab_window_1;
tab_window_2 = CreateWindowEx(NULL, "STATIC", " ",
WS_CHILD|SS_OWNERDRAW,
tab_rectangle.left+10, tab_rectangle.top+10,
tab_rectangle.right-tab_rectangle.left,
tab_rectangle.bottom -tab_rectangle.top,
tab_handle, (HMENU)2,
instance_handle, NULL);
CreateWindowEx(NULL,"button", "Clear", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
150, 175, 100, 25, tab_window_2, (HMENU) CLEAR_DATA, instance_handle, NULL);
SetParent(tab_window_2, window_handle);
SetCursor(LoadCursor(NULL, IDC_ARROW));
SetWindowLongPtr(window_handle, GWL_USERDATA, (LONG)this);
ShowWindow(window_handle, SW_SHOW);
UpdateWindow(window_handle);
}
~frame_window() {
UnregisterClass(window_class_name, instance_handle);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Windows main entry point
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int WINAPI wWinMain(HINSTANCE instance_handle, HINSTANCE, LPWSTR, INT) {
frame_window main_window("my base window");
main_window.run();
return 0;
}
The WM_COMMAND for handling the events from a Push Button must be in the Window Procedure of it's parent. Your code show tab_window_2 as the parent for the CLEAR_DATA button.
Move the WM_COMMAND code in the right Window Proc (or Dialog Proc).
EDIT: I think you didn't understand how the Tab Control works.
If you want each Tab View to contain more than a dummy static control, you should use modeless and borderless DialogBox, children of the Tab Control, and show/hide them accordingly to the TCN_SELCHANGE notification.
If you don't realy need the Dialog Box functionalities, you could create regular child windows (with RegisterClass/CreateWindow) and then add your controls as child windows.
In either case, the show/hide code must react to TCN_SELCHANGE.
With regular child windows or with Dialog Box the net effect is the same: you will have a Window Procedure, or a Dialog Procadure, in which WM_COMMAND will deal with user actions.
Don't play with SetParent. But, if you absolutly want it, then use:
HWND hWndPushClear = CreateWindowEx(NULL,"button", "Clear", [...]
SetParent( hWndPushClear, window_handle );
You may have weird Paint/Focus problems, however.
EDIT: Unfortunately, it seems that there is no good Tab Control tutorial in line.
I recommend updating your actual source code. As a starting point, do the following:
Replace CreateWindowEx by CreateDialog for tab_window_1 end tab_window_2 (only one Dialog must be visible)
Suppress all SetParent calls
Your CreateWindowEx() and WM_COMMAND code are seems correct.
So,
Check return value of CreateWindowEx() whether is NULL. NULL means which is failed to create control.
Check HWND of parent is valid. In your code, it is tab_window_2.
Also, if you do not want to any WS_EX_XXX style, you can just use CreateWindow().
This code is just sample.
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
switch(iMessage) {
case WM_CREATE:
CreateWindow("button","Clear",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
20,20,100,25,hWnd,(HMENU)0,g_hInst,NULL);
return 0;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case 0:
MessageBox(hWnd,"Clear Button Clicked","Button",MB_OK);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

Resources