Stop long running events - c#

I have a following form Form3 that is opened by another form Form1, and when closed Form1 opens back up.
The problem is when I close Form3 DoSomething keeps running after form is closed.
I understand that I can make DoSomething into a thread and set IsBackground = true but is there another way to stop all processes when form closes.
This code is just example, For illustration.
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
private void DoSomething()
{
int i = 0;
while(true)
{
if (!this.IsDisposed)
{
Application.DoEvents();
i++;
Thread.Sleep(10);
label1.Text = i.ToString();
dataGridView1.Rows.Add();
}
}
}
private void button1_Click(object sender, EventArgs e)
{
DoSomething();
}
private void Form3_FormClosed(object sender, FormClosedEventArgs e)
{
this.Dispose();
Form1.Default.Show();
}
}

You never break out of the while(true). You should either break the loop when it's IsDisposed is true, change your while loop to while(!IsDisposed), or store use a class level variable that determines when to break the loop.
I would probably opt for the latter, as it gives you a little more control.
public partial class Form3 : Form
{
volatile bool clDoSomething;
public Form3()
{
InitializeComponent();
}
private void DoSomething()
{
int i = 0;
clDoSomething = true;
while(clDoSomething)
{
Application.DoEvents();
++i;
Thread.Sleep(10);
label1.Text = i.ToString();
dataGridView1.Rows.Add();
}
}
private void button1_Click(object sender, EventArgs e)
{
DoSomething();
}
private void Form3_FormClosed(object sender, FormClosedEventArgs e)
{
clDoSomething = false;
Form1.Default.Show();
}
}

Your fundamental approach is flawed.
First off, Application.DoEvents should be avoided unless you are sure that you really need it, and that you are using it correctly. You do not need it here, and you are not using it correctly.
What you really need here is a Timer.
private Timer timer = new Timer();
private int count = 0;
public Form3()
{
InitializeComponent();
timer.Tick += timer_Tick;
timer.Interval = 10;
//when the form is closed stop the timer.
FormClosed += (_, args) => timer.Stop();
}
private void button1_Click(object sender, EventArgs e)
{
count = 0;
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
count++;
label1.Text = count.ToString();
dataGridView1.Rows.Add();
}
When the Form is create the Timer is configured. The tick event is set, along with the interval. The tick event will look similar to your DoSomething method; it will involve running some bit of code every 10 seconds, from the UI thread, while keeping the UI responsive. When the form is closed simply stop the timer and it will stop firing off these events.
Also note that in this example here pressing the button multiple times simply resets the timer and the count, it doesn't end up creating two loops that each fire every 10 milliseconds.

Override this.Dispose() or this.Close() as appropriate and kill off DoSomething() manually.

Thanks to cdhowie suggestions and input of all others. Mowing DoEvents to the end and adding IsDipsosed solved my problem.
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
private void DoSomething()
{
int i = 0;
while ((true) && !this.IsDisposed)
{
i++;
Thread.Sleep(10);
label1.Text = i.ToString();
dataGridView1.Rows.Add();
Application.DoEvents();
}
}
private void button1_Click(object sender, EventArgs e)
{
DoSomething();
}
private void Form3_FormClosed(object sender, FormClosedEventArgs e)
{
Form1.Default.Show();
}
}

try to add this intsruction in the FormClosing event :
System.Diagnostics.Process.GetCurrentProcess().Kill();
it's a little bit like this:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
System.Diagnostics.Process.GetCurrentProcess().Kill();
}

Related

Delay reacting to TextChanged event

I have WinForms application that reacts to keystrokes in a textbox using the TextChanged event. I want to delay reacting until there has been short gap (maybe 300 milliseconds) since the last keystroke. Below is my current code:
private void TimerElapsed(Object obj)
{
if (textSearchString.Focused)
{ //this code throws exception
populateGrid();
textTimer.Dispose();
textTimer = null;
}
}
private void textSearchString_TextChanged(object sender, EventArgs e)
{
if (textTimer != null)
{
textTimer.Dispose();
textTimer = null;
}
textTimer = new System.Threading.Timer(TimerElapsed, null, 1000, 1000);
}
My problem is that textSearchString.Focused throws a System.InvalidOperationException.
What am I missing?
A System.Threading.Timer runs on a background thread, which means that in order to access UI elements you must perform invocation or use a System.Windows.Forms.Timer instead.
I'd recommend the System.Windows.Forms.Timer solution as that is the easiest. No need to dispose and reinitialize the timer, just initialize it in the form's constructor and use the Start() and Stop() methods:
System.Windows.Forms.Timer textTimer;
public Form1() //The form constructor.
{
InitializeComponent();
textTimer = new System.Windows.Forms.Timer();
textTimer.Interval = 300;
textTimer.Tick += new EventHandler(textTimer_Tick);
}
private void textTimer_Tick(Object sender, EventArgs e)
{
if (textSearchString.Focused) {
populateGrid();
textTimer.Stop(); //No disposing required, just stop the timer.
}
}
private void textSearchString_TextChanged(object sender, EventArgs e)
{
textTimer.Start();
}
try this..
private async void textSearchString_TextChanged(object sender, EventArgs e)
{
await Task.Delay(300);
//more code
}

How to run a windows form from a backgroundworker runnig through another backgroundworker?

this is the simplified plan for a solution:
for some reasons i need to run a windows form through a backgroundworker that is runnig by another backgroundworker, when the new windows form loads, the older backgroundworker must pause. i write the code like this :
creating a class with name : temp
public class temp
{
static public BackgroundWorker backgroundWorker1 = new BackgroundWorker() { WorkerSupportsCancellation = true };
static public EventWaitHandle ew = new EventWaitHandle(false, EventResetMode.ManualReset);
static public BackgroundWorker back = new BackgroundWorker() { WorkerSupportsCancellation = true };
}
the codes for form1 are :
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
temp.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
temp.back.DoWork += new DoWorkEventHandler(back_DoWork);
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
Form2 f = new Form2();
f.Show();
}
private void button1_Click(object sender, EventArgs e)
{
temp.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
temp.back.RunWorkerAsync();
if (temp.backgroundWorker1.CancellationPending)
temp.ew.WaitOne();
}
}
}
and the codes of form2 goes here :
namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
temp.backgroundWorker1.CancelAsync();
temp.ew.Reset();
}
}
}
by clicking the button1 from form1 the temp.backgroundworker1 runs and then in the DoWork of temp.backgroundworker1, the temp.back runs and then FORM2 LOADS BUT THE FORM2 HANGS AND BECOMES USELESS AND YOU CANNOT USE THAT ANY MORE.
where did i wrong ?
the whole plan that i'm going to execute is :
we have a For loop that processes every row of a DataGridView.
each time in a certain point, another windowsform opens
and it stops the loop until the user inserts the information and then click on OK button, the windowsform closes and the loop keep on working. i dont know what to do.......
even if i dont cancel working of the temp.backgroundworker in form2load like the code below, the Form2 is useless
private void Form2_Load(object sender, EventArgs e)
{
}
Do not use any UI operation in the work thread (DoWork method). Maybe that's why you set the CheckForIllegalCrossThreadCalls property, but your app will not work properly just suppresses the error when the debugger is attached.
See my answer here for the correct usage of the BackgroundWorker (that is about canceling but you can see the operations in UI and worker thread).
In this particular case what you can use a similar volatile bool to sign the UI thread that the form can be shown. Or, if you want to send different messages between the threads, use a ConcurrentQueue<T> to write and read messages:
public partial class Form1 : Form
{
private enum Message
{
ShowForm2,
SuspendWork,
ResumeWork,
FinishWorker1
// ... and whatever you want
}
private Timer timer;
private ConcurrentQueue<Message> messagesToUI = new ConcurrentQueue<Message>();
private ConcurrentQueue<Message> messagesToWorker = new ConcurrentQueue<Message>();
public Form1()
{
InitializeComponent();
timer = new Timer(this);
timer.Interval = 10;
timer.Tick += PollUIMessages;
timer.Enabled = true;
}
void PollUIMessages(object sender, EventArgs e)
{
// do we have a new message?
Message message;
if (messagesToUI.TryDequeue(out message))
{
switch (message)
{
case Message.ShowForm2:
Form2 f = new Form2();
f.Show();
// todo: in Form2.Close add a Resume message to the messagesToWorker
break;
// ... process other messages
}
}
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
// Here you are in the worker thread. You can send a message to the
// UI thread like this:
messagesToUI.Enqueue(Message.ShowForm2);
bool isWorking = true;
// and here you can poll the messages to the worker thread
while (true)
{
Message message;
if (!messagesToWorker.TryDequeue(out message))
{
// no message: idle or work
if (isWorking)
DoSomeWork(); // do whatever you want
else
Thread.CurrentThread.Sleep(10);
continue;
}
switch (message)
{
case Message.FinishWorker1:
// finishing the worker: jumping out
return;
case Message.SuspendWork:
isWorking = false;
break;
case Message.ResumeWork:
isWorking = true;
break;
}
}
}

Move shape while key is down in WPF

I want to move a shape in a window while user holds a key down.
I'm new to this so i tried this approach, but i can't change circle.RederTransform from my new thread, because it belongs to a different thread. How should it be done?
public partial class MainWindow : Window
{
private Matrix transformationMatrix = Matrix.Identity;
private Thread myThread = null;
private bool isGoing = false;
public MainWindow()
{
InitializeComponent();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
isGoing = true;
myThread = new Thread(HandleMyThread);
myThread.Start();
}
private void Window_KeyUp(object sender, KeyEventArgs e)
{
isGoing = false;
}
private void HandleMyThread(object obj)
{
while(isGoing)
{
transformationMatrix.Translate(10, 0);
circle.RenderTransform = new MatrixTransform(transformationMatrix);
Thread.Sleep(50);
}
}
}
In WPF you want to use the System.Windows.Threading.DispatcherTimer class for UI-thread safe timers.
Here is an example:
public partial class MainWindow : Window
{
private DispatcherTimer _timer;
public MainWindow()
{
InitializeComponent();
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(50);
_timer.Tick += timer_Tick;
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
_timer.Start();
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
_timer.Stop();
}
private void timer_Tick(object sender, EventArgs e)
{
transform.X += 2;
transform.Y += 2;
}
}
You might get away with doing it by just using an Invoke...but there are better ways to do what you want to do.
Firstly, creating a thread each time you handle the KeyDown is inefficient...and could lead to problems...because it may take a while to create and schedule the Thread...thus a delay in your "thing" moving.
In addition it's possible for you to get in a mess with having multiple threads staying in existence. For instance, say someone was repeatedly pressing and releasing the key....in that case it's possible for "old" threads to still keep running...because isGoing flag is true....in other words, there is poor synchronization.
private void HandleMyThread(object obj)
{
Action action = new Action( ()=>
{
transformationMatrix.Translate(10, 0);
circle.RenderTransform = new MatrixTransform(transformationMatrix);
};
while(isGoing)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, action);
Thread.Sleep(50);
}
}

visual c# limit how much a button can be clicked

OK i wish to make it so there is a limit to how much a button can be clicked because with multiple boxes open causes my application to crash ~:(
here is my code for the form that i wish to stop it from opening to many don't get me wrong i do want multiple boxes just not many:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
this.KeyPreview = true;
this.KeyDown += new KeyEventHandler(Form1_KeyDown);
}
private void pictureBox1_Click(object sender, EventArgs e)
{
var myForm = new Form2();
myForm.Show();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Alt && e.KeyCode == Keys.A)
{
Form3 f3 = new Form3();
f3.ShowDialog();
}
}
private void Form2_Load(object sender, EventArgs e)
{
}
}
}
Use a private counter. Every time a user clicks the button, increment the counter, and check the counter before showing the new form.
private int clickCounter = 0;
private void pictureBox1_Click(object sender, EventArgs e)
{
this.clickCounter++;
if (this.clickCounter < 10) // arbitrary number
{
var myForm = new Form2();
myForm.Show();
}
}
This will work for ensuring the user doesn't abuse the button on a single instance of the form. To make sure the same counter applies to all instances of the form, make it static:
private static int clickCounter = 0;
private void pictureBox1_Click(object sender, EventArgs e)
{
clickCounter++;
if (clickCounter < 10) // arbitrary number
{
var myForm = new Form2();
myForm.Show();
}
}
If you'd like to decrement the counter when a form closes as Junior Programmer suggests, you can bind to the Closing event. This will effectively limit the number of new forms that can be opened (rather just limiting the number of times the button can be clicked). This will work for both the local and static counter versions:
myForm_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
clickCounter--;
}
private void pictureBox1_Click(object sender, EventArgs e)
{
if (clickCounter < 10) // arbitrary number
{
clickCounter++;
var myForm = new Form2();
myForm.Closing += myForm_Closing;
myForm.Show();
}
}
This is another solution which doesn't differ much from the p.s.w.g's answer;
public class Form1 : Form {
private static int count = 0;
public Form1(){
InitializeComponent();
button1.Click += click;
}
private void click(object sender, EventArgs e){
count++;
if(count > 20) {
button1.Click -= click;
count = 0;//If you want to reset
}
}
}
Simply create a global static int count variable and set it to 0. Every time the button is clicked, increase the value of count (count++).
So when the button is clicked put all your event handling in the bellow if statement instead of my comment
if(count<5)
{
count++;
//run your code for the button event
}
else
{
MessageBox.Show("You have pressed the button too many times");
}
So using your above code, your final should look like this
public partial class Form2 : Form
{
public static int count=0;
public Form2()
{
InitializeComponent();
this.KeyPreview = true;
this.KeyDown += new KeyEventHandler(Form1_KeyDown);
}
private void pictureBox1_Click(object sender, EventArgs e)
{
if(count<5)
{
count++;
var myForm = new Form2();
myForm.Show();
}
else
{
MessageBox.Show("You have pressed the button too many times");
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Alt && e.KeyCode == Keys.A)
{
Form3 f3 = new Form3();
f3.ShowDialog();
}
}
private void Form2_Load(object sender, EventArgs e)
{
}
}

updating gui from another class c#

hey i am new to c# plz help.
i am writing a program that sorts data in a file and it is a time consuming process so i thought that i should run it in a separate thread and since it has alot of step so i made a new class for it. the problem is that i want to show the progress in the main GUI and i know for that i have to use Invoke function but the problem is that the form control variables are not accessible it this class. what should i do ??????
sample code:
public class Sorter
{
private string _path;
public Sorter(string path)
{
_path = path;
}
public void StartSort()
{
try
{
processFiles(_path, "h4x0r"); // Just kidding
}
catch (Exception e)
{
MessageBox.Show("Error: " + e.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void processFiles(string Dir, string[] key)
{
/* sorting program */
}
and it is used as
public partial class Form1 : Form
{
Sorter sort;
public Form1()
{
InitializeComponent();
}
private void browseBtn_Click(object sender, EventArgs e)
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
textBox1.Text = folderBrowserDialog1.SelectedPath;
}
private void startBtn_Click(object sender, EventArgs e)
{
if (startBtn.Text == "Start Sorting")
{
Thread worker = new Thread(new ThreadStart(delegate() {
sort = new Sorter(textBox1.Text);
sort.StartSort(); }));
worker.start();
}
else
MessageBox.Show("Cancel");//TODO: add cancelling code here
}
}
plz help..
Add an Event to your class that is doing the multi-threaded work, that triggers when the progress changes. Have your form subscribe to this event and update the progress bar.
Note ProgressEventArgs is a little class that inherits EventArgs and has an Integer for the progress.
// delegate to update progress
public delegate void ProgressChangedEventHandler(Object sender, ProgressEventArgs e);
// Event added to your worker class.
public event ProgressChangedEventHandler ProgressUpdateEvent
// Method to raise the event
public void UpdateProgress(object sender, ProgressEventArgs e)
{
ProgressChangedEventHandler handler;
lock (progressUpdateEventLock)
{
handler = progressUpdateEvent;
}
if (handler != null)
handler(sender, e);
}
I would recommend you read up on the BackgroundWorker class. It is exactly for the problem you are trying to solve and makes things a lot easier than doing manual threading yourself.
Brief Example
public Form1()
{
InitializeComponent();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void btnStart_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i < 101; ++i)
{
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
//Sort Logic is in here.
Thread.Sleep(250);
backgroundWorker.ReportProgress(i);
}
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy && backgroundWorker.WorkerSupportsCancellation)
backgroundWorker.CancelAsync();
}
You could do something like this:
public delegate void StatusReporter(double progressPercentage);
public class MainClass
{
public void MainMethod()
{
Worker worker = new Worker(ReportProgress);
ThreadStart start = worker.DoWork;
Thread workThread = new Thread(start);
workThread.Start();
}
private void ReportProgress(double progressPercentage)
{
//Report here!!!
}
}
public class Worker
{
private readonly StatusReporter _reportProgress;
public Worker(StatusReporter reportProgress)
{
_reportProgress = reportProgress;
}
public void DoWork()
{
for (int i = 0; i < 100; i++ )
{
// WORK, WORK, WORK
_reportProgress(i);
}
}
}
There are a few option available to solve this sort of issue. In any case, you will have to fiddle with Invoke to get the UI to update.
You could...
...add an event that fires on your new class which your UI can listen to, and Invoke as applicable - you'd still need to pass the data to your worker class (by constructor, properties, method call, etc)
...keep the method as a method on your form, and pas that to start your new thread from (after all, a new thread doesn't have to be starting in a different class)
...change the access modifiers on your controls to be (say) internal such that any class within the same assembly can Invoke changes to the controls, or read from them.
...make your worker class a child of the form it needs to access - it can then see the privates of its parent, as long as it is passed a reference to the instance.

Categories