I want to write a code that displays numbers 1 to 10 in a textBox. Following code has been written by me. But unfortunately only number 10 is displayed in textBox. What is wrong in my code? Thanks.
public partial class Form1 : Form
{
int i,j;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for (i = 1; i <= 10; i++)
{
textBox1.Text = Convert.ToString(i);
for (j = 0; j < 10000000; j++) ;
}
}
}
textBox1.Text = Convert.ToString(i);
Overwrites the textbox text each loop. You want:
textBox1.Text += Convert.ToString(i) + " ";
Note, there are others ways of doing this
Set default value of text box to 0 and then just increment it on button click
private void button1_Click(object
sender, EventArgs e)
{
textBox1.Text = Convert.ToString(Convert.Int32(textBox1.Text)+1);
for (j = 0; j < 10000000; j++) ;
{
}
}
You should not do work in GUI thread. When you have some simple work (small calculation), then there is not a problem. But when you have a loner work, move the work to background worker.
This is the correct way how to do this:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace BackgroundWorkerExample
{
public partial class Form1 : Form
{
private BackgroundWorker worker;
public Form1()
{
InitializeComponent();
this.worker = new BackgroundWorker();
this.worker.DoWork += Worker_DoWork;
this.worker.ProgressChanged += Worker_ProgressChanged;
this.worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
this.worker.WorkerReportsProgress = true;
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) => this.button1.Enabled = true;
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.textBox1.Text = e.ProgressPercentage.ToString();
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 10; i++)
{
this.worker.ReportProgress(i);
// do work
Thread.Sleep(1000);
}
}
private void button1_Click(object sender, EventArgs e)
{
this.button1.Enabled = false;
this.worker.RunWorkerAsync();
}
}
}
The button (after the operation starts) is disabled to be inactive. The reason is, to prevent try second execution of the background thread. When the background worker ends, the button is enabled.
Related
I want to add elapsed time (minutes) to an GUI and do something else in parallel.
Everything I try does not succeed, it sticks in my gui. I add example:
namespace Backgrondworker
{
public partial class Form1 : Form
{
int aa = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
progressBar1.Maximum = 10;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for(int i =1;i<=10;i++)
{
Thread.Sleep(1000);
backgroundWorker1.ReportProgress(0);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value += 1;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("completed");
}
}
}
You should do the following
Create a Timer
Start the timer when you start the progress bar
In the timer, tick use the timespan class and Elapsed property to get the elapsed minutes and show them in a label.
Stop the Timer when the progressbar is at maxsize.
You put 0 in ReportProgress:
backgroundWorker1.ReportProgress(0);
Change this to:
int percent = (int)Math.Round((i * 100.0) / 10);
backgroundWorker1.ReportProgress(percent);
Dividing by 10 gives you the fraction of work done (you count 10 times in the loop) and multiplying by 100 is for getting the percent.
I explain:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(1000);
label2.Text = i.ToString();
}
});
Task.Run(() =>
{
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(1000);
label3.Text = i.ToString();
}
});
}
}
got : System.InvalidOperationException: 'Cross-thread operation not valid: Control 'label3' accessed from a thread other than the thread it was created on.'
" int ans = 2;
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i <21; i++)
{
ans = 2;
label1.Text += i.ToString();
while (true)
{
if (ans == 1)
{
break;
}
}
}
}
private void button1_Click(object sender, EventArgs e)
{
ans = 1;
} "
this is a simple app
I want to print a number & then wait to the button to be clicked to break the while loop
but when I run the application , the form doesn't show .
"T think that the problem is the while (true)".
what to do?
Use a timer. Start the timer when the form loads. Each time it ticks, increment the number and display it. On button click, you just need to stop the timer.
private Timer _myTimer;
private int number = 0;
private void Form1_Load(object sender, EventArgs e)
{
_myTimer = new Timer();
_myTimer.Interval = 1; // 1 millisecond
_myTimer.Tick += new EventHandler(MyTimer_Tick);
_myTimer.Start();
}
// increments the number at timer tick
private void MyTimer_Tick(object sender, EventArgs e)
{
number ++;
// TODO: update UI here
}
// Stops the timer
private void button1_Click(object sender, EventArgs e)
{
_myTimer.Stop();
}
It's best to not use a loop here. Since this loop won't end you won't ever leave Form_Load and it won't display the form. If you were trying to do some task when the user clicks a button, why not move that logic to button1_Click?
The correct way to implement such a task as you describe would be as such:
private EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset);
private void Form1_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 26; i++)
{
ewh.WaitOne();
Action updateLable = () => label1.Text = "" + i;
label1.BeginInvoke(updateLable);
}
});
}
private void button1_Click(object sender, EventArgs e)
{
ewh.Set();
}
As you can see I've replaced your busy wait (while(true)) with a .Net wait handle.
One of the answers describes a timer that acts every millisecond - that is a busy wait of sorts.
This is what async/await is for. Mark your Load() event with "async", then "await" a Task that continues when a ManualResetEvent is triggered in the Button click handler:
private System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);
private async void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < 21; i++)
{
label1.Text = i.ToString();
mre.Reset();
await Task.Factory.StartNew(() => { mre.WaitOne(); });
}
button1.Enabled = false;
label1.Text = "Done!";
}
private void button1_Click(object sender, EventArgs e)
{
mre.Set();
}
I am working on a simple mediaplayer application. It works great but I want to add some extra features. I have added a trackbar control.How can i set trackbar length the same as the music's length ?
Like if the song is halfways the trackbars halfways.This is what I have so far
string[] files, indexed_files;
private void button3_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = true;
if (ofd.ShowDialog() == DialogResult.OK) {
files = ofd.SafeFileNames;
indexed_files = ofd.FileNames;
for (int i = 0; i < files.Length; i++)
{
listBox1.Items.Add(files[i]);
}
}
button4.Enabled = true;
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
axWindowsMediaPlayer1.URL = indexed_files[listBox1.SelectedIndex];
progressBar1.Maximum =(int) axWindowsMediaPlayer1.currentMedia.duration;
axWindowsMediaPlayer1.PlayStateChange += axWindowsMediaPlayer1_PlayStateChange;
}
void axWindowsMediaPlayer1_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
trackBar1.Value = (int)axWindowsMediaPlayer1.Ctlcontrols.currentPosition;
}
int index = 0;
private void button4_Click(object sender, EventArgs e)
{
if (listBox1.Items.Count != 0) {
axWindowsMediaPlayer1.URL = indexed_files[index];
trackBar1.Maximum = (int)axWindowsMediaPlayer1.currentMedia.duration;
index++;
index = (index % listBox1.Items.Count);
}
}
This will bring you the desired outcome.In my example i just placed the url in the form load for demonstration purposes.The openstatechanged event its to set the trackbar maximum since you need to wait for the player to load the file,after that the code its pretty self-explanatory:
public partial class Form1 : Form
{
Timer t;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
axWindowsMediaPlayer1.URL = "YourUrlHere";
t = new Timer();
t.Interval = 1000;
t.Tick += new EventHandler(t_Tick);
}
void t_Tick(object sender, EventArgs e)
{
trackBar1.Value = (int)this.axWindowsMediaPlayer1.Ctlcontrols.currentPosition;
}
private void axWindowsMediaPlayer1_OpenStateChange(object sender, AxWMPLib._WMPOCXEvents_OpenStateChangeEvent e)
{
if (axWindowsMediaPlayer1.openState == WMPLib.WMPOpenState.wmposMediaOpen)
{
trackBar1.Maximum = (int)axWindowsMediaPlayer1.currentMedia.duration;
t.Start();
}
}
}
Yes its a timer:),and probably it is best to set it bellow 1000 for reasons of delay.
So you should now add a timer and insert the following code in timer Tick event handler:
trackbar.Value = this.axWindowsMediaPlayer1.ctlControls.CurrentPosition;
I'm new to C# and object-oriented programming in general. I've been trying to implement a "Cancel" button into my GUI so that the user can stop it mid-process.
I read this question: How to implement a Stop/Cancel button? and determined that a backgroundWorker should be a good option for me, but the example given doesn't explain how to hand arguments to the backgroundWorker.
My problem is that I do not know how to pass an argument into backgroundWorker such that it will stop the process; I have only been able to get backgroundWorker to stop itself.
I created the following code to try to learn this, where my form has two buttons (buttonStart and buttonStop) and a backgroundWorker (backgroundWorkerStopCheck):
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Timers;
namespace TestBackgroundWorker
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set the background worker to allow the user to stop the process.
backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
}
private System.Timers.Timer myTimer;
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
//If cancellation is pending, cancel work.
if (backgroundWorkerStopCheck.CancellationPending)
{
e.Cancel = true;
return;
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
// Notify the backgroundWorker that the process is starting.
backgroundWorkerStopCheck.RunWorkerAsync();
LaunchCode();
}
private void buttonStop_Click(object sender, EventArgs e)
{
// Tell the backgroundWorker to stop process.
backgroundWorkerStopCheck.CancelAsync();
}
private void LaunchCode()
{
buttonStart.Enabled = false; // Disable the start button to show that the process is ongoing.
myTimer = new System.Timers.Timer(5000); // Waste five seconds.
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.Enabled = true; // Start the timer.
}
void myTimer_Elapsed(object sender, ElapsedEventArgs e)
{
buttonStart.Enabled = true; // ReEnable the Start button to show that the process either finished or was cancelled.
}
}
}
The code, if it worked properly, would just sit there for five seconds after the user clicked "Start" before re-enabling the Start button, or would quickly reactivate the Start button if the user clicked "Stop".
There are two problems with this code that I am not sure how to handle:
1) The "myTimer_Elapsed" method results in an InvalidOperationException when it attempts to enable the Start button, because the "cross-thread operation was not valid". How do I avoid cross-thread operations?
2) Right now the backgroundWorker doesn't accomplish anything because I don't know how to feed arguments to it such that, when it is canceled, it will stop the timer.
I'd appreciate any assistance!
First of all, the problem to avoid "cross-thread operation was not valid" is use Invoke on controls. You cannot use a control from a different thread.
About the second issue, I would implement it in the following way. This is a minimum background worker implementation with cancel support.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set the background worker to allow the user to stop the process.
backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
backgroundWorkerStopCheck.DoWork += new DoWorkEventHandler(backgroundWorkerStopCheck_DoWork);
}
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
try
{
for (int i = 0; i < 50; i++)
{
if (backgroundWorkerStopCheck.CancellationPending)
{
// user cancel request
e.Cancel = true;
return;
}
System.Threading.Thread.Sleep(100);
}
}
finally
{
InvokeEnableStartButton();
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
//disable start button before launch work
buttonStart.Enabled = false;
// start worker
backgroundWorkerStopCheck.RunWorkerAsync();
}
private void buttonStop_Click(object sender, EventArgs e)
{
// Tell the backgroundWorker to stop process.
backgroundWorkerStopCheck.CancelAsync();
}
private void InvokeEnableStartButton()
{
// this method is called from a thread,
// we need to Invoke to avoid "cross thread exception"
if (this.InvokeRequired)
{
this.Invoke(new EnableStartButtonDelegate(EnableStartButton));
}
else
{
EnableStartButton();
}
}
private void EnableStartButton()
{
buttonStart.Enabled = true;
}
}
internal delegate void EnableStartButtonDelegate();
}
About passing arguments to the worker, you can pass any object in the RunWorkerAsync() method, and its reveived in the backgroundWorkerStopCheck_DoWork method:
...
backgroundWorkerStopCheck.RunWorkerAsync("hello");
...
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
string argument = e.Argument as string;
// argument value is "hello"
...
}
Hope it helps.
try this example and you will see how to pass data to and from the BackgroundWorker:
public partial class Form1 : Form
{
BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.WorkerSupportsCancellation = true;
}
private void button1_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
btnCancel.Enabled = true;
double[] data = new double[1000000];
Random r = new Random();
for (int i = 0; i < data.Length; i++)
data[i] = r.NextDouble();
bw.RunWorkerAsync(data);
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnStart.Enabled = true;
btnCancel.Enabled = false;
if (!e.Cancelled)
{
double result = (double)e.Result;
MessageBox.Show(result.ToString());
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
double[] data = (double[])e.Argument;
for (int j = 0; j < 200; j++)
{
double result = 0;
for (int i = 0; i < data.Length; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
return;
}
result += data[i];
}
e.Result = result;
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
bw.CancelAsync();
btnStart.Enabled = true;
btnCancel.Enabled = false;
}
}
Edit: I solved the problem by creating the background worker by creating it in code instead of dragging and dropping in design.
Now I know how to use a background worker.
Question
It's my first time using a BGWorker, so here's my issue...
The cursor doesn't change to "Wait".
The progress bar doesn't update.
RunWorkerCompleted isn't invoked.
But the textbox does update.
Am I doing something wrong ?
The Code
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace BGWorker
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
progressBar1.Minimum = 0;
progressBar1.Value = 0;
progressBar1.Maximum = 100;
}
private void button1_Click(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
textBox1.AppendText(i.ToString());
textBox1.AppendText("\n");
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
label1.Text = e.Error.Message;
}
else
{
label1.Text = "All Done !";
Cursor.Current = Cursors.Default;
}
}
}
}
Thanks.
I agree with pst.
Always create your worker in the code behind - dragging and dropping on the form is never a good idea
You never access UI elements from the DoWork. You can only do this from the ReportProgress and RunWorker Completed Events
Your if statement segment should contain your most common path with the least common path in the else.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 100; i++)
{
sb.AppendLine(i.ToString());
backgroundWorker1.ReportProgress(i);
}
e.Result = sb.ToString();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
textbox1.Text = e.Result.ToString();
label1.Text = "All Done !";
Cursor.Current = Cursors.Default;
}
else
{
label1.Text = e.Error.Message;
}
}