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));
}
Related
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;
}
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;
}
I'm playing around with Windows and D2D1
However when restoring my minimized borderless/menuless window
sometimes I get this very ugly bug
For a frame (or a few more) before the window gets drawn with D2D1
it will display this title bar with the name of the window.
This happens on about 5-10% of the restore operations.
The window class style is set to
CS_DBLCLKS|CS_OWNDC
but Ive also tried other styles.
The Window is created with CreateWindow and WS_POPUP|WS_SYSMENU as dwStyle
My rendering method is called on WM_PAINT but I also tried to move it so it
gets called every time but that does not help.
Any help is appreciated :)
#
I've found a workaround which I'm not completely fine with
Instead of calling ShowWindow(hWnd, SW_RESTORE)
I call
ShowWindow(hWnd, SW_HIDE);
ShowWindow(hWnd, SW_RESTORE);
ShowWindow(hWnd, SW_SHOW);
This however results in the taskbar icon being "renewed" which I dont want either.
Short simplified example code which features this problem (when minimizing/restoring)
#include <Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR cmd, int cmdShow)
{
WNDCLASSEX TestWC = { 0 };
TestWC.cbSize = sizeof(WNDCLASSEX);
TestWC.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(50, 50, 50));
TestWC.lpfnWndProc = DefWindowProc;
TestWC.lpszClassName = "Testklasse";
TestWC.style = CS_DBLCLKS | CS_OWNDC;
TestWC.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassEx(&TestWC);
HWND htest = CreateWindow("Testklasse", "Test", WS_POPUP | WS_SYSMENU | WS_VISIBLE, 200, 200, 400, 248, 0, 0, 0, 0);
MSG wMsg = { 0 };
bool shown = true;
while (wMsg.message != WM_QUIT)
{
if (PeekMessage(&wMsg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&wMsg);
DispatchMessage(&wMsg);
}
if (GetAsyncKeyState(VK_INSERT) & 0x8000 && shown)
{
shown = false;
ShowWindow(htest, SW_MINIMIZE);
continue;
}
if (GetAsyncKeyState(VK_DELETE) & 0x8000 && !shown)
{
shown = true;
ShowWindow(htest, SW_RESTORE);
continue;
}
if (GetAsyncKeyState(VK_END) & 0x8000)
PostQuitMessage(1);
Sleep(50);
}
return 1;
}
Found the solution myself:
Minimizing and restoring a borderless window
is undefined behaviour.
A workaround is to catch the WM_NCPAINT and draw the WHOLE
window yourself (even if its borderless it has a non-client area).
I have a window with 10 child edit controls in it. I would like to move from one edit control to other by pressing the tab key - how do I do this?
I mean, even if I could find out whether I pressed the tab-key, how do I find the next edit control to focus into? I hope I do not have to keep track of the edit controls myself as I already added them to the parent window.
PS: by "next" I mean the order I created the edit controls...
Edit: I'm on Win32 using plain C.
Edit 2: sample
#include
#define NAME "test"
LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND edit1, edit2;
switch (msg)
{
case WM_CREATE:
edit1 = CreateWindow("edit", "", WS_CHILD|WS_VISIBLE, 0, 0, 200, 50, hWnd, NULL, NULL, NULL);
edit2 = CreateWindow("edit", "", WS_CHILD|WS_VISIBLE, 250, 0, 200, 50, hWnd, NULL, NULL, NULL);
return 0;
case WM_CLOSE:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NAME;
wc.lpszClassName = NAME;
RegisterClass(&wc);
HWND win;
win = CreateWindow(NAME, "test", WS_OVERLAPPEDWINDOW, 0, 0, 500, 500, NULL, NULL, hInstance, NULL);
ShowWindow(win, nCmdShow);
UpdateWindow(win);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
The dialog manager does a lot of this for you, so if you don't have a good reason for creating your own window class, you might consider creating a dialog instead.
If you're still of a mind to roll your own, you'll have to intercept WM_CHAR and look for VK_TAB and VK_SHIFT | VK_TAB. The dialog manager uses something called "z-order" as the tab order (http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#zorder).
FWIW, my advice would be to not underestimate the burden you're taking on when trying to re-create an existing facility in Windows like this. For every behavior that you know about, there are usually at least as many that you don't. For example, how will your application behave on a pen-based device? What about accessibility extensions? Will screen readers be able to handle it properly? All of that stuff is already baked into the dialog manager.
I'm not sure I followed... I thought
dialogs are just a floating windows
above the "regular" (?) one. Is it
possible that the application uses
just a dialog, instead of proper
CreateWindow()?
Dialogs can be modal or modeless children of the main application window, but they can also be the main application window. That's typically called a dialog-based app.
If you want a dialog-based application (that is, an application with a dialog as it's main window), you'd do something like this:
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow )
{
MSG msg;
HWND hDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
if (hDlg != NULL)
{
ShowWindow(hDlg, SW_SHOWNORMAL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
The full example is probably too long to list here, but if you Google for CreateDialog, you should find some examples of this.
...And is it possible to
add other windows inside a dialog?
Even with custom window procedures?
(In other words, in dialogs, am I not
restricted just to the default
controls, like edit and static?)
Yes, you can create custom controls within the dialog. Within the DIALOG portion of your .rc file, you can include something like:
CONTROL "",IDC_MYCUSTOM,"MyCtrlClassName",WS_TABSTOP,10,20,30,40
You can also create a new control and add it to the dialog dynamically (I'll let you Google for an example).
Also, if I wanted to roll my own, how
do I find out what's the next control?
I don't really think there will be
more use cases than a basic desktop
Use GetNextDlgTabItem() if you want to cycle through the controls within a dialog in tab-order: http://msdn.microsoft.com/en-us/library/ms645495%28VS.85%29.aspx
If you're rolling your own, then you probably want something like the EnumChildWindows() function: http://msdn.microsoft.com/en-us/library/ms633494%28VS.85%29.aspx
This may also be useful: http://msdn.microsoft.com/en-us/library/bb775501%28VS.85%29.aspx
This kind of processing can be easily added to an application :- Add a call to IsDialogMessage() in your message loop - all the controls have to have the WS_TABSTOP style.
The parent window might have to be of the dialog class as the dialog window class stores state allowing it to (for example) restore focus to the correct control when activation is lost and restored.
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.