.NET Forum / .NET Framework / New Users / July 2007
MonthCalendar, when changing month + showing an msgbox, it loops..
|
|
Thread rating:  |
Vigylant - 02 Jul 2007 18:46 GMT Well, make a new windows-application... Put in a MonthCalendar, and put in this:
Public Class Form1
Private Sub MonthCalendar1_DateChanged(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DateRangeEventArgs) Handles MonthCalendar1.DateChanged
MsgBox("problem")
End Sub
End Class
Then start the program... See my problem? Any way to fix this? :)
Peter Duniho - 02 Jul 2007 23:54 GMT > Private Sub MonthCalendar1_DateChanged(ByVal sender As System.Object, > ByVal e As System.Windows.Forms.DateRangeEventArgs) Handles [quoted text clipped - 6 lines] > Then start the program... See my problem? > Any way to fix this? :) I'm going to take a wild guess and make the assumption that calling MsgBox() causes the DateChanged event to be raised again, which causes you to call MsgBox(), etc.
For future reference, you should skip the theatrics and actually _describe_ the problem you're having in your post. You have no way of knowing for sure that someone else will see the same behavior that you are, and so you could get an answer to a completely different question from the one you thought you were asking.
All that said, I see at least a couple of options:
* Don't call MsgBox() from within the event handler. Use BeginInvoke() or similar mechanism to cause the message box to be displayed outside of the DateChanged event handling call stack.
* Set a flag before you call MsgBox() the first time. Don't call MsgBox() if the flag is set.
Also, not being a VB user, I don't know what MsgBox() maps to, but on the outside chance that it's _not_ exactly the same as using the Forms.MessageBox class, you might try that instead. It's not really clear to me why displaying a message box would cause the DateChanged event to be raised, and maybe the Forms.MessageBox class avoids that behavior somehow.
Pete
Vigylant - 03 Jul 2007 00:18 GMT Well, the thing that happens is that:
When the mouse is still clicked, the messagebox shows... Then, more and more messageboxes show up, because apparently, the mouse hasent released the "scrolling-button" (which changes the month, which like you said, raises the event again and again)
I got a fix, and that was to execute the msgbox in a backgroundworker :)
Ill try your way with begininvoke, though, ive never used it before, but ill give it a go :)
Thanks for the help..
> > Private Sub MonthCalendar1_DateChanged(ByVal sender As System.Object, > > ByVal e As System.Windows.Forms.DateRangeEventArgs) Handles [quoted text clipped - 33 lines] > > Pete Peter Duniho - 03 Jul 2007 00:45 GMT > Well, the thing that happens is that: > [quoted text clipped - 4 lines] > the > event again and again) For the record, you never mentioned that part of the problem in your original post. You just said to "start the program".
I didn't bother trying to run your code because, well...to do so seemed pointless. But you certainly left out some important detail there.
> I got a fix, and that was to execute the msgbox in a backgroundworker :) How does that fix the behavior? If the user continues to drag and change the month, I would expect that you would simply keep executing new message boxes in a background worker.
> Ill try your way with begininvoke, though, ive never used it before, but > ill > give it a go :) Given your newest information, I'm not sure BeginInvoke() would help, just as I'm not clear on why running the message box in a background worker helps. From your description, the user really is doing something that causes the event to be raised, and every time the event is raised, you display a message box.
Until you stop doing that, it doesn't seem to me that it will matter how you display the message box. It's not the act of displaying the message box that causes the event to be raised again, so changing how you display the message box isn't going to fix the problem.
You need to set a flag while the message box is displayed, and not show it again until the message box has been dismissed.
Personally, I think that wouldn't really be all that great a solution either. IMHO, the _correct_ user interface is to not show the dialog until the user has released the mouse button. For that, you will need to set a flag in the DateChanged event that is then checked later in the MouseUp event handler, and only then display the message box.
It's pretty bad UI to be popping up dialog boxes when the mouse button is still down.
Pete
Vigylant - 03 Jul 2007 01:00 GMT Well, i really dont know how it works when the messagebox is in a backgroundworker, but it does work.. It does solve the problem...
Only weird thing, the event is raised twice for some intriguing reason :P Anyway, i just put in a check on the worker status, and it exits the sub if its not finished, so the problem is solved...
Although, explain some more about the mouseup event, it sounds less messy ^^
> > Well, the thing that happens is that: > > [quoted text clipped - 45 lines] > > Pete Peter Duniho - 03 Jul 2007 02:36 GMT > [...] > Although, explain some more about the mouseup event, it sounds less > messy ^^ Well, it turns out I don't have a great suggestion there after all.
I did a little checking on the MonthCalendar control, and found it to have a couple of problems. One is that you don't reliably get a DateChanged event when clicking and dragging within the control. It seems that generally, you have to drag within the same week to get the event (and to change the date...to be clear, it seems to be that the date itself isn't changing...when the date does change, the event does seem to be raised).
This may be explaining why you do not always get a new message box instance when dragging. Not all dragging causes the date to change.
It also has some pretty serious redraw issues when dragging. Yuck!
The more problematic issue is that the very first time that clicking and dragging occurs within the control, the DateChanged event is raised before the MouseDown event. Oops. Via the standard event-handling mechanism, you don't find out early enough about the mouse interaction to avoid doing anything in the event handler itself. So your only options are to bypass the standard event handling mechanism, or to defer _everything_. I prefer the latter (I think it's a little cleaner, and I try to avoid using the WndProc override whenever possible), but here's an example of both, just in case:
To bypass the event handling mechanism, you have to subclass the MonthCalendar control, so that you have direct access to the WndProc. I suppose if you wanted, you could expose new events driven by what's going on in the WndProc, but given that you have to subclass anyway, I figure it's simplest to just put everything in the subclassed control.
(sorry, C#...if I can try to read your VB code, you can try to read my C# code :) )...
public partial class MyMonthCalendar1 : MonthCalendar { public MyMonthCalendar1() { InitializeComponent(); }
private bool _fDateChanged; private bool _fMouseDown;
protected override void OnDateChanged(DateRangeEventArgs drevent) { base.OnDateChanged(drevent); if (_fMouseDown) { // Only defer the display of the message box if the mouse is captured // (Capture is true when the control has "captured" the mouse, in response // to the user clicking in the control) _fDateChanged = true; } else { // Otherwise, just go ahead and show the message box (if, for example, // the value changed programmatically) _ShowMessage(); } }
void _ShowMessage() { // do your actual message box here MessageBox.Show("Here's a message"); }
const int WM_LBUTTONDOWN = 0x0201; const int WM_LBUTTONUP = 0x0202;
protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_LBUTTONDOWN: _fMouseDown = true; break; case WM_LBUTTONUP: if (_fDateChanged) { // Still bad form to put modal behavior in event handlers, so use // BeginInvoke to make sure that the _ShowMessage() method isn't // actually run until back at the main event loop BeginInvoke(new MethodInvoker(_ShowMessage)); _fDateChanged = false; } _fMouseDown = false; break; } base.WndProc(ref m); } }
Here's another alternative, which involved deferring any work until after all of the current event processing is done (essentially, queue up a request to show the dialog in the form's message queue, so that by the time that request is actually executed, you've had time to observe the mouse-down event). Note that the code may call BeginInvoke() multiple times while clicking and dragging the mouse, but none of those should result in the message-box being shown; only the one that's invoked from the mouse-up handler will do that, since by the time it gets processed, the _fMouseDown flag will have been reset:
public partial class Form1 : Form { private bool _fDateChanged; private bool _fMouseDown;
public Form1() { InitializeComponent(); }
private void myMonthCalendar11_MouseDown(object sender, MouseEventArgs e) { _fMouseDown = true; }
private void myMonthCalendar11_MouseUp(object sender, MouseEventArgs e) { if (_fDateChanged) { BeginInvoke(new MethodInvoker(_ShowMessage)); _fDateChanged = false; } _fMouseDown = false; }
private void _ShowMessage() { if (!_fMouseDown) { MessageBox.Show("A message"); _fDateChanged = false; } }
private void myMonthCalendar11_DateChanged(object sender, DateRangeEventArgs e) { _fDateChanged = true;
// A slight optimization here would be to only call BeginInvoke() if // !_fMouseDown; I suspect any performance advantage would be minimal though BeginInvoke(new MethodInvoker(_ShowMessage)); } }
Hope that helps.
Pete
Vigylant - 03 Jul 2007 12:14 GMT Well, it would probably work, but ive decided to change that part of my program, so that i wont need to display the messagebox...
Thanks for all the help anyway :)
> > [...] > > Although, explain some more about the mouseup event, it sounds less [quoted text clipped - 160 lines] > > Pete
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|