I have a problem while doing this in the code-behind file of a winform :
// Waiting Cursor + disabling form
Cursor = Cursors.WaitCursor;
this.Enabled = false;
// Synchronous method
SomeWork();
// Re-enabling form
Cursor = Cursors.Default;
this.Enabled = true;
Current Behaviour
Clicking on a button for example during Somework() will execute the method associated to the button after re-enabling the form.
Expected Behaviour
I don't expect from the form to store the clicking events of the user while the form is disabled.
Question
Is there a way to empty the Clicking cache of the form (So that I'd do it before re-enabling the form) ?
IMPORTANT EDIT
A possible easy solution would be implementing the IMessageFilter interface in the code behind of the form. Disabling the left seems easy using this PreFilterMessage :
public bool PreFilterMessage(ref Message m)
{
// Blocks all the messages relating to the left mouse button.
return (m.Msg >= 513 && m.Msg <= 515) ;
}
But once again, disabling and re-enabling the mouse's left clicks DOES NOT EMPTY THE MOUSE BUFFER ...
The problem is that the process is running in the same thread, so the form doesn't actually get disabled before the process starts running. The easy thing to do would be use Application.DoEvents() to force it to set everything to disabled before starting the process, but the more professional (and probably safer) method is to run the time-consuming process in another thread.
NOTE: After running into another hitch in my own programming I found that you may have to run Application.DoEvents() before enabling everything again--it will fire any clicks the user made on the disabled controls, instead of waiting for the process to complete--enabling the controls--and THEN firing the click.
Obviously DoEvents is messy and I should be using threads.
Related
I want to show a dialog as modal( i.e. blocks all interaction with all the other ui in my application) But I don't want that call to block execution, how do I do that?
This isn't pretty, but for reasons out of my control, we just have one thread, and can't create more.
currently when we do a time consuming progress, we want to show a progress bar, but in order for it to update while the process is progressing, we have to call DoEvents() (eek).
This unsurprisingly causes a few issues sometimes, but if we could easily disable all ui except the progress bar, that might help.
I got this idea from reading this answer here:
https://stackoverflow.com/a/5183623/259846
Edit:
A modal dialog is simply one that disables all other UI - only the modal dialog can be interacted with. This is separate from whether or not the operation to show this dialog is blocking or not. I don't see any reason why you couldn't in theory have a function that shows a modal dialog box without blocking until the box is closed.
I want to show a progress bar, and I want this progress bar to update as a process progresses, I doesn't matter how I go about doing this, the fact is, that if you want to update one dialog, you have to update the whole UI. As such I want the rest of the UI except for the progress window to be disabled so it can't be interacted with while my process is in progress.
And yes, I can only have one thread, no parallelism.
Disable all other elements manually when opening the DialogBox. Then enable them when closing the box.
for example:
Button.Enabled = false;
Check this out for disabling all controls
How to disable all controls on the form except for a button?
You can also put up a transparent rectangle in front of everything that you can't click through. Then hide it when dialogbox closes.
I've thought of one way of doing it.
My question probably doesn't clearly indicate my use case, this solution still uses the blocking call to create the modal dialog, but still allows me to execute my 'do work, update progress' loop with the dialog open.
var progress = new Form();
this.BeginInvoke(new Action(() =>
{
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(16); // do a bit of my process
// Update progress dialog
Application.DoEvents();
}
progress.Close();
}));
progress.ShowDialog();
I need to disable all execution of any event (click) for a period of time after l click a button. At that particular period, no matter how many buttons I click the functions of buttons will not be executed. I cannot use the Thread.Sleep() because it only delays the execution, the function of the button still being executed. Does any1 can help? My code is here:
int delay = 2000; //unusable as it is just delaying the execution of clicked button.
public void PressCOM(string key)
{
if (key == "PressSc")
{
PressAndRelease(key);
PressAndRelease("C");
Thread.Sleep(delay);
}
Sleeping on the main thread is a bad idea anyway.
You will have to built in some logic concerning the buttons to either:
disable them
ignore clicks
Use a Boolean property that is reset by a timer.
Disabling and showing a "Please Wait..." kind of message to the user will be a better option...
You can Disable and enable the panels .Grids in WPF as you like
I Usually use a Adorner Overlay (translucent) with a message to stop user from interacting with the UI... usually Backround worker helps to Show and Hide the overlay when required.
This will be a Good Artice for Adorner implemtation in XAml
hope it helps.. :)
It's not really clear what you want to do.
It's not a really good idea to put a sleep in the main application thread. It will make your application unresponsive to all Windows messages (both originated by user or by Windows itself). Your users will fill it hanged.
If you need to disable only that button you may set its IsEnabled property to false, user won't be able to click it.
If you want to disable all events you can use the IsEnabled property of the window itself.
If to disable all controls (or just one button) isn't the right solution for you then you may use one of the followings.
Add a state variable (or a simple flag). In all your event handlers you'll check if that variable is true or not (you'll change it with a timer). Something like:
if (!_canHandleMouseClicks)
return false;
Disable all buttons and re-enable them after a period of time:
private static IEnumerable<Button> FindAllButtons(DependencyObject obj)
{
if (obj == null)
return null;
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childrenCount; ++i)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is Button)
yield return (Button)child;
// If you may have buttons inside buttons then uncomment these lines
//foreach (Button childOfChild in FindAllButtons(child))
// yield return childOfChild;
}
}
Then you can write:
foreach (Button button in FindAllButtons(window))
button.IsEnabled = false; // true to re-enable
A possible solution might be to show a dialog and hide the dialogs ok, cancel and close buttons. This will prevent any button from being clicked in the main-window.
Our application uses an animation to fade out a message window when it's closed. To do this, we override OnClosing, and if we haven't completed our animation, we cancel the close and start the animation. When the animation completes, we close the window. This seems to be a fairly common pattern based on what I've seen online, since Closing and Closed aren't routable events.
The problem is, we occasionally see a window get "stuck" - the animation doesn't seem to occur, so the "finished" flag never gets set and the window just sits there, canceling any close events. Sometimes, the animation eventually kicks off and the window closes, but other times it seems like its permanently stuck (though obviously you can't tell for sure...halting problem and all that...).
Does anyone have any thoughts as to why the Storyboard won't get kicked off even after calling Begin()? Without having been able to dig in really deep into the Storyboard code/data structures, it feels like it's waiting for something to happen before it actually kicks off, that never happens.
Here are some of the possibilities that I've ruled out so far:
incorrect threading (all windows, animations, storyboard, etc. are created/handled/accessed on the GUI thread)
GUI thread blocked (clicking the X still fires the Closing event, and the GUI thread responds to events in other windows, system tray, etc.)
GUI thread busy (we're only doing UI work on the GUI thread, and we generally only have one window open and at most one "close" animation running at a time)
That said, here are some things that might be contributing:
Windows can close each other (in some cases if one is already open, the new one will close the existing one)
We also have a "fade in" animation bound to the Loaded event via XAML, but that seems to complete correctly.
We recently removed our "main" UI window, so the application's main window is now a dummy hidden window.
I suspect that last one most strongly because it was added most recently, and this behavior was never reported/noticed before that change (though I can't say definitively it never happened before that change). But other GUI/window-related events still all work, and the fade-out animation logic works most of the time.
Finally, here's the meat of our code for doing the closing animation:
public class MyWindow : Window {
private bool _storyBoardCompleted;
private Storyboard _closingStoryBoard;
protected Storyboard GetClosingStoryBoard()
{
Grid mainGrid = (Grid)FindName("MainGrid");
DoubleAnimation closingAnimation = new DoubleAnimation();
closingAnimation.From = 1;
closingAnimation.To = 0;
closingAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
Storyboard.SetTarget(closingAnimation, mainGrid);
Storyboard.SetTargetProperty(closingAnimation, new PropertyPath(OpacityProperty));
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(closingAnimation);
storyboard.Completed += StoryboardCompleted;
return storyboard;
}
private void StoryboardCompleted(object sender, EventArgs e)
{
_storyBoardCompleted = true;
Close();
}
protected override void OnClosing(CancelEventArgs e)
{
if (_closingStoryBoard == null)
{
_closingStoryBoard = GetClosingStoryBoard();
_closingStoryBoard.Begin();
}
if (!_storyBoardCompleted)
{
e.Cancel = true;
}
}
}
EDIT:
The issue seems to pop up whenever we open two of these windows very close together in time, and close the first before showing the second. Essentially:
window1.Show()
window1.Close()
window2.Show()
At this point, if we try to close window2, it will cancel the close as in the code above, but the animation won't begin (hence the problem). If we later on call window2.Close() followed closely by window3.Show() (i.e. show a third window), then window2's animation finishes and the window closes - but then window3 is stuck in the same state. So the behavior almost seems to get "passed on" from window to window once we are in this state.
EDIT:
I've ruled out another possibility - I was wondering whether the "fade-in" storyboard of the second window was somehow conflicting with the "fade-out" storyboard of the first, and somehow affecting the second window's fade-out. But the problem occurs even after removing the fade-in storyboard.
Furthermore, the problem goes away entirely if I replace the fade-out storyboard with just a half-second dispatch timer (i.e. just a delayed close, no animation). Just a sanity check that it definitely has something to do with the animation...
Did you ever try adding parameters to the Begin method:
_closingStoryBoard.Begin(this);
I never could quite pin down specifically why this was happening, but it seemed to be related to the fact that I was showing the second window while the first one was still animating. I still don't understand why this caused problems, but I was able to work around it by waiting for the first one to close completely before showing the second (basically hooking onto the first window's Closed event).
Would still love to know why this behaves this way, in case the workaround of "wait for the first window to close" ever stops being sufficient...
I have a form that appears as shown in the attached image. I have two parts of the form that concern this question: the TabControl, and the Panel, as shown in the image. It should be noted that the panel is NOT within the TabControl.
My situation is that I have a thread that executes continuously when the button, displayed in melt-your-eyes green in the Panel, is clicked. The thread polls the device which I'm interfacing with and updates the controls in the "Status" GroupBox at the bottom of the TabControl. When the user clicks on a control in the TabControl (tabControl_Enter event), I trigger a ManualResetEvent which lets the thread finish its iteration so that I can perform the IO required by the clicked control. The code to to suspend the thread is as follows:
private void StopSynchThread()
{
synchWaitHandle.Reset();
//various UI changes
}
private void updateSynchStat()
{
while (true)
{
synchWaitHandle.WaitOne();
try
{
updateSynch();
}
}
What I would like to do is then restart the thread automatically, instead of by button press, as is currently done. What I'm trying to do is avoid having to restart the thread by conditionally calling StartSynchThread() within each of the "bazillion" UI event handlers. StartSynchThread() is defined as:
private void StartSynchThread()
{
synchWaitHandle.Set();
}
Is there a precedent or decent paradigm for handling this? Without any concept of how to do so, I was thinking that I could alter my function that performs the IO with the device to generate an event after it gets a response from the device, but that seems inelegant.
Any ideas? I appreciate your insights. Thanks.
If you really can fire it off with a simple button click, you ought to be able to just put a timer on the form that will periodically check for the right conditions and then "push" the button (call synchWaitHandle.Set();) automatically.
On my main form, there is another (floatable) window. This floatable window works sort of like a popupwindow in that it will close when the user clicks somewhere else outside of this window. This is handled by the Deactivate event. But what I want to do is, if the user clicks on a different control (say a button), I want to both close this float window and then activate that button with just one click. Currently, the user has to click twice (one to deactivate the window and once more to activate the desired button). Is there a way to do this with just one click?
foreach(Control c in parentForm.Controls)
{
c.Click += delegate(object sender, EventArgs e)
{
if(floatyWindow != null && floatyWindow.IsFloating)
{
floatyWindow.Close();
}
};
}
And then add your handlers as normal. This additional handler can close the floaty window.
Make sure you floaty window isn't a dialog too as this will not allow your parent form's controls to be clicked.
I had a slightly hacky solution. In your Deactivate event, fire another custom event to your main form. Then when you main form is handling the custom event, enumerate through your control(this.Controls) and locate the control under the mouse by checking all their bound then call Focus(). You might need to sort by the one with the smallest surface area, or you can have a separate list of "focus-able" control like button just for this purpose.
Another way might be to switch focus to your main form immediately after OnMouseLeave of the floatable window, or OnMouseHover of your main window, but keep the floatable windows on top, just no focus. Handle the global mouse down of your main form, and close the floatable window by then.
These are just theories, not tested.
I had an issue like this once too, when a customer wanted "floaty" windows all over there application. I used used an approach similar to the one described in this article:
http://www.vbaccelerator.com/home/NET/Code/Controls/Popup_Windows/Popup_Windows/article.asp
Code sample available here:
http://www.vbaccelerator.com/home/NET/Code/Controls/Popup_Windows/Popup_Windows/Popup_Form_Demonstration.asp
By extending this a bit we created "floaty" windows similar to the ones VS uses when you get a runtime error while debugging code.
At the very least reading the code may give you some insight, however, quarrelsome's response may be the more simple solution.