button queues its click events instead of disabling it - c#

I change my button's "Enabled" property to "false" but my button still catches it's click event. I put Thread.Sleep() method to imitate some process. While my button is greyed out, i click on it, and after current process is done it begins work again (because i clicked on it while it was greyed out)
Here's my code:
int i = 0;
public Form1()
{
InitializeComponent();
label1.Text = "0";
}
private void button1_Click(object sender, EventArgs e)
{
//first click
button1.Enabled = false;
i++;
Thread.Sleep(3000); //if i click twice more while button is greyed-out the app will be non-responsive for 9 second and then prints "3" to my label
label1.Text = i.ToString();
button1.Enabled = true;
}
How can i disable my button completely (not allowing it's events to rise, but visible)?

You are freezing the UI thread which prevent anything from happening UI-wise. You should considering using the TPL to do such work.
private async void button1_Click(object sender, EventArgs e)
{
//first click
button1.Enabled = false;
i++;
await Task.Delay(TimeSpan.FromSeconds(3));
label1.Text = i.ToString();
button1.Enabled = true;
}

The Thread.Sleep(3000) call is blocking the function so the button doesn't get disabled. A quick and dirty fix to this is to call Application.DoEvents(); directly after button1.Enabled = false;. This forces the application to process any waiting events and should ensure that the button is disabled.
If you plan to replace Thread.Sleep(3000) with a long running process then you should use a BackgroundWorker. You'll find it under Components in the designer Toolbox.

Related

Button disabling while method execution (PreviewLeftMouseDown/Up must be workable)

I need a sequence as follows:
button default state
button Down, "methods for button Down" execute and Button itself is disabled for a time.
button is enabled since "Disabling" time is elapsed, button Up, "methods for button Up" execute.
button default state
I've tried this code and it acts at its first part properly. But second part (Up) does not execute.
Could anyone help me?
private void btn1_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var btn = (Button)sender;
btn.IsEnabled = false; //Disable button.
var fooTimer = new System.Timers.Timer(5000); //Exceute after 5000 milliseconds
fooTimer.Elapsed += (fooTimer_s, fooTimer_e) =>
{
//It has to be dispatched because of thread crossing if you are using WPF.
Dispatcher.Invoke(() =>
{
btn.IsEnabled = true; //Bring button back to life by enabling it.
fooTimer.Dispose();
});
};
fooTimer.Start();
// methods for button Down go here
}
private void btn1_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// methods for button Up go here
}
thanks in advance!
You could do this more easily in an async Click handler:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
button.IsEnabled = false;
await Task.Run(() =>
{
// perform time-consuming action
Thread.Sleep(5000); // just for test
});
button.IsEnabled = true;
}

WPF WebBrowser disable clicking & typing

Is there a way to prevent users in clicking and typing in WPF WebBrowser? It appears to me that it is only possible to do this in WinForms.
Things that I've tried:
browser.IsEnabled = false; - didn't work, can still click (navigate) and type in text
browser.Focusable = false; - same
having overlay button, which would consume clicks and focus - WebBrowser is a special element, that is always on top of other elements
having another WebBrowser on top of the main one with blank page loaded and opacity set to 0% as an alternative to overlay button (3.) - WPF WebBrowsers do not properly handle opacity, didn't work
browser_MouseDown event with e.Handled = true; - the event is for some reason not called on mouse down
Is there something that I've missed or did wrong in my attempts?
Three events and Boolean did it for me.
bool BrowserIsLoaded = false;
private void Browser_LoadCompleted(object sender, NavigationEventArgs e)
{
BrowserIsLoaded = true;
}
private void Browser_Navigating(object sender, NavigatingCancelEventArgs e)
{
if(BrowserIsLoaded)
e.Cancel = true;
}
private void Browser_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (BrowserIsLoaded)
e.Handled = true;
}
When the browser has finished loading it triggers the LoadCompleted event. Set a Boolean then check that when trying to navigate to a new page when they try to type in a box.
If you don't want to use your own Boolean (I used it for other things so it made sense to me) you can just ask the browser if it's loaded when ever you need it:
private void Browser_Navigating(object sender, NavigatingCancelEventArgs e)
{
WebBrowser wb = (WebBrowser)sender;
if(wb.IsLoaded)
{
e.Cancel = true;
}
}

Animated GIF in Windows Form while executing long process

I have developed a simple windows Application(MDI) in C# which exports the data from SQL to Excel.
I am using ClosedXML to achieve this successfully.
When the process is executed, I want to show a picturebox containing a animated GIF image.
I am a beginner and don't know how to achieve this, the picturebox appears after the process is completed.
I saw lot of posts which says to use backgroundworker or threading which I have never used and finding it hard to implement.
Can I have an step by step example with explanation.
The two functions which I have created which I call before and after I execute the code.
private void Loading_On()
{
Cursor.Current = Cursors.WaitCursor;
pictureBox2.Visible = true;
groupBox1.Enabled = false;
groupBox5.Enabled = false;
groupBox6.Enabled = false;
Cursor.Current = Cursors.Arrow;
}
private void Loading_Off()
{
Cursor.Current = Cursors.Arrow;
pictureBox2.Visible = false;
groupBox1.Enabled = true;
groupBox5.Enabled = true;
groupBox6.Enabled = true;
Cursor.Current = Cursors.WaitCursor;
}
It is not that hard to add a BackgroundWorker
Open your form in the designer
Open the Toolbox (ctrl+alt+X)
Open the category Components
Drag the Backgroundworker on your From
You will end-up with something like this:
You can now switch to the event view on the Properties tab and add the events for DoWork and RunWorkerCompleted
The following code goes in these events, notice how DoWork use the DowWorkEventArgs Argument property to retrieve the value that is supplied in RunWorkerAsync.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// start doing what ever needs to be done
// get the argument from the EventArgs
string comboboxValue = (string) e.Argument; // if Argument isn't string, this breaks
// remember that this is NOT on the UI thread
// do a lot of work here that takes forever
System.Threading.Thread.Sleep(10000);
// afer this the completed event is fired
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// this runs on the UI thread
Loading_Off();
}
Now you only need to start the background job, for example from a button click event to call RunWorkerAsync
private void button1_Click(object sender, EventArgs e)
{
Loading_On();
backgroundWorker1.RunWorkerAsync(comboBox1.SelectedItem); // pass a string here
}
Done! You have successfully added a backgroundworker to your form.
The best way to achieve that is running the animation in a async task, but accordingly some limitations is it possible to do that on windows forms using a Thread Sleep.
eg: In your constructor,
public partial class MainMenu : Form
{
private SplashScreen splash = new SplashScreen();
public MainMenu ()
{
InitializeComponent();
Task.Factory.StartNew(() => {
splash.ShowDialog();
});
Thread.Sleep(2000);
}
It is very important to put the Thread Sleep after have started a new one, don't forget that every action you did on this thread you need ot invoke, for example
void CloseSplash(EventArgs e)
{
Invoke(new MethodInvoker(() =>
{
splash.Close();
}));
}
Now your gif should work!

Updating the UI right before running the BackgroundWorker

I have this really little problem, but which can't be easily solved. Currently, my program has 2 buttons, a "Start" and a "Cancel". When the user clicks the start, the buttons should go instantly:
StartButton.IsEnabled = false;
CancelButton.IsEnabled = true;
But this occurs only when the BackgroundWorker has finished (all the code which will be ran after pressing the button), because the UI is always updated as last. There's no way I could add these commands to the "ProgressChanged" or "Completed" event of the backgroundworker. These events can take up to 10min to complete.
One easy way is to add these commands to the "ProgressChanged" part, and in the end "Complete" change their state again. But I'd like to avoid this, as the buttons should be showing their real state all the time, not after few "ProgressChanged" events. Of course there's always ways around, like not using the button's UI properties.
Is there any short solution for this?
It doesn't work to add the Button.Property changes to the ClickEvent. That's the main problem in this. I can easily use the "Completed" part of BGW to change the Button's back to match the starting state. The problem is to get them set right before all the events and BGW.
if you have a start button like:
this.StartButton = new System.Windows.Forms.Button();
then you can do
this.StartButton.Click += new System.EventHandler(this.button1_Click);
and then do
private void button1_Click(object sender, EventArgs e)
{
StartButton.IsEnabled = false;
CancelButton.IsEnabled = true;
Thread bg = new Thread(new ThreadStart( UpdateDatabase()));
bg.Start();
}
if you want the bg thread to send messages to the UI use the Invoke method like here
public delegate void UpdateUIHndler();
public void UpdateUI()
{
}
and do
if (InvokeRequired)
{
Invoke(new UpdateUIHndler(UpdateUI));
}
Take a look at a previous question of mine (quite similer). I should go for option 1.
ASP.NET Application log while code is running / progress bar
UI will only be delayed 5 seconds. Instead of text update the button styling using AJAX.
you can disable the start button in the click event of that button itself and enable it again it on RunWorkerCompleted event of BGW as shown below
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
private void StartButton_Click(object sender, RoutedEventArgs e)
{
startButton.IsEnabled = false;
}
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
stratButton.IsEnabled = true;
}
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
//Your processing code
}

.NET Timer Control

I have a windows app which is just a form with a timer control on. I've managed to track this down to the following situation:
private void timer1_Tick(object sender, EventArgs e)
{
MessageBox.Show("Test");
timer1.Enabled = false;
}
Will print Test again and again until I stop the program. However:
private void timer1_Tick(object sender, EventArgs e)
{
//MessageBox.Show("Test");
textBox1.Text += "t";
timer1.Enabled = false;
}
Just adds a single "t" to the textbox.
Can anyone tell me why MessageBox.Show is causing the function to return before the timer is disabled?
The call to MessageBox.Show blocks execution of timer1_Tick until you close the messsagebox, so the call to set timer1.Enabled = false; doesn't occur until after that. Because of this, the timer is still running and thus timer_Tick` still keeps getting called, every time the timer fires, until you hit OK on one of the message boxes.
What you need, if you want displaying the messagebox to stop the timer from firing again, is:
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
MessageBox.Show("Test");
}
You disable the timer after the user clicked the messagebox away.
MessageBox.Show shows a modal dialog. It will return (to the caller method) after the user responded to the messagebox. If you disable the timer first, the event will not be triggered again, and the user will have enough time to react.
Try this:
timer1.Enabled = false;
MessageBox.Show("Test");
Are you clicking OK on test, each timer click? If the message boxes keep stacking up one on top of the other, it's because MessageBox.Show doesn't return until you close the messagebox. In the meantime a message pump will continue to run, and process your timer messages.

Categories