can someone please let me know why the System.Windows.Forms.Timer continues to show multiple message boxes? I thought that it is on GUI thread ... and therefore after the first messagebox the GUI thread should block. But this is not the case
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int nValue = 0;
void tmr_Tick(object sender, EventArgs e)
{
nValue++;
MessageBox.Show(nValue.ToString());
}
System.Windows.Forms.Timer tmr = new System.Windows.Forms.Timer();
private void btnStartTimer_Click(object sender, EventArgs e)
{
tmr.Interval = 500;
tmr.Enabled = true;
tmr.Tick += new EventHandler(tmr_Tick);
}
}
The MessageBox.Show() method includes (as all modal dialogs do) a message loop that continues to pump window messages.
Window messages are what allow a window to interact with the user (update itself, accept input, etc.), as well as what allows the Forms.Timer class to work.
If you want your Forms.Timer to stop ticking when the dialog is shown, you need to set the timer's Enabled property to false before you show the dialog.
In your Tick event stop the timer and then start again after MessageBox.Show like:
void tmr_Tick(object sender, EventArgs e)
{
tmr.Enabled = false;
nValue++;
MessageBox.Show(nValue.ToString());
tmr.Enabled = true;
}
The reason you are getting repeated MessgeBoxes is because your timer is continuing after showing the first MessageBox.
A message box does not block the GUI-Thread. It's as simple as that. You can interact with the message box, after all :)
Also: The internal workings of the timer are not clear, but I would guess that it runs on another thread and just returns on the GUI-Thread.
Related
I need to make a splash screen that is visible for 5000 milliseconds before closing. I pass in 5000 to my SplashScreen form constructor and set timer1.Interval = time. I can't seem to find a straightforward answer online and I don't have much experience with timers. I assume that I need to show the splash screen, start the timer, check for when timer1.Tick occurs, and close the form but I don't know the syntax on how to do that.
private void Form1_Load(object sender, EventArgs e)
{
SplashScreen splash = new SplashScreen(5000, appLogo, "Text Editor", "Copyright (c) 2020", "John Doe");
splash.timer1.Enabled = true;
splash.ShowDialog();
splash.timer1.Start();
// Wait for Tick event to occur.....
splash.Close();
}
In your SplashScreen form, you need to define the Tick event:
timer1.Tick += new EventHandler(CloseForm);
That calls a method to close the form:
private void CloseForm(Object source, EventArgs eventArgs)
{
this.Close();
}
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
}
I thought the Load event might help, but the following code just shows “Done” immediately.
public Form1()
{
InitializeComponent();
Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(3000);
Text = "Done";
}
How do I make it Sleep after the form is shown?
Thanks.
There is a Shown event for a windows form. Check out: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.shown.aspx
Or if you are lazy, here you go:
public Form1()
{
InitializeComponent();
Shown += Form1_Shown;
}
private void Form1_Shown(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(3000);
Text = "Done";
}
Enjoy.
I would suggest do not block a form, process something and after show in its title "Done", cause that what you want to do, I presume. This gives to the user blocked UI feeling, which is not good.
It's definitely better to show some temporary "Wait for..." form and on completion of the operation/calculation you perform, show your main form.
Much more UX focused design.
You could start a Timer in the Form1_Load method and link its Elapsed event to a method that displays the message.
Thread.Sleep(3000); in the Load event handler delays the form opening for 3 seconds. This why it does not work as you expect. A timer is the best solution.
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.
Happy Friday SO!
I'm building a multi-WinForm application and am having some troubles.
I have a main WinForm that stays open at all times. It hits a database every minute looking for changes, and if there is a change, it will open the second WinForm (this may seem like a stupid way to do things, but for my purpose, this is the best method).
Just sticking the following code into my Form1.cs doesn't do the trick:
Application.Run(new Form2());
Can you guys point me in the right direction? I have no idea where to turn.
Form2 form2 = new Form2();
form2.Show();
and to prevent a ton of forms being opened, maybe:
Form2 form2 = new Form2();
form2.ShowDialog();
#Comment:
A BackgroundWorker is used to keep your current UI Thread responsive. It was not designed to keep multiple forms pumping happily along. Look into running your intensive code as a Background thread within a ThreadPool.
If what you wish is to launch a long process and to show the progress to the user, for example just like when you have a progress bar or something alike, you should use a BackgroundWorker to do the job. Here's a simple example:
public partial class ProgressForm : Form {
// Assuming you have put all required controls on design...
// Allowing some properties to be exposed for progress update...
public properties MaximumProgress {
set {
progressBar1.Maximum = value;
}
public properties OverallProgress {
set {
progressBar1.Value = value;
}
}
public partial class MainForm : Form {
private BackgroundWorker backgroundWorker1;
private ProgressForm _pf;
public MainForm() {
InitializeComponent();
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
// Assuming process starts on Button click.
private void button1_Click(object sender, EventArgs e) {
_pf = new ProgressForm();
_pf.MaximumProgress = number-of-elements-to-treat-returned-by-prevision-or-whatever-else;
// Launching the background workder thread.
backgroundWorker1.RunWorkerAsync(); // Triggering the DoWork event.
// Then showing the progress form.
_pf.ShowDialog();
}
private void backgroundWorker1_DoWork(object sender, EventArgs e) {
LaunchProcess();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
_pf.OverallProgress = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, EventArgs e) {
_pf.Close();
_pf.Dispose();
}
private void LaunchProcess() {
// Do some work here...
// Reporting progress somewhere within the processed task
backgroundWorker1.ReportProgress();
}
}
This is not a compileable code as its purpose is to illustrate the main idea.
Now, is this something alike you want to do?