Solved

Custom cursors over Window controls

Posted on 1998-11-24
13
340 Views
Last Modified: 2013-12-03
I want to have a user selectable cursor displayed in my
windows, including over Window controls, menu bars, and
in dialog boxes ...
When I create a dialog box I call SetClassWord, passing
GCW_HCURSOR as a parameter, while processing the WM_INITDIALOG msg.  The cursor reverts back to the arrow
when over the menu bar.

For window controls I create a window (invisible with no
width/height) for each predefined type - ie LISTBOX,
COMBOBOX, STATIC ...) and call SetClassWord. This is
done whenever a custom cursor is selected and at when
the main program terminates (to revert to the arrow cursor)

There are stil some spots when the arrow cursor shows up -
inside drop down lists and over some window controls in
common dialog boxes.  I'm wondering what other methods
can be employed to get better results.
0
Comment
Question by:cpirrita
  • 7
  • 6
13 Comments
 
LVL 22

Accepted Solution

by:
nietod earned 100 total points
ID: 1416310
The cursor handle stored in the window class (the one you are changing) is sort of "the last option" cursor.  It is used if no other cursor is specified.  The proper approach would be to handle the WM_SETCURSOR message instead.  Details follow.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1416311
The way that WM_SETCURSOR is used is that it is sent to the frontmost window under the mouse.  The default window procedure's handler for this message, passeses it to the parent window and this continues until there is no parent window (or until somone overides the handler rather than passing it to the default window procedure), then the procedure returns false.  The child window's handler looks at the code returned value.  It it is true, it just returns true.  If it is false, it looks to see if the mouse is actually in the window, or if it is in one of its child windows.  if it is in a child window, it returns false.  If it is in the window, it sets the cursor to the cursor specfied in the class (or to the default arrow cursor if the mouse is in the non-client area or if there is no cursor for the class).

Thus in this way, when a window gets the WM_SETCURSOR message, it gives its parent window (and its grandparent and so on) an opportunity to set the cursor themselves.  If they do so, they return TRUE (which is passed down the window chain) and the message is effectively ignored by the window.  If none of them handle the message, they will (eventually) return FALSE.  The window procedure will then change the cursor to its default.

Thus you have two ways (at least) to handle this, depending on what you need to get done.  The easiest way may be to handle this message in the parent window.  This allows you to write a WM_SETCURSOR handler for just the parent window and it will effect the behaviour in the child windows.  However, for complex cases it may be easier to handle this by writting an WM_SETCORSOR handler for each child window as well.  

Let me know if you have questions.
0
 

Author Comment

by:cpirrita
ID: 1416312
Gave it a brief try last night - I had my main window call SetCursor from the WM_SETCURSOR msg.  Some of my windows are created via DialogBox or
DialogBoxParam. I don't believe they are considered child windows; the cursor
didn't change inside those windows. That cleared up when I put a WM_SETCURSOR
handler in them.  I did experience some cursor toggling when over some pre-defined  window controls; I got around that by initially using a SetClassWord to change
the cursor to NULL for each of the pre-defined window types.

While my custom cursor now does display over the menu bars - It still reverts
back to the arrow when inside a drop-down list or on the button that displays the
dropdown list.  It also toggles when on the scroll bar control.  The controls that gives
me problems were created via the resource editor and are attributes of the control
(eg COMBOBOX with CBS_DROPDOWNLIST;  EDITTEXT with ES_AUTOHSCROLL)
so I'm not sure if something like a window subclass procedure needs to be
defined for those controls...


0
 
LVL 22

Expert Comment

by:nietod
ID: 1416313
>> Some of my windows are created via DialogBox or DialogBoxParam.
>> I don't believe they are considered child windows
That is correct, they are not.  A child window is a window that is confined to another window's client area, like a control window or a MDI child window.  Dialogs are "owned" windows.  They can move outside of the owner's client area

>> I'm not sure if something like a window subclass procedure
>> needs to be defined for those controls
Unfortunately, it sounds like it.  If you put a handler in those control's parent window, and it made no difference, then those controls must not be passing the message to the parent window.  (What might be happening is that in some cursor locations, they handle the message directly, and in some locations they pass it on to the default window procedure where it is then passed to the parent.  This sort of logic is used in other places, like setting control colors, for example).  If that is the case, and it sounds like it is, you will have to sub-class the control and handle the message within the control.  (The easy handling would be to always call the defualt window procedure for the message, which will always call the parent window....)

One exception to this might be the scroll bars.  Do you mean "window scroll bars" that appear at the edges of the window (part of the frame) or do you mean "scroll bar controls" that is, seperate windows that appear within the client area of the window?
0
 

Author Comment

by:cpirrita
ID: 1416314
I meant scroll bar controls.  I'll try sub-classing the controls and see how that works
out.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1416315
Note that all the sub-classed controls can use the same window procedure (unless there are other things to be done) even though they are for different controls.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:cpirrita
ID: 1416316
thanks for the info - FYI subclasing did not change the cursor for those controls.  The
breakpoints I had inthe subclass procedure hit but the cursor either stayed an arrow or
it flickered between the arrow and the custom cursor.  On the whole though things look
better.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1416317
>>FYI subclasing did not change the cursor for those controls
It should have.  Perhaps the default window procedure was still getting called.  Can you post the window procedure code for the sub-classed controls?
0
 

Author Comment

by:cpirrita
ID: 1416318
here's a control that displays the custom cursor while in the text box
portion of the control but flickers between ther custom cursor and the
arrow cursor when in the drop down list box.
It's defined in the resource file as :

COMBOBOX     IDC_AD_PLIST,17,30,110,113, CBS_SIMPLE | WS_VSCROLL |
             WS_TABSTOP

 code for the parent window:

  switch (wMsg) {    
    case WM_INITDIALOG:
       hCursor   = GetCursor();    // static var
       hControl  = GetDlgItem(hDlg, IDC_AD_PLIST);  // static var
       SetClassWord(hDlg,     GCW_HCURSOR, NULL);
       SetClassWord(hControl, GCW_HCURSOR, NULL);
       lpfnMyListBox = MakeProcInstance((FARPROC)pListBox, hInstance);
       lpfnListBox   = (FARPROC)GetWindowLong(hControl, GWL_WNDPROC);
       SetWindowLong(hControl, GWL_WNDPROC, (LONG)lpfnMyListBox);
       return TRUE;
     case WM_SETCURSOR:
       SetCursor(hCursor);
       return TRUE;

 subclass procedure for combobox

LONG FAR PASCAL pListBox(HWND hWnd, WORD wMsg, WORD wParam,LONG lParam)
{

  switch (wMsg) {
     case WM_SETCURSOR:
       SetCursor(GetCursor());
       break;
  }
  return CallWindowProc(lpfnListBox, hWnd,wMsg,wParam,lParam);
}


Breakpoints set in the WM_SETCURSOR hit once the cursor is placed within
the drop down listbox.  I tried adding a WM_CREATE portion that set the
cursor for hWnd to NULL but the breakpoint never hit.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1416319
In the Subclass procedure the SetCursor(GetCursor()) is a problem.  It effectively does nothing so it is hard to say what that will produce.

It should either do a SetCursor(XXXX) where XXXX is a cursor handle to the cursor you want to use.  (probably stored in a global varaible) OR it should forward the message to the parent window like
return SendMessage(GetParent(),wmsg,wParam,lParam);

In the parent (dialog) window procedure I suspect that the WM_SETCURSOR is not correct.  The hCursor  value it is using may not be initialized.  (I can't tell for sure, but if it is initialized, then this is not a problem).

Actually, the WM_INITDIALOG seems like it might try to initialize the hCursor, but if so, it does it poorly.  again it uses the GetCursor() procedure and there is no telling what that will return.  if you want to use the dialog's default cursor handle, use GetClassWord with the dialog window to get the default cursor for the dialog.

Note that for this to work hCursor must be a global or static variable.  If it is local varaible then the value set on WM_INITDIALOG won't be changed on the WM_SETCURSOR.
0
 

Author Comment

by:cpirrita
ID: 1416320
I should have explained things a little more than I did.
The main window of my program changes the cursor.  The handle to the cursor is
stored in a static variable.  it is initialized to the arrow cursor
The GetCursor function is a function of mine that returns this static variable.
By the time the code that I listed runs, I have already selected a custom cursor so
the GetCursor function returns a valid handle.  I have already verified via breakpoints
that the listed procedure and the subclass procedure are using a valid cursor handle.

It just dawned on me that from reading your comments it seems like we're talking
about different GetCursor functions - I just checked windows.h and saw another
GetCursor (oops).  I changed my function's name and re-ran it. However I
got the same results (breakpoints still show valid cursor handle)
0
 
LVL 22

Expert Comment

by:nietod
ID: 1416321
I can't image how it compiled with the two GetCursor()s running around.

Anyways I missed the obvious problem.  Even though your sub-class window procedure sets the cursor  (correctly it seems) it still calls the original window procedure, which ends up settign the cursor as well.  Don't call the original window procedure for this message.

The easy way to do this is to put the call to the original window procedure in a "default" section of the switch() statement.
0
 

Author Comment

by:cpirrita
ID: 1416322
I did that and verified I return 0 instead of calling CallWindowProc when the
WM_SETCURSOR msg is processed but didn't get any different results.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

What my article will show is if you ever had to do processing to a listbox without being able to just select all the items in it. My software Visual Studio 2008 crystal report v11 My issue was I wanted to add crystal report to a form and show…
Entering time in Microsoft Access can be difficult. An input mask often bothers users more than helping them and won't catch all typing errors. This article shows how to create a textbox for 24-hour time input with full validation politely catching …
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now