C++ window draggable by its menu bar - c++

So I'm using Visual C++, and I have created a draggable, borderless window. Anyway, there is a toolbar along the top, and i want to be able to drag the window by that toolbar. I still want the toolbar to be functional, but i have no earthly idea how to be able to drag the window by it. This is my current window (see the toolbar on the top):
And this is my current code to make it draggable:
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
if(hit == HTCLIENT) hit = HTCAPTION;
return hit;
}
break;

You are on the right track with hooking WM_NCHITTEST. Now, you need to make change what constitutes a client hit versus a caption hit. If I understand your code right now, anywhere you click within the client area of the window (everything but the border) will allow you to drag the window elsewhere. This will make interacting with your application very difficult. Instead, you should be returning HTCAPTION only after you have determined that the hit was within the menubar area. Specifically, the area of the menubar that does not hold the File/Edit/Help buttons.
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
if (hit == HTCLIENT) { // The hit was somewhere in the client area. Don't know where yet.
// Perform your test for whether the hit was in the region you would like to intercept as a move and set hit only when it is.
// You will have to pay particular attention to whether the user is actually clicking on File/Edit/Help and take care not to intercept this case.
// hit = HTCAPTION;
}
return hit;
break;
}
Some things to keep in mind here:
This can be very confusing to a user that wants to minimize, close, or move your application. Menubars do not convey to the user that you can move the window by dragging them.
If you are concerned with vertical pixels you may consider doing what other applications on Windows are starting to do -- moving the menubar functionality to a single button that is drawn in the titlebar. (See recent versions of Firefox/Opera or Windows explorer in Windows 8 for some idea to move things to the titlebar.

In one of my applications I also wanted to make the window what I call "client area draggable". Unfortunately the mentioned solution (replacing HTCLIENT with HTCAPTION)
does have a serious flaws:
Double-clicking in the client area now shows the same behaviour as
double-clicking the caption (i.e. minimizing/maximizing the window)!
To solve this I did the following in my message handler (excerpt):
case WM_MOUSEMOVE:
// Move window if we are dragging it
if (mIsDragging) // variable: bool mIsDragging;
{
POINT mousePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
mIsDragging = (ClientToScreen(hWnd, &mousePos) &&
SetWindowPos(hWnd,
NULL,
mDragOrigin.left + mousePos.x - mDragPos.x,
mDragOrigin.top + mousePos.y - mDragPos.y,
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE));
}
break;
case WM_LBUTTONDOWN:
// Check if we are dragging and safe current cursor position in case
if (wParam == MK_LBUTTON)
{
POINT mousePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
if (ClientToScreen(hWnd, &mousePos) &&
DragDetect(hWnd, mousePos))
{
// Check if the cursor is pointing to your new caption here!!!!
mIsDragging = true;
mDragPos = mousePos;
GetWindowRect(hWnd, &mDragOrigin);
SetCapture(hWnd);
}
}
break;
// Remove this case when ESC key handling not necessary
case WM_KEYDOWN:
// Restore original window position if ESC is pressed and dragging active
if (!mIsDragging || wParam != VK_ESCAPE)
{
break;
}
// ESC key AND dragging... we restore original position of window
// and fall through to WM_LBUTTONUP as if mouse button was released
// (i.o.w. NO break;)
SetWindowPos(hWnd, NULL, mDragOrigin.left, mDragOrigin.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
case WM_LBUTTONUP:
ReleaseCapture();
break;
case WM_CAPTURECHANGED:
mIsDragging = false;
break;
The (pseudo) code omits the return values (default: 0) and variable definitions but
should make the procedure clear anyway!? (If not drop me a line and I'll add more
or all code).
ps: I just found another comprehensive description which also explains the differences
of these two solutions: http://tinyurl.com/bqtyt3q

Related

How to add checkbox beside a text using mfc

I'm learning mfc, activex controls and containers. I'm doing a assignment on containers(Dialog).I have created a dialog, when I do right click, there are system menu items like Minimize, Maximize, Move, Size. All these items appeared by selecting true in dialog properties. I have added another item "Always on top" which will be appear on top. When the user click on Always on top I provided a message box which will say "Do you want enable?" I gave yes or no for that, but I have to avoid this message box and need to add a checkbox beside the Always on top item. So that whenever user wants the dialog to appear on top they can use the checkbox.
I have tried in one possible way by adding a bitmap image of checkmark but somehow it's not working and I have less time. If anyone has any idea how I can add this checkbox please let me know.
This is the code where I have appended the item
BOOL CDiagDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
CMenu * pSystemMenu = GetSystemMenu(FALSE);
pSystemMenu->AppendMenu(MF_ENABLED, IDM_SYSCOMMAND_CUSTOM,_T("Always on top"));
return TRUE; // return TRUE unless you set the focus to a control
}
void CDiagDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if (nID == IDM_SYSCOMMAND_CUSTOM)
{
AfxMessageBox(
_T("Enable Always on top"), MB_YESNO
);
switch (iResponse) {
case IDYES:
SetWindowPos(&CWnd::wndTopMost, 0, 0, 0, 0, SWP_NOSIZE);
break;
case IDNO:
SetWindowPos(&CWnd::wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE);
break;
}
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}

Win32 detect if window is maximized/docked to half screen (Win-key + Left/Right)

I have a classic Win32-API (C++) application and need to detect if the window is docked to the left/right half of the screen.
Background of the question is that the window only sizes in grid steps, let's say 32 pixel. In full screen the program detects that state, allow size to match the full screen and pad the excess space. With Windows 8 and later I would like to do the same instead of currently leaving borders (because the size snaps to a multiple of 32 pixel).
With the function GetWindowPlacement() you can retrieve the normal window rectangle, using the member rcNormalPosition of WINDOWPLACEMENT. Then compare the normal rectangle to the actual window rectangle. If they don't match the window is most likely in a docked state.
Example:
bool IsDockedToMonitor(HWND hWnd)
{
WINDOWPLACEMENT placement = {sizeof(WINDOWPLACEMENT)};
GetWindowPlacement(hWnd, &placement);
RECT rc;
GetWindowRect(hWnd, &rc);
return placement.showCmd == SW_SHOWNORMAL
&& (rc.left != placement.rcNormalPosition.left ||
rc.top != placement.rcNormalPosition.top ||
rc.right != placement.rcNormalPosition.right ||
rc.bottom != placement.rcNormalPosition.bottom);
}
Note that this solution is not reliable 100% of the time. There is a slim chance that the normal rectangle and current window rectangle could match even when the window is docked to the side of the monitor.
The Aero Snap feature is built into the Shell, not the window manager. As such, there is no particular window style or flag that indicates the docked state. The Shell simply repositions windows in response to certain actions (and internally records the state). It does so in a way that is indistinguishable from manually repositioning a window with the mouse or keyboard.
You cannot reliably determine, whether a window is docked to the left or right of the screen. There is no particular message sent by the Shell, nor is a window's size and position relative to the working area a sufficient property.
What you are trying to accomplish isn't possible. You will have to implement a solution, that doesn't require the information that isn't available. One such implementation would be to always use padding for window sizes, that don't allow the entire client area to be used. Another solution would be to implement the opposite: Allow window resizing to any size, unless you know the user is manually resizing the window. You can determine the latter by handling the WM_SIZING message.
In addition to what IInspectable has already mentioned, there is another way to determine this information and act accordingly.
Wait for a WM_WINDOWPOSCHANGED message and read its x, y, cx, and cy values from the WINDOWPOS pointer stored in lParam.
Get a handle to the current monitor on which the window is placed by calling MonitorFromWindow.
Create a MONITORINFO variable and set its cbSize field to sizeof(MONITORINFO).
Use the monitor handle and the address of your MONITORINFO variable to call GetMonitorInfo.
Read the rcWork value from your MONITORINFO variable.
rcWork.top == WINDOWPOS.y && rcWork.bottom == (WINDOWPOS.y + WINDOWPOS.cx) && rcWork.left == WINDOWPOS.x - the window is "docked" to the left
rcWork.top == WINDOWPOS.y && rcWork.bottom == (WINDOWPOS.y + WINDOWPOS.cx) && rcwork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the right
rcWork.top == WINDOWPOS.y && rcWork.left == WINDOWPOS.x && rcWork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the top
rcWork.top == (WINDOWPOS.y + WINDOWPOS.cy) && rcWork.left == WINDOWPOS.x && rcWork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the bottom
You say you already have logic to determine if the window is fullscreen (do you mean fullscreen or maximized?), but effective maximization can be determined if left == x && top == y && right == x + cx && bottom == y + cy.
Here is an MSDN example of something similar.
Note that it may be more desirable to cache the MONITORINFO values so you don't need to call it every time the window is repositioned.
If you only want this to apply when a user does NOT manually resize the window, here is a contrived example of a possible way to do so:
LRESULT CALLBACK windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static bool userSizing = false;
switch (msg)
{
// could also catch WM_ENTERSIZEMOVE here, but this will trigger on
// moves as well as sizes
case WM_SIZING:
userSizing = true;
break;
case WM_EXITSIZEMOVE:
userSizing = false;
break;
case WM_WINDOWPOSCHANGED:
if (userSizing)
{
break;
}
// do logic to check to see if the window is sized in a "docked"
// manner here
break;
// handle other window messages ...
}
}

The entire scrollbar moves in a CWnd when calling ScrollWindowEx with SW_SCROLLCHILDREN

I've added a CScrollBar to my Cwnd named CPanel. But when I scroll the page the whole scrollbar moves. Any idea how I can solve this? Changing CPanel to a CScrollView or CFormView is sadly not an option.
CPanel::CPanel()
{
CreateEx(WS_EX_CONTROLPARENT, _T("Static"), NULL, WS_CHILD | WS_TABSTOP | WS_BORDER, m_clRect, pwndParent, IDC_PANEL_FORM);
ScrollBarInit();
}
void CPanel::ScrollBarInit()
{
//Before this i calculate size of scrollbar and size of scrollarea
m_pclScrollBar = new CScrollBar();
m_pclScrollBar->Create(WS_CHILD | WS_VISIBLE | SBS_VERT | SBS_RIGHTALIGN, clRectScrollbar, this, IDC_SCROLLBAR_FORM);
m_pclScrollBar->SetScrollRange(VSCROLL_RANGE_MIN, VSCROLL_RANGE_MAX);
//After this I add scrollbar info
}
void CPanel::OnVScroll(UINT iSBCode, UINT iPos, CScrollBar* pclScrollBar)
{
switch(pclScrollBar->GetDlgCtrlID())
{
case IDC_SCROLLBAR_FORM:
ScrollBarScroll(iSBCode, iPos, pclScrollBar);
break;
}
}
void CPanel::ScrollBarScroll(UINT iSBCode, UINT iPos, CScrollBar *pclScrollBar)
{
int iScrollPositionPrevious;
int iScrollPosition;
int iScrollPositionOriginal;
iScrollPositionOriginal = m_pclScrollBar->GetScrollPos();
iScrollPosition = iScrollPositionOriginal;
if(m_pclScrollBar != NULL)
{
SCROLLINFO info = {sizeof( SCROLLINFO ), SIF_ALL};
pclScrollBar->GetScrollInfo(&info, SB_CTL);
pclScrollBar->GetScrollRange(&info.nMin, &info.nMax);
info.nPos = pclScrollBar->GetScrollPos();
iScrollPositionPrevious = info.nPos;
switch(iSBCode)
{
case SB_TOP: // Scroll to top
iScrollPosition = VSCROLL_RANGE_MIN;
break;
case SB_BOTTOM: // Scroll to bottom
iScrollPosition = VSCROLL_RANGE_MAX;
break;
case SB_ENDSCROLL: // End scroll
break;
case SB_LINEUP: // Scroll one line up
if(iScrollPosition - VSCROLL_LINE >= VSCROLL_RANGE_MIN)
iScrollPosition -= VSCROLL_LINE;
else
iScrollPosition = VSCROLL_RANGE_MIN;
break;
case SB_LINEDOWN: // Scroll one line down
if(iScrollPosition + VSCROLL_LINE <= VSCROLL_RANGE_MAX)
iScrollPosition += VSCROLL_LINE;
else
iScrollPosition = VSCROLL_RANGE_MAX;
break;
case SB_PAGEUP: // Scroll one page up
{
// Get the page size
SCROLLINFO scrollInfo;
m_pclScrollBar->GetScrollInfo(&scrollInfo, SIF_ALL);
if(iScrollPosition > VSCROLL_RANGE_MIN)
iScrollPosition = max(VSCROLL_RANGE_MIN, iScrollPosition - VSCROLL_PAGE);
break;
}
case SB_PAGEDOWN: // Scroll one page down
{
// Get the page size
SCROLLINFO scrollInfo;
m_pclScrollBar->GetScrollInfo(&scrollInfo, SIF_ALL);
if(iScrollPosition < VSCROLL_RANGE_MAX)
iScrollPosition = min(VSCROLL_RANGE_MAX, iScrollPosition + VSCROLL_PAGE);
break;
}
case SB_THUMBPOSITION: // Scroll to the absolute position. The current position is provided in nPos
case SB_THUMBTRACK: // Drag scroll box to specified position. The current position is provided in nPos
iScrollPosition = iPos;
break;
default:
break;
}
if(iScrollPositionOriginal != iScrollPosition)
{
m_pclScrollBar->SetScrollPos(iScrollPosition);
ScrollWindowEx(0, iScrollPositionOriginal - iScrollPosition, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE);
}
}
}
Since you specified SW_SCROLLCHILDREN in your call to ScrollWindowEx (see also the Windows API documentation for ScrollWindowEx; it is usually better than MFC's), and requested the entire client area to be scrolled by passing NULL for the lpRectScroll parameter, the system does just that. The scrollbar is a child window as well, so it gets moved like all other child controls.
The solution is hinted to in the documentation for SW_SCROLLCHILDREN:
Scrolls all child windows that intersect the rectangle pointed to by lpRectScroll by the number of pixels specified in dx and dy.
To prevent the scrollbar from moving alongside the other child windows, it must be excluded from the rectangle passed as the lpRectScroll parameter. To do so, query the client area, and subtract the area occupied by the scrollbar. Assuming that the scrollbar is at the right and covers the entire height, the following code will solve your issue:
if(iScrollPositionOriginal != iScrollPosition) {
m_pclScrollBar->SetScrollPos(iScrollPosition);
// Query the window's client area
CRect clientArea;
GetClientRect(clientArea);
// Find the area occupied by the scrollbar
CRect scrollbarArea;
m_pclScrollBar->GetWindowRect(scrollbarArea);
// Adjust the client area to exclude the scrollbar area
CRect scrollArea(clientArea);
scrollArea.DeflateRect(0, 0, scrollbarArea.Width(), 0);
ScrollWindowEx(0, iScrollPositionOriginal - iScrollPosition, scrollArea, NULL,
NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE);
}
Also keep in mind the remark on using the SW_SCROLLCHILDREN flag:
If the SW_SCROLLCHILDREN flag is specified, Windows will not properly update the screen if part of a child window is scrolled. The part of the scrolled child window that lies outside the source rectangle will not be erased and will not be redrawn properly in its new destination. Use the DeferWindowPos Windows function to move child windows that do not lie completely within the lpRectScroll rectangle.
Since you don't have control over the amount of pixels the user will scroll, there will be situations where the screen will not properly update. To work around this, implement a solution following the procedure outlined in the quote above: Replace the call to ScrollWindowEx with a series of calls to DeferWindowPos, repositioning all child windows manually.

Right Click on ListView WinAPI C++

I have a window with a listview which I would like to right click an entry in the listview and have certain options displayed in a context menu. I cant find any examples for C++. The similar question is here but this is for listbox and i cant make it work in my program
So far I could not find any examples for c++ online. I could make it work for WM_LBUTTONDOWN on my main window but not on listview. Anyway, this is my code which i cant make it work:
case WM_CONTEXTMENU:
if ((HWND)wParam == hWndListView) {
POINT cursor;
GetCursorPos(&cursor);
TrackPopupMenu((HMENU)GetSubMenu(LoadMenu(hInst, MAKEINTRESOURCE(IDR_CONTEXT)), 0), TPM_LEFTALIGN | TPM_RIGHTBUTTON, cursor.x, cursor.y, 0, hWnd, NULL);
}
break;
Any advise? Thanks
Answer:
case WM_NOTIFY:
// When right button clicked on mouse
if ((((LPNMHDR)lParam)->hwndFrom) == hWndListView)
{
switch (((LPNMHDR)lParam)->code)
{
case NM_RCLICK:
{
POINT cursor; // Getting the cursor position
GetCursorPos(&cursor);
// Creating the po-up menu list
TrackPopupMenu((HMENU)GetSubMenu(LoadMenu(hInst, MAKEINTRESOURCE(IDR_CONTEXT)), 0), TPM_LEFTALIGN | TPM_RIGHTBUTTON, cursor.x, cursor.y, 0, hWnd, NULL);
}
break;
}
break;
}
break;
This works under WndProc

Reset cursor in WM_SETCURSOR handler properly

INTRODUCTION AND RELEVANT INFORMATION:
I have made an application that needs to change the look of a cursor into hand when mouse hovers above the static control, but resets it to a normal cursor otherwise.
My initial application was in full screen mode, but recently terms have changed and it must have a resizable window.
This means that my handler for WM_SETCURSOR must be rewritten to reflect newly introduced changes.
Cursors are loaded in WM_CREATE, and I have defined class cursor, like this:
// cursors
case WM_CREATE:
hCursorHand = LoadCursor( NULL, IDC_HAND );
hCursorArrow = LoadCursor( NULL, IDC_ARROW );
// other stuff
In my class:
WNDCLASSEX wc;
// ...
wc.hCursor = hCursorArrow;
//...
This is my old WM_CURSOR handler ( code is simplified for clarity purposes ):
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
SetCursor(hCursorHand);
else
SetCursor(hCursorArrow);
return TRUE;
If cursor hovers above static control, then my handler changes it to hand, else sets it to default cursor ( arrow ).
Bellow is the picture I have sketched in Paint that displays the desired look of the cursor when it hovers above static control, it is on the client area, and when user resizes window.
If additional code snippets are required, ask and I will edit my post, but for now, they are omitted to keep the post short and concise.
I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.
MY EFFORTS TO SOLVE PROBLEM:
Bellow are the code snippets that I have tried, but they all failed:
First snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
return DefWindowProc( hWnd, msg, lParam, wParam );
Second Snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
break; // based on MSDN example
Third snippet:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
return FALSE;
These set cursor to hand no matter where it is.
If I leave my WM_SETCURSOR handler unchanged, the only problem I get is that instead of sizing arrows, I get regular arrow ( as the cursor’s look ) when I hover over the border, but window can be sized.
If I comment out my WM_SETCURSOR handler, sizing arrows and cursor arrow appear properly, but cursor doesn’t change into hand when hovers above static control ( which is logical, since there is no WM_SETCURSOR handler to change it ).
I have browsed through SO archive, looked on MSDN, CodeProject , DaniWeb, Cprogramming and CodeGuru, but had no success.
Looking through those, I have found an example where people compare low word of the lParam against hit test code.
Looking through MSDN I have found link for hit test values ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms645618%28v=vs.85%29.aspx ) and I have found link for cursor types (http://msdn.microsoft.com/en-us/library/windows/desktop/ms648391%28v=vs.85%29.aspx ).
Currently I am reading them, because I think that I will have to load additional cursor resources, take several comparisons of hit test values, and then use those resources to set cursor look accordingly.
QUESTION:
I really would like my WM_SETCURSOR handler to look like this:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
else
// reset cursor's look to default
so I ask the community to instruct me on how to do this.
If this isn’t possible, then I will consider using multiple if statements to check the hit test code, and set cursor’s look accordingly.
Of course, if there is better solution for my problem, please suggest it, I will consider it as well.
Thank you.
Regards.
In general, if you handle the WM_SETCURSOR message you must either
Call SetCursor() to set the cursor, and return TRUE, or
If the message came from a child window, return FALSE for default processing, or
If the message is from your own window, pass the message through to DefWindowProc()
I think the last two points aren't made quite clear by the MSDN docs.
The window under the mouse pointer gets the first WM_SETCURSOR message. If it handles it and returns at that point, nothing else happens. If however it calls DefWindowProc(), then DWP forwards the message to the window's parent to handle. If the parent chooses not to handle it, it can return FALSE and the DefWindowProc processing will continue.
But this only applies if the message came from a previous call to DWP. If the message originated with the window itself, rather than a child, returning TRUE or FALSE without setting the cursor means the cursor won't be set at all.
Another thing: although your question didn't specify, I'm assuming from your use of GetDlgItem() that your top-level window is a dialog. If that's true, you can't just return TRUE or FALSE for a message - you need to return the value using SetWindowLongPtr() and store the return value in DWLP_MSGRESULT. Returning FALSE from a dialog procedure indicates that you didn't handle the message at all - this is equivalent to passing a message through to DefWindowProc().
So I think the proper handling for your situation is, in your top-level window:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
return TRUE;
}
return FALSE;
If your top-level window isn't in fact a dialog, you would do this:
case WM_SETCURSOR:
if( (HWND)wParam == GetDlgItem( hwnd, 4000 ) )
{
SetCursor(hCursorHand);
return TRUE;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
Here's my first example, if cursor go to menubar, cursor changes to cursor hand:
HCURSOR cursorHand = LoadCursor(NULL, IDC_HAND);
case WM_SETCURSOR:
if(LOWORD(lParam) == HTMENU)
{
SetCursor(cursorHand);
}
break;
Here's my second example, if cursor goes to button, cursor changes to cursorHand:
HCURSOR cursorHand = LoadCursor(NULL, IDC_HAND);
case WM_SETCURSOR:
if(LOWORD(lParam) == buttonId)//declare you
{
SetCursor(cursorHand);
}
break;
Warning:menubar and button is not created! Create you and please, check my answer example.
The question as I understand it is, given a parent window with one more more child windows (one of which is a static control) how do you set the cursor to the hand when the cursor is over the static control, and the arrow when the cursor is over the client area of the parent window, and let default processing take place when the cursor is over a non-client area of the parent window.
To check things out, I wrote a simple program with a top-level window with a static control as a child window. My first attempt was the following:
1) Set the class cursor of the top-level window to LoadCursor(NULL, IDC_ARROW). This allows the default processing to set the cursor to the arrow when appropriate.
2) Keep track of the position of the mouse cursor by handling the WM_MOUSEMOVE message by calling my HandleWMMouseMove function as follows:
case WM_MOUSEMOVE:
HandleWMMouseMove(lParam);
break;
//ptCurrMousePos is a global variable of type POINT
static void HandleWMMouseMove(LPARAM mousepos)
{
ptCurrMousePos.x = (int)(short)(LOWORD(mousepos));
ptCurrMousePos.y = (int)(short)(HIWORD(mousepos));
}
3) and then all I had to do was handle the WM_SETCURSOR message by calling my HandleWMSetCursor function as follows:
case WM_SETCURSOR:
if (HandleWMSetCursor())
return TRUE;
break;
//hwndFrame is a handle to the top-level window.
//hwndStatic is a handle to the static control.
static BOOL HandleWMSetCursor(void)
{
if (ChildWindowFromPoint(hwndFrame, ptCurrMousePos)==hwndStatic) {
SetCursor(hCursorHand);
return TRUE;
}
return FALSE;
}
this worked fine but I couldn't understand how the code posted in the question was working even partially. So I asked the questioner whether the child window was really a static control. The answer was yes but it was created with the SS_NOTIFY style. So I created my child window with this style and my code stopped working, but the code posted with the question started to work. With the help of the Spy++ program distributed with Visual Studio I learned the following.
Static controls are normally transparent (not in the visual sense, but meaning that even when the mouse is over the transparent window, Windows will consider the mouse to be over the window underneath the transparent window).
When you create a static control with SS_NOTIFY the control is no longer transparent (i.e. it starts to receive an process mouse messages (like WM_MOUSEMOVE), so my code stopped working because it never received WM_MOUSE messages when the cursor was over the static control. On the other hand the code in the question did not work when the static control was created without SS_NOTIFY because without this style the static control was transparent, which meant that the WARAM in the WM_SETCURSOR message was never equal to the static control (in other words Windows never considered the mouse to be over the static control because it was transparent).
It is possible to combine the two approaches by changing the WM_SETCURSOR handler function to the following:
//hwndFrame is a handle to the top-level window.
//hwndStatic is a handle to the static control.
static BOOL HandleWMSetCursor(WPARAM wParam)
{
if (((HWND)wParam == hwndStatic) || (ChildWindowFromPoint(hwndFrame, ptCurrMousePos)==hwndStatic)) {
SetCursor(hCursorHand);
return TRUE;
}
return FALSE;
}