I've a backgroundworker which take care of a timer in my application. This is the code:
protected override void OnLoad(EventArgs e)
{
// Inizializzo il backgroundworker
bgwTimer.WorkerReportsProgress = true;
bgwTimer.WorkerSupportsCancellation = true;
bgwTimer.DoWork += (bgwTimer_DoWork);
bgwTimer.RunWorkerCompleted +=(bgwTimer_RunWorkerCompleted);
bgwTimer.ProgressChanged += (bgwTimer_ProgressChanged);
}
void bgwTimer_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
throw new NotImplementedException();
}
void bgwTimer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
throw new NotImplementedException();
}
Basically the event "ProgressChanged" is never fired and so I cannot update the status of a progressbar.
The event DoWork is linked to this method:
void bgwTimer_DoWork(object sender, DoWorkEventArgs e)
{
int i = 0;
if (bgwTimer.CancellationPending)
{
e.Cancel = true;
}
else
{
while (bgwTimer.IsBusy)
{
Thread.Sleep(1000);
bgwTimer.ReportProgress(i);
refreshTimer();
}
}
}
By my side the code looks good and it runs fine. As you can see the ReportProgress method is called but the event is not fired. Any hints?
UPDATE:
Whops! I found that the event "bgwTimer_ProgressChanged" is fired only if I run the RunWorkerAsync right after the declaration of event. Basically:
bgwTimer.ProgressChanged += (bgwTimer_ProgressChanged);
bgwTimer.RunWorkerAsync(); //this works!
Since I run the worker when the user press a button, the event is not triggered.
Here's the code of click event button:
private void btnNext_Click(object sender, EventArgs e)
{
this.TopMost = true;
btnNext.Enabled = false;
progressBar1.Step = 0;
if (_bgwTimer.IsBusy)
_bgwTimer.CancelAsync();
else
_bgwTimer.RunWorkerAsync();
}
Put a breakpoint, or a Debug.Print or System.Windows.Forms.Messagebox just before bgwTimer.ReportProgress(i), to verify that you're actually entering the while loop.
Note that the BackgroundWorker is not actually a timer; it's a wrapper for a thread that provides a threadsafe invoking layer for your user interface.
Your if (bgwTimer.CancellationPending) { } should be inside the while loop, not outside it. It will only get checked once in your current code.
Note that, if you're inside the DoWork event handler, then by definition you're running an asynchronous process, so IsBusy should always be true (according to the MSDN documentation), and therefore your while is an infinite loop. But check it with your debugger.
It's not raising the event because the value of i is always zero, which is helpfully undocumented but I found out the same thing when building a background worker a while back.
You forgot to start the worker. Add this line to your OnLoad() method:
bgwTimer.RunWorkerAsync();
In the DoWork-Method replace bgwTimer through ((BackgroundWorker)sender). Maybe this is the problem
Related
private void button1_Click(object sender, EventArgs e)
{
//progress bar
backgroundWorker1.RunWorkerAsync();
progressBar1.Value = 1;
progressBar1.Minimum = 1;
progressBar1.Maximum = 10;
label3.Text = "backgroundworker Task completed";
Thread.Sleep(100);
Application.DoEvents();
// end of bgw
}
I am getting error on the Backgroundworker1.RunworkerAsync() where it shows an error of Backgroundworker is busy and try to use it aftertime.
Is there any other method to solve. I thank in advance.
When you call RunWorkerAsync, the IsBusy property will return True. The BackgroundWorker will raise its DoWork event and you do your background work in that event handler. Once that event handler completes, it will raise its RunWorkerCompleted event and you can optionally handle that event to update the UI after the background work. Once the event handlers for both events complete, the IsBusy property will return False. The whole point of that property is to tell you that the BackgroundWorker is already busy so you shouldn't call RunWorkerAsync. The solution to your problem is not call RunWorkerAsync until IsBusy is False again, e.g.
private void button1_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
{
return;
}
// Setup ProgressBar and whatever else here first.
backgroundWorker1.RunWorkerAsync();
}
I have a method that use in downloading files from server . MY method works fine, however i want to return a calculated percentage of the process execute at each instance of the execution. i tried to use backgroundworker and use the backgroundworker_changed method for my progressbar as below.
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
Start worker on button click.
private void btnStart_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
worker_changed method
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProcessPercentage;
}
My problem is , i understand i have to report progress from the DoWork() method. Please how do i achieve that? I thought of doing something like this.
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = Convert.ToInt32( e.UserState );
}
It still does not fire as there was no progress returned from the DoWork. Any help would be appreciated.
You need to call ReportProgress from your DoWork method along with the precentage int value.
For example:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//you can safely pass data out of the thread via the 'obj'
bw.ReportProgress(0, obj);
}
As explained in this official tutorial from msdn. You have to report progress changes by yourself inside your DoWork method. Here's is an example :
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(10/*the percentage you want*/);
}
The BackgroundWorker.ProgressChanged event :
is raised when you call the ReportProgress method
and it is designed to :
add code to indicate the progress, such as updating the user
interface.
The BackgroundWorker.RunWorkerCompleted event :
occurs when the background operation has completed, has been canceled,
or has raised an exception
Both events are intended to be called when your DoWork method failed, ended or called the ReportProgress method. So in another words, we can say that you're the one deciding when they occur. It cannot be done without an action of yours, it's your process and only you know when it is 20%, 30%or 100% (and so on...) completed.
private void AddMyScrollEventHandlers()
{
VScrollBar vScrollBar1 = new VScrollBar();
}
private void button1_Click(object sender, EventArgs e)
{
while (true)
{
if (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
vScrollBar1.Value = vScrollBar1.Value + 1;
label1.Text = vScrollBar1.Value.ToString();
}
else
{
break;
}
System.Threading.Thread.Sleep(200);
}
}
private void button2_Click(object sender, EventArgs e)
{
// vScrollBar1.Scroll
}
I am new in C#. I was working on scroll. What I wanted here is, if anyone click button1 then scroll automatically move to the end and I wanted to show gradual value in label1. Also when someone click button2 scrolling stop.
Now the problem is label1 do not show gradual change in value. It shows value once when the scrolling stop.
Also when scrolling continue i,e when while loop is working I can not click on button2. Actually I can not click on the form even.
Someone please give me some idea how to do this.
This happens because the thread that is performing the task is busy, and it's the same thread that updates the UI. You can use a multithreading solution. Take a look at
BackgroundWorker
All the UI events run in the main thread of the application, so the application can only process one event at a time. When the application is processing an event, no other event will be processed.
Since you are doing a UI related work periodically, the best option is to use the Timer class:
Drop Timer from the toolbox into the form.
In the properties window, set the interval to 200.
Double click the timer object to create the Tick event handler.
Put this code in the newly created timer1_Tick method:
if (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
vScrollBar1.Value = vScrollBar1.Value + 1;
label1.Text = vScrollBar1.Value.ToString();
}
else
{
timer1.Stop();
}
Change your methods as below:
private void AddMyScrollEventHandlers()
{
timer1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Stop();
}
Now you're done.
I would recommend using BackgroundWorker control, as suggested by Agustin Meriles. However, one more important thing to note is that You should use Control.Invoke(...) method to update controls from another thread.
I've modified Your code, tested it in a sample application and it seems to work correctly.
First, add a new BackgroundWorker control to Your form and assign backgroundWorker1_DoWork to its DoWork event.
Then, You can use the code below:
private void button1_Click(object sender, EventArgs e)
{
//code from here is moved to BackgroundWorker control
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//while (true)
//the condition directly in the while looks more clear to me
while (vScrollBar1.Value + 1 < vScrollBar1.Maximum)
{
//update controls using Invoke method and anonymous functions
vScrollBar1.Invoke((MethodInvoker)delegate() { vScrollBar1.Value += 1; });
label1.Invoke((MethodInvoker)delegate() { label1.Text = vScrollBar1.Value.ToString(); });
//when called inside BackgroundWorker, this sleeps the background thread,
//so UI should be responsive now
System.Threading.Thread.Sleep(200);
}
}
If You have any problems when using this code, please let me know.
Update
As mentioned in the comments, You could also use ProgressChanged event of the BackgroundWorker. It requires some more changes in the code, but is more suitable in this case. You can find some information about it here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged.aspx.
If You are not going to add any other code with more processing in the while loop, You can also use Timer control, as suggested by MD.Unicorn in his answer.
I'd like to use a BackgroundWorker for background operations because I thought there is no need to take care of "BeginInvoke" etc. when updating WinForm-Controls. Is that right? As far as I know, you can update WinForms controls directly by using the ProgressChanged and RunWorkerCompleted eventhandlers.
But I can't, I although get the following exception:
Control control name accessed from a thread other than the thread it was created on
Some code:
public partial class ConfigurationForm : Form
{
public ConfigurationForm()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
label1.Text = String.Empty;
// [...]
}
private void StartButton_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
label1.Text = "Converting...";
backgroundWorker1.RunWorkerAsync();
}
}
private void CancelButton_Click(object sender, EventArgs e)
{
if (backgroundWorker1.WorkerSupportsCancellation == true)
{
backgroundWorker1.CancelAsync();
}
progressBar1.Dispose();
this.Close();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
// EXCEPTION here, why?
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
Converter c = new Converter();
c.Start(worker, e);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// EXCEPTION in all cases, why?
if (e.Cancelled == true)
{
label1.Text = "Canceled";
}
else if (e.Error != null)
{
label1.Text = "Error: " + e.Error.Message;
}
else
{
label1.Text = "Done!";
}
}
}
I have to say, this is not a WinForms application but an VSTO PowerPoint add-in. The Form above gets created by the add-in like this when the user is clicking an icon in the ribbon bar of PowerPoint:
//Do I need [STAThread] here? but doesn't seem to work anyway
private void button1_Click(object sender, RibbonControlEventArgs e)
{
ConfigurationForm config = new ConfigurationForm();
config.Show();
}
Can you tell me what's the problem here?
I posted the link but I don't actually think it is the best solution. Clearly the failure occurs because you never called Application.Run() or used Form.ShowDialog(). You can assign the context explicitly as shown but you can get some very tricky problems if you don't do it right. Like assigning it more than once.
The better fix is to ask it to automatically install itself. Which then ensures that whatever form you create will then install it only when it wasn't done before. Put this in front of the form creation code:
WindowsFormsSynchronizationContext.AutoInstall = true;
With the big advantage that if the code ever gets repeated, you won't create another instance of it and potentially screw up the thread's ExecutionContext.
Do consider ShowDialog() as another fix. If I'm not mistaken then you now also have a problem with tabbing and shortcut keystrokes.
Your assumption would be correct for Windows Forms. The way it works though is BackgroundWorkers uses the SynchronizationContext of the current thread. In a windows app, that will be a WindowsFormsSynchronizationContext, which does the marshalling for you.
In a VSTO app, it won't be. It will probably just be the default one, which simply executes the methods. The link from Hans Passant has the code you need to get it to work as expected. I.e.:
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
...create and start your background worker here...
I would like to use the following code in C# but I just can't seem to get out of it. I would like to terminate the application if the user presses a key or moves the rodent (aka mouse). Here is my code (no laughing!).
private void frmDots_KeyDown(object sender, KeyEventArgs e)
{
bgNotClicked = false;
Close();
}
private void frmDots_Click(object sender, EventArgs e)
{
bgNotClicked = false;
Close();
}
while (bgNotClicked)
{
// Clear the first element in our XY position. This is the reverse of the way I normally create the dots application
System.Drawing.Rectangle clearDots = new System.Drawing.Rectangle(Dots.PositionX[iCounter], Dots.PositionY[iCounter], 8, 8);
// Create the black color and brush to clear dots
Color clearDotsColor = Color.Black;
SolidBrush clearDotsBrush = new SolidBrush(clearDotsColor);
// Finally clear the dot
e.Graphics.FillEllipse(clearDotsBrush, clearDots);
GetRandomPosition(iCounter);
// Fill the elements to display colors on the displays canvas
System.Drawing.Rectangle colorDots = new System.Drawing.Rectangle(Dots.PositionX[iCounter], Dots.PositionY[iCounter], 8, 8);
// Create the color and brush to show dots
Color colorRandom = GetRandomColor();
SolidBrush colorBrush = new SolidBrush(colorRandom);
// Finally show the dot
e.Graphics.FillEllipse(colorBrush, colorDots);
Thread.Sleep(5);
iCounter++;
if (iCounter == 399)
{
iCounter = 0;
}
}
}
Your "busy waiting" strategy is poor design. Instead, you should use event handlers that are fired:
On keypress.
When the mouse is moved.
In either case, you can respond by terminating the application.
Edit:
After seeing your edit, this is definitely your problem. The issue is that your while loop blocks the main UI thread, so it never handles the Windows Messages which trigger your key press/mouse/etc handlers.
You have a couple of options - you can either move some of this onto a separate thread, do what I suggested below, or add a call to Application.DoEvents in your while loop. This would allow your event handlers to run, which would in turn set bgNotClicked = false;. Right now, that's never occurring because your UI is blocked entirely.
Original Post:
If you're doing this loop in your UI thread, you're going to need to rethink the design a bit.
Setting bgNotClicked = false; somewhere in an event handler will work, but only if your event handler is able to run. If you're doing the above code in the UI thread, it will block your UI thread indefinitely, preventing the event handler from firing.
I would recommend reworking this to be based off a timer (so it runs repeatedly on regular intervals), instead of locked into a while loop. This would allow your other UI events to fire between runs, and instead of setting bgNotClicked = false;, your event handler could just set the timer to be not enabled.
Your bgNotClicked variable needs to be set to false by your event handler for key-press.
If the rodent is moved by your mouse, you would need a similar mouse event handler.
The break keyword will terminate a loop. In this case, when you hit the case where you want to stop the loop, you would just use break;.
If you're looping like that you need to give the application a moment to process the events that you're hoping will cause the interruption. This is the job of the DoEvents method.
private bool WeAreDone = false;
private void DoingIt()
{
while (true)
{
Application.DoEvents();
if (WeAreDone)
{
break;
}
}
}
private void InterruptButton_Click(object sender, EventArgs e)
{
WeAreDone = true;
}
I think using a Timer fits the Windows event-driven model better than the busy wait while loop. You might try something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int iCounter = 0;
private void Draw()
{
// ....
}
private void timer1_Tick(object sender, EventArgs e)
{
Draw();
iCounter++;
if(iCounter == 399)
{
iCounter = 0;
}
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 5;
timer1.Enabled = true;
}
private void Form1_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
Close();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
timer1.Enabled = false;
Close();
}
}
This does not seems to be the correct way. .Net Framework has provided you with the events to handle the KeyPress and MouseMove/Click actions. Why are you not using them?
Try moving the loop into a BackgroundWorker's DoWork event handler. Then your GUI will still be responsive and instead of that nasty variable, you can just call the CancelAsync method to stop the loop.
You can exit the loop using the break statement.
EDIT: OK, I take back the flag thing!
Use Environment.Exit(2); (or Environment.Exit(1) it really doesn't make a difference) to exit out of the application.
Exit While
...................