Click to See Complete Forum and Search --> : Subclassing


mmscg
December 15th, 2004, 03:48 PM
Hello again,

Time to learn something new!

I would like to create a new project creating a "PictureBox" again, but this time do it using a Static Control and subclassing it to capture mouse movements etc.

I am extremely happy with the "Child Window" method that MikeAThon taught me, but to continue to learn, I would like to explore this new concept.

I have found code snippets for subclassing, but none in a complete sample.

My attempts thus far have not produced a simple structure that will compile.

A code sample, or pseudo code to get me going would be appreciated.

NoHero
December 19th, 2004, 06:45 AM
In this Thread (http://www.codeguru.com/forum/showthread.php?t=321340) I have provided a sample of a button that gets subclass to show a different image on MouseHover. You may take a look at it because I catches the concept of subclassind and how to track the mouse.

mmscg
December 19th, 2004, 07:57 PM
Thanks!

I definately can use that as a basis to subclass my Static control.

P.S.
That's one ugly looking button!
(just teasing, :D ;)
it demonstrates owner-drawn controls and mouse "hover" quite nicely!)

mmscg
December 20th, 2004, 12:21 AM
OK, I've now got my subclassing working for a control inside the main window.

Problem is it doesn't work for the static control.

Here is what I have done:
(I have changed "static" to "button" when creating the control to be sub-classed
to demonstrate that it works for other controls; just not the "static")

#define STRICT
#define WIN32_LEAN_AND_MEAN

// Header Files
#include <windows.h>

// Prototypes
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK StaticWndProc(HWND, UINT, WPARAM, LPARAM);

// Global Variables
HINSTANCE hInst;
HWND hWndStatic;
WNDPROC lpfnStaticProc;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcx;
HWND hWndMain;
MSG Msg;

// Register the Window Class for the Main Window
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = 0;
wcx.lpfnWndProc = MainWndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wcx.lpszMenuName = NULL;
wcx.lpszClassName = "win32APIClass";
wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wcx))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Create and Show the Main Window
hWndMain = CreateWindowEx(
NULL, // no extended styles //WS_EX_CLIENTEDGE
"win32APIClass", // class name
"win32API Window", // window name
WS_OVERLAPPEDWINDOW, // overlapped window
CW_USEDEFAULT, CW_USEDEFAULT, // default horzontal & vertical positions
320, 240, // width & height
NULL, // no parent or owner window
NULL, // class menu used
hInstance, // instance handle
NULL // no window creation data
);

if(hWndMain == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

ShowWindow(hWndMain, nCmdShow);
UpdateWindow(hWndMain);

// Create the Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

return static_cast<int>(Msg.wParam);
}

// The Window Procedure
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
hWndStatic = CreateWindowEx(WS_EX_CLIENTEDGE,
"button",
0,
WS_VISIBLE | WS_CHILD ,
50, 50, 220, 100,
hWnd,
0,
hInst,
0);

// Subclass the static control
lpfnStaticProc = (WNDPROC)SetWindowLong(hWndStatic, GWL_WNDPROC, (long)(WNDPROC)StaticWndProc);
SetFocus(hWnd);
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
// Subclass procedure
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

switch (uMsg)
{
RECT rc;
case WM_LBUTTONDOWN:
MessageBox(NULL, "Left button down!", "Debug!",
MB_ICONEXCLAMATION | MB_OK);
break;
case WM_MOUSEMOVE:
GetClientRect(hWnd, &rc);
if (LOWORD(lParam) > 0 && HIWORD(lParam) > 0 && LOWORD(lParam) < rc.right - rc.left && HIWORD(lParam) < rc.bottom - rc.top)
{
if (GetCapture() != hWnd)
{
SetCapture(hWnd);
SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)(LPSTR)"Mouse inside control!");


}
}
else
{
SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)(LPSTR)"Mouse has left the building!");
ReleaseCapture();
}
break;

default:
return CallWindowProc(lpfnStaticProc, hWnd, uMsg, wParam, lParam);
}
return 0;
}

Andreas Masur
December 20th, 2004, 04:19 AM
Safe Subclassing in Win32 (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwui/html/msdn_subclas3.asp)

NoHero
December 20th, 2004, 11:38 AM
What do you exactly mean with "not working"? Don't you get any messages?
Because the code supposed to be right.

mmscg
December 20th, 2004, 12:20 PM
If you are referring to the code I posted, that works...

but if I change:
hWndStatic = CreateWindowEx(WS_EX_CLIENTEDGE, "button", 0, WS_VISIBLE | WS_CHILD ,...
to:
hWndStatic = CreateWindowEx(WS_EX_CLIENTEDGE, "static", 0, WS_VISIBLE | WS_CHILD ,...
For a static control (which I want to subcalss), then it doesn't work (mousemove and leftbuttondown messages are not intercepted).

NoHero
December 20th, 2004, 12:31 PM
I think this might be interesting for you:


Applications often use static controls to label other controls or to separate a group of controls. Although static controls are child windows, they cannot be selected. Therefore, they cannot receive the keyboard focus and cannot have a keyboard interface. A static control that has the SS_NOTIFY style receives mouse input, notifying the parent window when the user clicks or double clicks the control. Static controls belong to the STATIC window class.

Although static controls can be used in overlapped, pop-up, and child windows, they are designed for use in dialog boxes, where the system standardizes their behavior. By using static controls outside dialog boxes, a developer increases the risk that the application might behave in a nonstandard fashion. Typically, a developer either uses static controls in dialog boxes or uses the SS_OWNERDRAW style to create customized static controls.


If I change the code to this:


#define STRICT
#define WIN32_LEAN_AND_MEAN

// Header Files
#include <windows.h>

// Prototypes
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK StaticWndProc(HWND, UINT, WPARAM, LPARAM);

// Global Variables
HINSTANCE hInst;
HWND hWndStatic;
WNDPROC lpfnStaticProc;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcx;
HWND hWndMain;
MSG Msg;

// Register the Window Class for the Main Window
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = 0;
wcx.lpfnWndProc = MainWndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE+1;
wcx.lpszMenuName = NULL;
wcx.lpszClassName = "win32APIClass";
wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wcx))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Create and Show the Main Window
hWndMain = CreateWindowEx(
NULL, // no extended styles //WS_EX_CLIENTEDGE
"win32APIClass", // class name
"win32API Window", // window name
WS_OVERLAPPEDWINDOW, // overlapped window
CW_USEDEFAULT, CW_USEDEFAULT, // default horzontal & vertical positions
320, 240, // width & height
NULL, // no parent or owner window
NULL, // class menu used
hInstance, // instance handle
NULL // no window creation data
);

if(hWndMain == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

ShowWindow(hWndMain, nCmdShow);
UpdateWindow(hWndMain);

// Create the Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

return (int)(Msg.wParam);
}

// The Window Procedure
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
hWndStatic = CreateWindowEx(WS_EX_CLIENTEDGE,
"static",
0,
WS_VISIBLE | WS_CHILD | SS_NOTIFY,
50, 50, 220, 100,
hWnd,
0,
hInst,
0);

// Subclass the static control
lpfnStaticProc = (WNDPROC)SetWindowLong(hWndStatic, GWL_WNDPROC, (long)(WNDPROC)StaticWndProc);
SetFocus(hWnd);
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
// Subclass procedure
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

switch (uMsg)
{
RECT rc;
case WM_LBUTTONDOWN:
MessageBox(NULL, "Left button down!", "Debug!",
MB_ICONEXCLAMATION | MB_OK);
break;
case WM_MOUSEMOVE:
GetClientRect(hWnd, &rc);
if (LOWORD(lParam) > 0 && HIWORD(lParam) > 0 && LOWORD(lParam) < rc.right - rc.left && HIWORD(lParam) < rc.bottom - rc.top)
{
if (GetCapture() != hWnd)
{
SetCapture(hWnd);
SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)(LPSTR)"Mouse inside control!");


}
}
else
{
SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)(LPSTR)"Mouse has left the building!");
ReleaseCapture();
}
break;

default:
return CallWindowProc(lpfnStaticProc, hWnd, uMsg, wParam, lParam);
}
return 0;
}


It works fine.

(I just removed the static_cast<> because I used a C compiler to compile this)

mmscg
December 20th, 2004, 11:38 PM
Yep, that works... thanks!

The article referenced by Andreas Masur talks of removing the subclass.
I haven't done this.

1) Is it necessary?
2) Where do I put this line to remove the subclass if it is necessary?


// Remove the subclass for the (edit) control.
//
SetWindowLong(hEditWnd, GWL_WNDPROC, (DWORD) lpfnOldWndProc)

NoHero
December 21st, 2004, 08:22 AM
If you your subclass is not a one time thing you don't need to.
If you want to destroy your window you must not handle the WM_DESTROY message on your own. I have never done this so far only if it was necessary.