I am creating a program where the user is pressing buttons like move 10 and move 1 to move a robot and when a user presses the button it will add a text in a listbox like "MoveRobot1" so when the user has finished pressing the buttons and presses the Play button, the program should go down the list line by line moving the robot based on the list at an interval of 300ms but I don't understand how to make it read line by line instead of all at once when I press Play.
private void BtnPlay_Click(object sender, EventArgs e)
{
this.WorkProgress += new WorkProgressHandler(DoWork);
_counter = 0;
this.robot.Reset();
this.MoveRobot(0);
string query1 = "MoveRobot(1)";
string query2 = "MoveRobot(10)";
for (int i = 0; i < MoveBox.Items.Count; i++)
{
if (MoveBox.Items[i].ToString() == query1)
{
this.MoveRobot(1);
DoWork();
}
if (MoveBox.Items[i].ToString() == query2)
{
this.MoveRobot(10);
DoWork();
}
}
}
private void DoWork()
{
_counter++; // increment the counter
}
Use Thread.Sleep in the end of every iteration :
for (int i = 0; i < MoveBox.Items.Count; i++)
{
if (MoveBox.Items[i].ToString() == query1)
{
this.MoveRobot(1);
DoWork();
}
if (MoveBox.Items[i].ToString() == query2)
{
this.MoveRobot(10);
DoWork();
}
Thread.Sleep(300);
}
Related
I am trying to sort an array of 5 random integers in a listbox to ascending order. While creating a new thread and calling it separately, the form becomes unresponsive when I select the 'sort' button.
public partial class Form1 : Form
{
const int iSize = 5;
int[] iArray = new int[iSize];
Random rnd = new Random();
public Form1()
{
InitializeComponent();
}
Thread th;
private void btnGenerate_Click(object sender, EventArgs e)
{
for (int i = 0; i < iArray.Length; i++)
{
iArray[i] = rnd.Next(0, 5);
lbxNumbers.Items.Add(iArray[i]);
}
}
public void threadMethod()
{
bubbleSort(iArray);
foreach(int item in iArray)
{
lbxNumbers.Items.Add(item);
}
}
private void btnSort_Click(object sender, EventArgs e)
{
th = new Thread(threadMethod);
lblStatusUpdate.Text = "Sorting...";
}
private void btnClear_Click(object sender, EventArgs e)
{
lbxNumbers.Items.Clear();
}
public static void bubbleSort(int [] iArray)
{
bool isSorted = false;
int lastUnsorted = iArray.Length - 1;
while(!isSorted)
{
for(int i = 0; i < iArray.Length-1; i++)
{
if(iArray[i] > iArray[i+1])
{
swap(iArray, i, i + 1);
isSorted = false;
}
}
}
}
public static void swap(int [] iArray, int i, int j)
{
int tmp = iArray[i];
iArray[i] = iArray[j];
iArray[j] = tmp;
}
}
I am uncertain where the thread actually kicks in. The listbox can generate the array immediately and clear it, but sort makes it freeze. I am also unsure whether I need to use the background worker tool on this form.
To expound on Dour High Arch's comment.
EDIT
There are a couple of issues with your code. UI updates can only be done on the UI thread. You can accomplish this with the SychronizationContext or async/await handles it for you. Also, your bubblesort was missing an exit condition for the while loop, so it would run forever. The following demonstrates how this would work using async/await.
private async void btnSort_Click(object sender, EventArgs e)
{
lblStatusUpdate.Text = #"Sorting...";
await Run();
}
public async Task Run()
{
//This line runs asynchronously and keeps the UI responsive.
await bubbleSort(iArray);
//The remaining code runs on the UI thread
foreach (int item in iArray)
{
lbxNumbers.Items.Add(item);
}
}
public async Task bubbleSort(int[] iArray)
{
await Task.Run(() =>
{
bool isSorted = false;
while (!isSorted)
{
isSorted = true; //You were missing this to break out of the loop.
for (int i = 0; i < iArray.Length - 1; i++)
{
if (iArray[i] > iArray[i + 1])
{
swap(iArray, i, i + 1);
isSorted = false;
}
}
}
});
}
You can not act on UI, or Main thread within of another.
This line :
lbxNumbers.Items.Add(item);
which is invoked from another thread can cause a trouble.
The proper way of handling this scenario in WindowsForms would be using of
Control.Invoke method.
I process a file line by line reading various events that have a time stamp and data associated with them. I want to be able to show a form while doing processing which I need to interact with and intercept some events by having a button saying interrupt EventX and if it is pressed it will show the event data in a rich text box field when this event is reached sometime in the future. I can then change some of that event data (let's say I simulate some conditions) and when I press "Resume" it should resume processing by raising an event to the intended subscriber for further processing.
So I need an interceptor that will be pass-trough mechanism when a certain form element is pressed and pass that data to the intended subscriber.
I am ok to wait synchronously for modifying data and pressing "Resume"
Thanks
If you want to have a Responsive GUI while doing a long running operation, you need some form of Multitasking. Wich means either async/await or any of the many Multithreading (Thread and BackgroundWorker, mostly) approaches
While pause and resume could be added, doing so usually more work then it is worth. At the very least you run into issues like still held filehandles or race conditions. Often a "cancel" action is way enough/better then a full stop/resume mechanic.
As a Beginner I would advice you to use the BackgroundWorker. It is about as easy as getting into Multitasking via Multithreading can be. I even wrote a example for it a few years back:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
Thank you Christofer,
I accepted you answer as you gave me some suggestions how to solve my problem.
You can see bellow how I solved this problem
Rad
//class variable
private SimulatorRunner simulatorRunner;
//Code behind DevicesSimulatorForm form
private void RunSimulator()
{
btnRerun.BackColor = Color.BurlyWood;
ParameterizedThreadStart start = new ParameterizedThreadStart(RunSimulator);
Thread simulatorProcessingThread = new Thread(start);
simulatorProcessingThread.Start(this);
}
//This will run in a separate thread so when accessing controls Invoke is being used.
public void RunSimulator(object form)
{
DevicesSimulatorForm devicesSimulatorForm = (DevicesSimulatorForm) form;
simulatorRunner.Run(devicesSimulatorForm);
devicesSimulatorForm.InvokeEx(formInner =>
{
formInner.btnRerun.BackColor = Color.LightGray;
InitializeFields();
InitializeTextBackBorder();
InitializeButtonControls();
running = false;
});
}
public class SimulatorRunner
{
public void Run(DevicesSimulator form)
{
string buffer = "Some content read from file in a loop that needs to be passed
to a rich text box when a boolean Intercept check box is true
and FormStatusIntercept will return true and with Thread.Sleep(1)
we will have a chance to update the buffer to the new value and by
unchecking Intercept check box we will exit while loop and continue
processing"
while (true)
{
if (FormStatusIntercept(form, ref buffer))
{
Thread.Sleep(1);
}
else
{
publishEventArgs.Buffer = buffer;
break;
}
}
PublishEvent?.Invoke(this, publishEventArgs);
}
}
private bool FormStatusIntercept(DevicesSimulator simulatorForm, ref string buffer)
{
string modifiedBuffer = buffer;
//When btnFormStatus button is pressed it changes FormStatusContinued = true
//which allows continuation of the processing by exiting while loop
if (simulatorForm.FormStatusContinued == true)
{
simulatorForm.InvokeEx(form =>
{
if (form.rtbFormStatus.Text != modifiedBuffer)
{
modifiedBuffer = form.rtbFormStatus.Text;
}
form.FormStatusContinued = false;
form.FormStatusInterceptPending = false;
});
buffer = modifiedBuffer;
return false;
}
else if (simulatorForm.FormStatusIntercept == true)
{
if (simulatorForm.FormStatusInterceptPending == false)
{
//Whith check box pressed (true) we request a stop
//and we enter a while loop with Thread.Sleep(1)
simulatorForm.InvokeEx(form =>
{
form.btnFormStatus.Text = "Continue";
form.rtbFormStatus.Text = modifiedBuffer;
form.FormStatusInterceptPending = true;
});
}
return true;
}
return false;
}
I have this project with a method like so.
private void LoopThis()
{
MessageBox.Show("Hello World!");
}
And I have this button that invokes the method, and a textbox where I enter a int let's say i enter 10.
Ten I want that method to execute 10 times.
What kind of loop do I use to do this?
private void Button_Click(object sender, RoutedEventArgs e)
{
int times;
if (Int32.TryParse(TextBox.Text, out times))
{
// It could parse the input text (so we deduce it was an integer)
// and not a string.
for (int i = 0; i < times; i++)
{
LoopThis();
}
}
else
{
// Throw exception or show a message to the user
}
}
I am writing in C# and using .net framework 3.5. I am running through multiple loops that on each iteration build the UI and then wait for user feedback (waiting for a Pass or Fail button click). Here is what I am looking to do:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// add both button handlers to function btn_Click
for(int test = 0; test < 5; test++)
for(int variation = 0; variation < 4; variation++)
for(int subtest = 0; subtest < 3; subtest++)
{
// call function to update GUI
// may need to do stuff while at this state
// wait for user to click pass/fail button
}
}
private void btn_Click(object sender, EventArgs e)
{
// pass button pressed, inform for loop to iterate to next test
// if fail button, then stop tests
}
}
The wait inside the for loop is what gets me. Since this is single threaded I was running into issues with Sleep and do not like the idea of putting the check for a button press inside a while loop with a global variable. I tried AutoResetEvent to wait for the button click but that gave me issues too. What is the best way to handle this?
I did write the code in a way that I could try to implement it but do not think it is a very elegant solution:
public partial class Form1 : Form
{
int test;
int variation;
int subtest;
public Form1()
{
InitializeComponent();
test = 0;
variation = 0;
subtest = 0;
// update GUI
btnUpdate.Text = "Hello # " + test.ToString() + "." + variation.ToString() + "." + subtest.ToString();
}
private void btnUpdate_Click(object sender, EventArgs e)
{
subtest++;
if (test >= 5)
if (variation >= 4)
if (subtest >= 3)
{
// done case
Console.WriteLine("Tests are complete");
btnUpdate.Visible = false;
}
if (subtest > 3)
{
subtest = 0;
variation++;
}
if (variation > 4)
{
variation = 0;
test++;
}
Console.WriteLine("I was clicked");
// update button
btnUpdate.Text = "Hello # " + test.ToString() + "." + variation.ToString() + "." + subtest.ToString();
}
}
Thanks in advance!
Threads are overkill for this kind of scenario. You just need to do some code cleanup and refactoring and it will immediately look much nicer and logical.
public class LoopCounter
{
int test = 0;
int variation = 0;
int subtest = 0;
const int MAX_TEST = 5;
const int MAX_VARIATION = 4;
const int MAX_SUBTEST = 3;
public int Test {get{return test;}}
public int Variation {get{return variation;}}
public int Subtest {get{return subtest;}}
public bool DoNext()
{
if (test >= MAX_TEST) // test for end all cycling
return false;
subtest++;
if (subtest < MAX_SUBTEST)
return true;
subtest = 0;
variation ++;
if (variation < MAX_VARIATION)
return true;
variation = 0;
test++;
if (test < MAX_TEST)
return true;
return false;
}
}
Then, you can easily use this code as follows:
public partial class Form1 : Form
{
LoopCounter counter = new LoopCounter();
public Form1()
{
InitializeComponent();
UpdateUI();
}
private void UpdateUI()
{
// update GUI
btnUpdate.Text = "Hello # " + counter.Test.ToString() + "." + counter.Variation.ToString() + "." + counter.Subtest.ToString();
}
private void btnUpdate_Click(object sender, EventArgs e)
{
Console.WriteLine("I was clicked");
if (counter.DoNext())
{
UpdateUI();
}
else
{
// done case
Console.WriteLine("Tests are complete");
btnUpdate.Visible = false;
}
}
}
Just use a separate thread to UpdateUI and block it while button not pressed:
public partial class Form1 : Form
{
AutoResetEvent are = new AutoResetEvent(true);
public Form1()
{
InitializeComponent();
_updateGUI = new UpdateGUIDelegate(UpdateGUI);
Task.Factory.StartNew(() => BuildTest());
}
private void BuildTest()
{
for (int test = 0; test < 5; test++)
for (int variation = 0; variation < 4; variation++)
for (int subtest = 0; subtest < 3; subtest++)
{
are.WaitOne();
if (this.InvokeRequired)
this.Invoke(_updateGUI, test, variation, subtest);
else
UpdateGUI(test, variation, subtest);
}
}
delegate void UpdateGUIDelegate(int test, int variation, int subtest);
private UpdateGUIDelegate _updateGUI;
private void UpdateGUI(int test, int variation, int subtest)
{ }
private void btn_Click(object sender, EventArgs e)
{
are.Set();
}
}
I have 4 dogs who're racing, I need to move them across the form but they don't move gradually, they start out at the starting line and immediately teleport to the finish line, without moving in between. With every timer tick, their location.X is incremented.
Do I need one timer or 4? I currently have one, and its interval is set to 400.
This is the relevant code:
private void btnRace_Click(object sender, EventArgs e)
{
btnBet.Enabled = false;
timer1.Stop();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{ while (!isWon)
{
for (i = 0; i < Dogs.Length; i++) // there are four dogs
{
if (Dogs[i].Run()) // Run() returns true if full racetrack is covered by this dog
{
Winner = i + 1;
isWon = true;
MessageBox.Show("We have a winner! Dog #" + Winner);
break;
}
}
}
And in the Dog class:
public bool Run()
{
Distance = 10 + Randomizer.Next(1, 4);
p = this.myPictureBox.Location;
p.X += Distance ;
this.myPictureBox.Location = p;
//return true if I won the game
if (p.X >= raceTrackLength)
{
return true ;
}
else
{
return false ;
}
}
The dogs only appear to move one step and then immediately show up on the finish line. What am I doing wrong?
Remove While loop from timer1_Tick method.
This method runs every 400 ms, but in your case at first launch it waits until one dog wins.
Also you should stop the timer after one of dogs win.
private void timer1_Tick(object sender, EventArgs e)
{
for (i = 0; i < Dogs.Length; i++) // there are four dogs
{
if (Dogs[i].Run()) // Run() returns true if full racetrack is covered by this dog
{
Winner = i + 1;
isWon = true;
timer1.Stop();
MessageBox.Show("We have a winner! Dog #" + Winner);
break;
}
}
}
Your timer goes off just once and is stuck in this loop;
while (!isWon)
{
}
Remove the loop and let the Timer do it's work
Add at the end
if (isWon) timer1.Stop();