-
Controlling CWaitCursor from different methods
Hello,
I read some documentation on CWaitCursor and can't find my solution. Nor in the archives of this forum.
I want to display an hourglass while some treatment is done.
But the call of the treatment and its end are not in the same function.
At start I send a message to another module, and want to start my CWaitcursor there.
The application goes on, may do some other treatment.
When I receive the answer, in the function where I treat the message I want to end the CWaitCursor.
Should I create a separate thread whise work will only be to display an hourglass and kill it at the end of the treatment? Or is there a better way of doing it??
Thanks for any hint
Marina
-
Re: Controlling CWaitCursor from different methods
so, what is it that's happening curently?
BTW: I had a lot of problems displaying a CWait... in a thread.
-
Re: Controlling CWaitCursor from different methods
I think I see the cursor change to hourglass, but it goes back to normal at the end of my method, i.e. when I send my message to the other module.
I don't know how to make it last longer than the scope where it has been created;
I use : or would like to do something like :
Code:
OnAction()
{
// Get my data
RestoreWaitCursor();
// Send my message
}
OnReceiveAnswerMessage()
{
// Do some treatment
EndWaitCursor();
}
-
Re: Controlling CWaitCursor from different methods
this is from MSDN. maybe it helps:
Code:
void CMyView::AnotherFunction()
{
// some processing ...
CMyDialog dlg;
dlg.DoModal();
RestoreWaitCursor();
// some more processing ...
}
// If the dialog is invoked from a member function of
// some non-CCmdTarget, then you can call CWinApp::DoWaitCursor
// with a 0 parameter value to restore the hourglass cursor.
void CMyObject::AnotherFunction()
{
CMyDialog dlg;
dlg.DoModal();
AfxGetApp()->DoWaitCursor(0); // same as CCmdTarget::RestoreWaitCursor
}
-
Re: Controlling CWaitCursor from different methods
As I told you, I read MSDN , and that does not help.
The examples given are always in the same scope.
-
Re: Controlling CWaitCursor from different methods
are you dealing with threads?
-
Re: Controlling CWaitCursor from different methods
If my assumption is correct that you want to show the hour glass until you get the message back, I would guess that BeginWait... in OnAction, and EndWait.. in OnMessagerecvd should do the trick provided of course that your program doesn't do something in the mean-time causing the wait cursor to go away, like opening a dialog. If it does you might need to introduce a helper-flag to be used to determin if the wait cursopr should be restored or not after such an action.
-
Re: Controlling CWaitCursor from different methods
I don't think it works...
I see the wait cursor very quickly, then nothing.
And my function that stops the CWaitcursor is never called. I don't receive the answer yet.
My guess is that the Wait Cursor is stopped when the program gets out of the scope where it has been created.
Marina
-
Re: Controlling CWaitCursor from different methods
Something struck me while attempting to test my own reply:
You don't have any OnSetCursor handlers anywhere that might override your hour glass when your method which sets it ends? I did...
I've tested the underlying problem; Start wait cursor in one method, and ending it in another. It works fine (at least within the same class, as my test was). But; If some other class commands another cursor, that one wins... I'm not sure, but maybe some of the MFC base class message handler sets cursor "under the hood", and that is what causes your stuff not to work?
This works fine (as long as I don't move the cursor outside CMainFrame):
Code:
void CMainFrame::OnNcRButtonDblClk(UINT nHitTest, CPoint point)
{
BeginWaitCursor(); // Display the hour-glass cursor
CFrameWnd::OnNcRButtonDblClk(nHitTest, point);
}
void CMainFrame::OnNcRButtonDown(UINT nHitTest, CPoint point)
{
EndWaitCursor();
CFrameWnd::OnNcRButtonDown(nHitTest, point);
}
-
Re: Controlling CWaitCursor from different methods
Well, yes, some other class do get the hand ... For example the communication class, that sends and receive the message, whereas I create my hourglass in the Dialog Class that request the messages and refreshed when the message eventually arrives.
Even in the communication class, between the message is sent and its answer arrives, other messages may arrive that will cause another class to do the treatment.
(That's normal coding isn't it?) ;)
Has nobody tried to make a CWaitCursor treatment like this?
Marina
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by Marina Vaillant
I don't know how to make it last longer than the scope where it has been created;
Simple: Don't create it locally (on the stack), but on the heap (with new), and keep a pointer to it as a class member. However, at that point you could just as well use SetCursor() instead. The trick about CWaitCursor is that you simply need to place it at the beginning of a block, and it's ctor and dtor will handle (even nested) SetCursor() calls for you. If you don't want that automatic functionality, just call SetCursor() (or BeginWaitCursor / EndWaitCursor) yourself.
-
Re: Controlling CWaitCursor from different methods
Have you tried to NOT move the mouse until your anwer is received back in your dialog class? My theory is that in this case, you should have the hour glass until the answer processing is done (and Endwaitcursor is called). If that works, you need to handle WM_SETCURSOR in all your OTHER classes that might change the cursor, based on your desires originating from your dialog class. A possibly daunting task...
-
Re: Controlling CWaitCursor from different methods
I have never dealt with the MFC class, nevertheless, using 'BeginWaitCurosr()' and 'EndWaitCursor()' usually works for me without any consideration of the stack or heap... ;)
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by gstercken
Simple: Don't create it locally (on the stack), but on the heap (with new), and keep a pointer to it as a class member. However, at that point you could just as well use SetCursor() instead. The trick about CWaitCursor is that you simply need to place it at the beginning of a block, and it's ctor and dtor will handle (even nested) SetCursor() calls for you. If you don't want that automatic functionality, just call SetCursor() (or BeginWaitCursor / EndWaitCursor) yourself.
Ok, I'll try this.
I allocate the CWaitCursor * pWaitCursor in my beginning function.
Then I make a SetCursor(WHAT?) ??
And then to stop it, a SetCursor(WHAT?), or a Delete(pWaitcursor) ??
Presently, with an allocated CWAitCursor, and a delete (not called!!!) I have the cursor a ver short amount of time.
I should not do that because if I never get a message as an answer, I'll never delete the pointer.
Marina
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by Andreas Masur
Don't you think Alin is right when s/he says that if another class or thread or whatever takes control of the Cursor, it gets back to normal even before my EndWaitCursor() is called ??
Marina
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by DP TWO
Have you tried to NOT move the mouse until your anwer is received back in your dialog class? My theory is that in this case, you should have the hour glass until the answer processing is done (and Endwaitcursor is called). If that works, you need to handle WM_SETCURSOR in all your OTHER classes that might change the cursor, based on your desires originating from your dialog class. A possibly daunting task...
I do not move it as much as I can, but for caliing the function I press a button, si I necessarily release the button, and maybe move it a bit...
Marina
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by Marina Vaillant
Ok, I'll try this.
I allocate the CWaitCursor * pWaitCursor in my beginning function.
Then I make a SetCursor(WHAT?) ??
And then to stop it, a SetCursor(WHAT?), or a Delete(pWaitcursor) ??
Yes, you would delete it - but as said above, that's not the intended use of CWaitCursor. Instead of creating it with new and deleting it with delete, you might just as well use BeginWaitCursor() / EndWaitCursor() (that's what CWaitCursor does anyway). The only reason for using CWaitCursor at all is the (very common) situation where you would call BeginCursor() when entering a block and EndCursor() when leaving it. The only benefit in using CWaitCursor at all is that you don't need to remember to call EndWaitCursor() yourself. In your case (with begin/end required in different functions) there's simply no value in using CWaitCursor - just call BeginWaitCursor() / EndWaitCursor(), and you're done.
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by Marina Vaillant
Don't you think Alin is right when s/he says that if another class or thread or whatever takes control of the Cursor, it gets back to normal even before my EndWaitCursor() is called ??
Well...no...the wait cursor will be displayed until you call 'EndWaitCursor()'...unless someone else is changing the cursor...
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by Marina Vaillant
Don't you think Alin is right when s/he says that if another class or thread or whatever takes control of the Cursor, it gets back to normal even before my EndWaitCursor() is called ??
Marina
I didn't really say that. I only wanted to say that setting/resetting of CWaitCursor should be done by your UI thread. This is what I was trying to get at.
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by Andreas Masur
Well...no...the wait cursor will be displayed until you call 'EndWaitCursor()'...unless someone else is changing the cursor...
If I'm not mistaken, ANY UI class can set ANY cursor, regardless of what some other class might want to do, if you let it. There is only one cursor, and it takes the shape you tell it to, and stays that way until you (or someone else) tell it to be different. If you want it back to your own shape, you have to do it manually.
I think there is some cursor manipulation going on somewhere that you don't know or think about. In principle, your code should work as you intended.
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by Marina Vaillant
I do not move it as much as I can, but for caliing the function I press a button, si I necessarily release the button, and maybe move it a bit...
Marina
This got me thinking somewhat more. The sample I gave earlier deals with CMainFrame. What if CDialog does something under the hood that we haven't thought about? So, after spending most of the day on this thread, although I should be doing something way different (don't tell my boss!), here is a very simple solution, that makes sure your intention works (at least as long as the mouse is inside the dialog where your button to fire your message is):
Simply handle WM_SETCURSOR in your dialog class and make sure that the default handler isn't called. Like this:
Code:
BOOL CYourDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default
//return CDialog::OnSetCursor(pWnd, nHitTest, message); // Don't invoke this.
return TRUE; // Do this instead
}
It seems like CDialog sets the cursor by default, thus you need to block its default action. Why it is like this I don't know. Maybe someone else can elaborate more on that?
Now code like this works:
Code:
void CYourDlg::OnBnClickedBtn1()
{
BeginWaitCursor(); // Or RestoreWaitCursor() for that matter
}
void CYourDlg::OnBnClickedBtn2()
{
EndWaitCursor();
}
Clicking one button shows hour glass, clicking another removes it. No need for any more complicated stuff than that. Of course, if you move the mouse to another dialog (which handles WM_SETCURSOR the default way), or some other GUI that specifies cursor, the cursor will change. I have tested this with 2 dialogs open, a view in between that doesn't change cursor, and moving from my dialog which handles WM_SETCURSOR to one that doesn't. Result: The cursor changes to arrow as I enter the dialog which doesn't override OnSetCursor...
-
Re: Controlling CWaitCursor from different methods
Thank you for spending so much time on this problem (hey, if it works you can write an article on this because it's missing!! ;)
-
Re: Controlling CWaitCursor from different methods
Thank you DP TWO, handling the WM_SETCURSOR seems to work.
I'll have to do it in both my dialogs since I'll need to be able to make an hourglass on both dialogs...
Thank you very much to the others who tried to help and made us move forward in finding the solution!!
I can't rate all of you, Codeguru asks me to spread some rating around before rating you again ...
Marina
-
Re: Controlling CWaitCursor from different methods
Except that now... i don't see any cursor when it's not an hourglass...
How do I set cursor manually to a normal cursor?
Marina
-
Re: Controlling CWaitCursor from different methods
That was strange... Doesn't EndWaitCursor restore the default arrow? Could be some issues with dialog-focus? I don't know, but maybe if you have focus on your "other" dialog (the one which doesn't start Wait cursor), the EndWaitCursor called from the one which doesn't have focus, might not restore the cursor because it is on another dialog? I'm guessing, but if you can test and see if there are difference whether or not focus is on the dialog that started the hour glass when it's supposed to end, and we could work more on it from there. I'm not surprised if there are any un-desired side-effects by handling WM_SETCURSOR in the way I suggested. I don't get easily surprised when dealing with MFC...
But, setting cursor manually is straight-forward. Example:
Code:
SetCursor(::LoadCursor(NULL,IDC_ARROW));
Challenge is to find the best place to do it to ensure desired effect. You want to make sure this is not done if the desired cursor is the hour glass, in your case possibly determined by a completely different GUI class (ref. your two dialogs).
-
Re: Controlling CWaitCursor from different methods
Thank you!!!
I was a bit mixed up for a while.
I think I'm ok now... until I find some other problem with it.
Explanation of my problem and solution.
When my dialog was initialising, some actions were already done, the same actions that requested the use of an hourglass when done manually.
So the dialog started with a cursor as an hourglass. And the EndWaitCursor() was only making the cursor disappear. So putting the SetCursor(::LoadCursor(NULL,IDC_ARROW)); right after the EndWaitCursor() restored it ok.
Maybe since the cursor was never an arrow on this dialog, it could not find any restoration shape... ??
It seems ok now. I still wonder why it started with an hourglass cursor since I put breakpoints on all my BeginWaitCursor() and it did not stop anywhere...
Thank you
Marina
-
Re: Controlling CWaitCursor from different methods
Except I really need to know why it starts with an hourglass. Even when there is not treatment to do at the beginning, it starts with an hourglass. But then, since there is no reception of confirmation message, it stays like this for ever.
-
Re: Controlling CWaitCursor from different methods
Well, according to MSDN documentation of EndWaitCursor() it restores the PREVIOUS cursor, not necessarily the arrow. Since we have already blocked the default handling of WM_SETCURSOR, it could probably happen that there is no previous cursor to find in your case. That could be an explanation as to why you have to do the manual cursor selection after EndWaitCursor().
The start up issue I don't understand if there are no calls to BeginWaitCursor(). How is your dialog invoked? Could it be that for instance the default handling of menu clicks displays a wait cursor for a while? Without us noticing it? Does your dialog take a long time to open?
If this theory holds, it explains why it will stay with hour glass; We don't let the dialog use its own cursor, because we handle WM_SETCURSOR the way we do? Again, I'm guessing/shooting from the hip. Don't really know, but I've learned during this thread that there are stuff going on with cursors that is not necessarily easily appearant.
-
Re: Controlling CWaitCursor from different methods
Well, ok, I solved this by setting the cursor to the arrow during the OnInitDialog, before any of the treatment may be done, then using BeginWaitCursor is some treatment is necessary.
Then it stops when it receives confirmation, having the default one to get back to. :)
And if no treatment is necessary, the cursor stays to what it is iset to during the OnInitDialog()!
Marina
-
Re: Controlling CWaitCursor from different methods
Well, Marina, you seem to be a very good engineer; If you can't solve a problem, work around it and then move on. A strategy according to my heart... It was an interesting and chellenging problem, though. And no, there won't be any article. I won't risk exposing my shortcomings to such a broad audience. I really don't consider myself an expert to the level where I can start publishing articles on this subject. My field is actually in control theory. This UI programming is something I do only to let the people who use my company's real products a nice view of, and means of interacting with, the really interesting stuff of being able to make big boats stay on a certain spot and move in a controlled manner to another spot. It's called Dynamic Positioning. Look it up on Google for instance. That's really interesting. :thumb:
-
Re: Controlling CWaitCursor from different methods
Another challenge then ...
I changed my CDialog to be a CPropertyPage now... and guess : the cursor does not change any more using BeginWaitCursor().
Any idea why?? How could I do that?
Marina
-
Re: Controlling CWaitCursor from different methods
Why did you that???? My boss will kill me if he knows what I'm spending my time on... Well, well, here we go with some thoughts. Haven't tested much, and again I'm no expert:
I think the problem is still related to some MFC-classes doing their own thing with cursors, only that now we deal with new classes. CPropertyPage is derived from CDialog, it might or might not have its own implementation of OnSetCursor(). Haven't checked.
But, and here I think the main issue in your case lies, if you now have your dialog implemented as CPropertPage, you probably also have involved the class CPropertySheet. This one is NOT derived from CDialog, and limited testing indicates that this class definetly has its own handling of Cursor (or maybe it uses the CWnd-implementation).
If you have your CPropertyPage dialog implemented in the "standard" way, i.e. with some tabs to bring up different CPropertPages, be aware that when you click the tab, you're actually interacting with the CPropertySheet class, not your CPropertyPage. Since the CPropertPage shows up "immediately", and long before you're able to move your mouse away from the CPropertySheet, any cursor settings done in your CPropertyPage::OnInitDialog() will be overridden by CPropertySheet::OnSetCursor() immediately...
BTW; Found that for instance the header in a CListBox also changes cursor to whatever it wants, arrow or cross-hair, dependant on where you place the pointer. It's tricky these things, it seems like. Lots of "under the hood action" performed by MFC by default you need to handle appropriately...
Sorry I don't have time to test out more, and maybe come up with a complete solution, maybe tomorrow, if you still have the issue... ;)
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by DP TWO
Simply handle WM_SETCURSOR in your dialog class and make sure that the default handler isn't called. Like this:
Code:
BOOL CYourDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default
//return CDialog::OnSetCursor(pWnd, nHitTest, message); // Don't invoke this.
return TRUE; // Do this instead
}
It seems like CDialog sets the cursor by default, thus you need to block its default action. Why it is like this I don't know. Maybe someone else can elaborate more on that?
WM_SETCURSOR if windows' way of asking the window beneath the mouse pointer to modify it to it's wishes.. Different window classes do it in their own way. A grid class may show a + kind of cursor and list view may show a hand cursor and things like that. Generally, a defautl window proc would handle this message to return the handle to the cursor passed during RegisterClass in the WNDCLASS structure's hCursor member.
Hence, calling CDialog::OnSetCursor does that bring back the arrow cursor.
Now, what the OP could do is:
Code:
CYourclass::OnSetCursor(..)
{
if(m_bIsProcessing)
SetCursor(m_hWaitCursor) ; // this could be LoadCursor(IDC_WAIT) in ctor
else
CYourBaseClass::OnSetCursor()
}
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by DP TWO
you might need to introduce a helper-flag to be used to determin if the wait cursopr should be restored or not after such an action.
I agree with Kirants that a helper flag to be used to determine which cursor to load in OnSetCursor would be doable and also preferrable maybe, and it was a suggestion earlier in this thread. At that time Marina needed to handle two dialogs and set the wait cursor in both, based on some action in one. That would have required messages to be sent from one to the other (unless she did some class deriving allowing them to know about each other).
Maybe now, with the change to PropertyPage, the helper flag would be an easier and better solution? I still think she needs to look to he CPropertySheet class as well (which I presume she's using, since she's dealing with Property Pages). If the action requiring wait cursor happens in CPropertyPage, CPropertySheet needs to know about as well to ensure desired cursor effect, I would imagine.
My main point is that when dealing with cursor shapes, we need to take care because some classes might override our desires without us knowing about it, causing unexpected cursors to show up. If you don't do anything, MFC has a default way of handling a lot of stuff.... This is, I believe, also what Kirantis is pointing out.
-
Re: Controlling CWaitCursor from different methods
Quote:
Originally Posted by DP TWO
If you don't do anything, MFC has a default way of handling a lot of stuff.... This is, I believe, also what Kirantis is pointing out.
Close.. MFC doesn't have that much a role here. It is more Windows doing that..
-
Re: Controlling CWaitCursor from different methods
Of course, you're right... I've been lured away from the good old days of using Windows APIs, to using MFC, and now have a tendency to give MFC more credit/blame it more than what it actually deserves...
-
Re: Controlling CWaitCursor from different methods
Marina, my advice: Do not use CWaitCursor. It uses BeginWaitCursor and EndWaitCursor.
See this thread and this thread for more.
-
Re: Controlling CWaitCursor from different methods
Thank you John, and thank you all.
JohnCz , if I use BeginWaitCursor() this brings back to the first problem, which is that I 'm not calling the begin and the end from the same function.
Thanks for the reminder about the handling of OnSetCursor().