Button_click in windows form - c#

I have this button5 function below. What I want is when the use wants to click button1 after clicking button5 the while loop in button5 should break, because the choice is now 1. Choice is a global variable set to zero at the start. While the button5 function is running the button1 function will not be called on click. How to solve this problem?
This is the Answer Thanks Everyone For the Help!!
private Thread demoThread = null;
delegate void SetTextCallback(string text);
private void button1_Click(object sender, EventArgs e)
{
choice = 1;
System.Console.WriteLine(choice);
}
private void button5_Click(object sender, EventArgs e)
{
//button1.Enabled = false;
button5.Visible = false;
panel2.Visible = true;
panel1.Visible = true;
panel3.Visible = true;
label2.Visible = true;
button1.Visible = true;
button2.Visible = true;
button3.Visible = true;
button4.Visible = true;
this.demoThread = new Thread(new ThreadStart(this.StartForLoop));
this.demoThread.Start();
}
private void StartForLoop()
{
while (choice != 1 || choice != 2 || choice != 3)
{
if (choice == 1 )
{
choice = 1;
break;
}
if (choice == 2)
{
choice = 2;
break;
}
if (choice == 3)
{
choice = 3;
break;
}
Application.DoEvents();
}
System.Console.WriteLine("AAA");
if (choice == 3)//why
{
}
if (choice == 1)//true
{
System.Console.WriteLine("label");
this.SetText("Does the animal lay eggs?");
}
if (choice == 2)//false
{
}
}

This is a wrong concept. Never use Application.DoEvents();, it's a hack. It does not solve any of your problems.
You do not need a while loop in Button5_Click. Everything in that loop could be handled by code in your other click handler.
Your problem can probably be solved by a state machine. This looks complicated, but it#s not. Try to implement it as simple as possible and ask another question when you encounter problems.

You have a problem with Thread's, the problem is that you program Thread is busy in the loop of your button5 and until it finishes handling button5 code, your thread will not pay attention to more anything.
To solve this, you must run your while loop inside of new thread like this:
Thread t = new Thread (new ThreadStart(delegate(){
//while goes here along with the if's...
}));
t.Start();
In your button1, when you change the value of your global variables
the code inside of a thread launched in the button5 will now be aware of
your changes and behave accordingly.
Also be very careful with the following, since choice is a global variable
it can be access by two threads now at the same time, the program thread
and your new thread, because of this ensure you access the choice variable
with mutexs, in c# you can access a thread shared variable like this:
//declare this next to your choice variable.
Object mux_choice = new Object();
lock(mux_choice){
//changing choice here is thread safe.
}
Since choice seems to be a value type, you must create a object representing the access to your value type variable (http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx).
You have more info about threads in C# here:
http://www.albahari.com/threading/
Note: Make sure you protect the choice variable everywhere it is used.
Also, from your comments I assume that you want to modify the Form controls
properties, like label2.Text="..." becarefull with that, you will face Cross Thread Exceptions if you do that. To modify a Controls property you must call the Invoke method, that invokes the change in the UI thread, like this:
label2.Invoke((MethodInvoker)(() => label2.Text = "some text"));
Depending on the .NET framework version, here is a code compatible with .NET 2.0:
label2.Invoke(new MethodInvoker(delegate(){ label2.Text = "some text"; }));
Regards.

The best way is to run a for each loop into another thread so it will not disturb the UI of form.
and user can easily click on button 1.
like just for basic idea :
private void button5_Click(object sender, EventArgs e)
{
button5.Visible = false;
panel2.Visible = true;
panel1.Visible = true;
panel3.Visible = true;
label2.Visible = true;
button1.Visible = true;
button2.Visible = true;
button3.Visible = true;
button4.Visible = true;
Thread thread = new Thread(new ThreadStart(StartForLoop));
thread.Start();
}
public void StartForLoop()
{
while (choice != 1 || choice != 2 || choice != 3)
{
if (choice == 1 || choice == 2 || choice == 3)
{
choice = 1000;
break;
}
Application.DoEvents();
}
if(choice==3)//why
{
}
if(choice==1)//true
{
label2.Text = "asdasd";
}
if(choice==2)//false
{
}
}
PS : better to implement lock on choice as well

Related

Proper way to cancel BackgroundWorker

Its my first time working on a wpf. An existing system has to process an excel file, now the requirement is, the excel file must have five comlumns. Here is the code that has to do the processing
void InsertIDsNamesAndAddWorker_DoWork(object sender, DoWorkEventArgs e)
{
// read the excel file here
int columns = xlWorkSheet.UsedRange.Columns.Count;
if (columns == 5)
{
//do your normal processing
}
else
{
//if requirements are not met then display error message
System.Windows.MessageBox.Show("There must be five columns in this
file", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
Now this code does get into the else part, it however continues to other part which then displays an error message that says "done processing".
Here is the current code of the method that does the confirmation message
void InsertIDsNamesAndAddWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
ProgressBarValue = 100;
StatusLable = "Done Processing.";
if (System.Windows.MessageBox.Show("Done Processing.", "Status", MessageBoxButton.OK, MessageBoxImage.Information) == MessageBoxResult.OK)
{
StatusLable = string.Empty;
ProgressBarValue = 0;
}
}
Now with me being new to the wpf technology, I realized that the hard-coded value of Statuslable is the one causing issues, so I went to set the ProgressBarValue to 100 if the the requirements were met and processing is done.
I also set the ProgressBarValue to zero if the colums was not equal to 5.
Here is the new code
void InsertIDsNamesAndAddWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
int count = ProgressBarValue;
if (count != 100)
{
StatusLable = string.Empty;
ProgressBarValue = 0;
}
else
{
//ProgressBarValue = 100;
StatusLable = "Done Processing.";
if (System.Windows.MessageBox.Show("Done Processing.", "Status", MessageBoxButton.OK, MessageBoxImage.Information) == MessageBoxResult.OK)
{
StatusLable = string.Empty;
ProgressBarValue = 0;
}
}
}
My main question is, is this the right way though? is the any other way I can cancel the work if requirements are not met?
Use the Result prorperty of the DoWorkEventArgs instance you get passed in DoWork:
void InsertIDsNamesAndAddWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (columns == 5)
{
//do your normal processing
e.Result = true; // we're OK
}
else
{
//if requirements are not met then display error message
System.Windows.MessageBox.Show("There must be five columns in this
file", MessageBoxButton.OK, MessageBoxImage.Error);
e.Result = false; // something wrong
}
}
and then in RunWorkerCompleted check the Result value and handle accordingly.
void InsertIDsNamesAndAddWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// check if we're not cancelled or error-ed before checking the Result
result = (!e.Cancelled && e.Error == null)? (bool) e.Result: false; // what is the outcome
ProgressBarValue = 100;
if (result) {
StatusLable = "Done Processing.";
if (System.Windows.MessageBox.Show("Done Processing.", "Status", MessageBoxButton.OK, MessageBoxImage.Information) == MessageBoxResult.OK)
{
StatusLable = string.Empty;
ProgressBarValue = 0;
}
}
else
{
StatusLable = "Error in Excel sheet";
}
}
Notice that Result is of type object. You can put any instance of a type into it, even your own class, which might be needed if you want to return more fine grained details about what went wrong.
The BackgroundWorker has a property called WorkerSupportsCancellation. If this is set to true, you've got another option to cancel the execution.
Whenever something wrong happens, you can call backgroundWorker.CancelAsync(), which will set a boolean to true (which is the CancellationPending property in the BackgroundWorker object).
You can then check, during execution, if the CancellationPending is true. If so, the worker should stop.
If the worker stops, it'll launch the RunWorkerCompleted event, which will end up in the handler for the method (if any is added).
This way of cancelling can be checked at all instructions, or at the start of a for loop (as in: for (int i = 0; (i < x) && worker.CancellationPending; i++) ;)
Hope this helps!

C# BackgroundWorker Completed Called Way Before Completion

I have been trying to work out why my background worker is 'finishing' its work when there is still a lot for it to do. I am actually in the process of refactoring the code for this app, so it did work in the past, but now I am unable to figure out what has gone wrong.
Specifically, the app should open Outlook and then perform a few checks. However, the background worker exits straight after Outlook is opened for no apparent reason (as you will se below there is still plenty of processing to be done).
This appears to be happening early on in the Start() method, directly after calling Process.Start() on Outlook.exe.
The code runs in this order:
calling the background worker - this was the user's choice from a radio set
....
else if (radioButton5.Checked == true)
{
textBox1.Text = "Please wait while your session restarts";
pageControl1.SelectedIndex = 10;
backgroundReset.RunWorkerAsync();
}
The do-work method
public void backgroundReset_DoWork(object sender, DoWorkEventArgs e)
{
backgroundReset.WorkerSupportsCancellation = true;
Session.Reset();
}
the reset session method starts by killing the current session ...
public static void Reset()
{
KillSession();
System.Threading.Thread.Sleep(5000);
Start();
// THE BACKGROUNDWORKER EXITS BEFORE HERE!
if (IsLoggedIn() == false)
{
return;
}
else
{
// Make sure Lync is open before finishing the process ...
var j = 0;
GetSession(Init.servers);
j = 0;
var checker = false;
checker = ProcessHandler.CheckRunning("lync.exe");
while (checker == false)
{
if (j == 100)
{
break;
}
Thread.Sleep(500);
checker = ProcessHandler.CheckRunning("lync.exe");
j++;
}
}
}
As you can see from the comment, the backgroundworder is calling RunWorkerCompleted way before the Reset() method has finished executing.
Below are the other methods called (kill, logoff, start):
KillSession logs the session of and then makes sure it is logged off
private static void KillSession()
{
if (sessionId != null)
{
LogOff();
for (int i = 0; i < 150; i++)
{
if (IsLoggedIn() == true)
{
Thread.Sleep(500);
}
else
{
break;
}
}
}
}
LogOff sends a Cmd command to log off the current session
public static void LogOff()
{
string strCmdIn = "/C LOGOFF " + sessionId + " /SERVER:" + serverName;
Cmd.Exec(strCmdIn);
}
Start() Simply opens Outlook, causing a Citrix session to also start. The app is definitely launching Outlook, but after that it doesn't reach either of the for statements - the BackgroundWorker just exits.
public static void Start()
{
Process.Start(appDataCitrix + "Outlook.exe");
for (int i = 0; i < 15; i++)
{
if (IsLoggedIn2() == false)
{
Thread.Sleep(1000);
}
else
{
break;
}
}
if (IsLoggedIn2() == false)
{
Process.Start(appDataCitrix + "Outlook.exe");
for (int i = 0; i < 10; i++)
{
if (IsLoggedIn2() == false)
{
Thread.Sleep(1000);
}
else
{
break;
}
}
}
}
Does anyone have any idea what is going on here? It is driving me crazy!
Many thanks
Update
The RunWorkerCompleted Method:
As far as my understanding goes, this has no baring on when the process will finish.
public void backgroundReset_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Session.IsLoggedIn())
{
btnFailFinish.Visible = true;
label10.Text = Session.serverName;
pageControl1.SelectedIndex = 3;
}
else
{
pageControl1.SelectedIndex = 10;
pictureBox2.Visible = false;
textBox1.Text = "Double-click Outlook on your desktop to launch a new session.";
textBox15.Text = "Once you have done this please click Finish.";
pictureBox9.Visible = true;
}
}
This is probably because of an exception being thrown from within the start method.
You may either add a try / catch block all around this method and handle the error from within the catch, or check in the RunWorkerCompleted method if an exception occurred :
private void RunWorkerCompleted (object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
// handle your exception here
}
}

Exce­pti­on with th­re­ads in ­wp­f a­pp

Hi guys I receive an exception if (checkBox1.IsChecked == true || checkBox2.IsChecked == true):
The calling thread cannot access this object because a different
thread owns it.
in my wpf app:
private void button1_Click(object sender, RoutedEventArgs e)
{
int i = 0;
int j = Int32.Parse(textBox1.Text);
thr = new Thread[j];
for (; i < j; i++)
{
thr[i] = new Thread(new ThreadStart(go));
thr[i].IsBackground = true;
thr[i].Start();
}
}
public void go()
{
while (true)
{
string acc = "";
string proxy = "";
if (checkBox1.IsChecked == true || checkBox2.IsChecked == true)
{
if (checkBox1.IsChecked == true)
Proxy.type = "http";
else if (checkBox2.IsChecked == true)
Proxy.type = "socks5";
else
Proxy.type = "none";
proxy = rand_proxy();
}
}
}
Why?
You cannot access UI elements from a thread other than one which was those created. Your check boxes are created on UI thread and you can access these only on UI thread.
try this.
public void go()
{
Dispatcher.BeginInvoke(new Action(()=>{
while (true)
{
string acc = "";
string proxy = "";
if (checkBox1.IsChecked == true || checkBox2.IsChecked == true)
{
if (checkBox1.IsChecked == true)
Proxy.type = "http";
else if (checkBox2.IsChecked == true)
Proxy.type = "socks5";
else
Proxy.type = "none";
proxy = rand_proxy();
}
}), null);
}
You cannot access UI elements on a different thread than the UI. To work around this, you can check
checkBox1.Dispatcher.CheckAccess()
and if true, use
checkBox1.Dispatcher.Invoke
or
checkBox1.Dispatcher.BeginInvoke
Use CheckAccess to see if you need call Dispatcher.BeginInvoke or Invoke
See also this post
Basically you're not allowed to access controls from threads other than the thread they were created on. There's a good explanation of the WPF threading model here, and this walks through the issue you are describing.
Good luck.

Locking thread in C#

In my C# project I m using the Timer.One(keyboard_timer) is for to watch if a user press F8 or not and another Timer(clipboard_time) is to watch if Clipboard contains a text or not.In my project The keyboard is always enable and the clipboard_timer is enabled when a user press F8.If the user again press F8 the clipboard_timer is disabled.what my project does that When user press F8 and he copies a word then my project show the meaning of the copied word in a window.my program runs on the background and always check if a user press F8 if he does then all-time my program check the clipboard, if it contains a text(word) if it does the show the meaning of the word everytime.
My code is here:
On the initialize
keyboard_timer.Enabled = true;
keyboard_timer.Tick += new EventHandler(keyboard_timer_Tick);
then
public void keyboard_timer_Tick(object sender, EventArgs e)
{
// clipboard enable
if ((a % 2) != 0)
{
// F9 is for Easy mood to eanble
if ((GetAsyncKeyState(Keys.F9) == -32767) && hot_key == "F9")
{
label2.Text = "Easy";
online_clipboard_active = "";
Clipboard.Clear();
clipboard_timer.Enabled = true;
clipboard_timer.Tick += new EventHandler(clipboard_timer_Tick);
++a;
}
///// // F8 is for online Mood
else if ((GetAsyncKeyState(Keys.F8) == -32767) && online_hot_key == "F8")
{
label2.Text = "Online";
online_clipboard_active = "on";
clipboard_timer.Enabled = true;
clipboard_timer.Tick += new EventHandler(clipboard_timer_Tick);
++a;
}
}// end of enable
//clipboard disable
if ((a % 2) == 0) //
{
// F9 is for Easy mood to disable here
if ((GetAsyncKeyState(Keys.F9) == -32767) && hot_key == "F9")
{
label2.Text = "Off";
clipboard_timer.Enabled = false;
++a;
}
// F8 is for online Mood to disable here
else if ((GetAsyncKeyState(Keys.F8) == -32767) && online_hot_key == "F8")
{
label2.Text = "Off";
online_clipboard_active = "";
clipboard_timer.Enabled = false;
++a;
}
}//end of clipboard disable
}// end of keyboard timer
Clipboard timer is
public void clipboard_timer_Tick(object sender, EventArgs e)
{
if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Text))
{
string x = Clipboard.GetText();
Clipboard.Clear();
if ((a%2)==0 && online_clipboard_active == "on")
{
//cal online_mood form to translate the string from googletranslator
online_mood o = new online_mood(x);
o.Show();
}
else if((a%2)==0 && online_clipboard_active == "")
{
//cal show_meaning form to show the meaning into a window
show_meaning s = new show_meaning(x);
s.Show();
}
}
}// end of clipboard timer Tick
I want that when clipboard timer enable on that time keyboard timer will be lock because both of them uses a variable. When clipboard timer runs then keyboard timer will be lock and then when clipboard timer finished it works then the keyboard timer will be reactivated How can I solve this?
Anyone give me any help?????
Simple locking can be accomplish with a static member.
private static readonly object Sync = new object();
and then before setting or getting the info use
lock(Sync)
to lock the variable.
BTW: I don't get what you are trying to do. Maybe you should use the Keypress event of the form instead of the timers.
Look into C# locks
you basicly do this:
public Object lockObject = new Object(); //You want a separate object so that it's not changed when lock is active
...
//Code that can be run by any number of thread at the same time
...
lock(lockObject)
{
...
//Code that is accessable by only one thread at a time:
...
}
...
//Code that can be run by any number of thread at the same time
...
Hope this helps you out a bit.

How to update button text after event

I'm trying to let a program post a bunch of text. The user enters text, the amount of messages and how fast these must be delivered. While the program is busy, the button text needs to be "Stop" instead of "Start". When you press the button to force it to stop after you've initially launched it, the text changes back to "Start", but this doesn't happen when the program stops after the given amount of messages are delivered, even though the code is in place and doesn't generate an error.
I have a feeling that this is because of the text not updating for some reason. I've tried to flush it with Invalidate() and Update(), but this isn't working. How to fix this?
Here is the code:
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text == "Start")
{
isEvil = true;
button1.Text = "Stop";
Thread t = new Thread(StartTyping);
t.Start(textBox1.Text);
}
else
{
isEvil = false;
button1.Text = "Start";
}
}
private void StartTyping(object obj)
{
string message = obj.ToString();
int amount = (int)numericUpDown2.Value;
Thread.Sleep(3000);
for (int i = 0; i < amount; i++)
{
if (isEvil == false)
{
//////This does NOT work
//button1.Text = "Start";
//button1.Invalidate();
//button1.Update();
//button1.Refresh();
//Application.DoEvents();
break;
}
SendKeys.SendWait(message + "{ENTER}");
int j = (int)numericUpDown1.Value * 10;
Thread.Sleep(j);
}
}
You have four answers telling you to update UI stuff from the UI thread, but none of them address the logic flow problem with your code.
The reason why it doesn't happen is because it only happens in the for-loop when isEvil is false. When does isEvil get set to false? Only when you click "Stop", and nowhere else.
If you want the button to go back to "Start" after the thread finishes, without clicking "Stop", then you need to add code after the loop to do that, independent of the value of isEvil: (piggybacking off of VoidMain's answer)
private void StartTyping(object obj)
{
string message = obj.ToString();
int amount = (int)numericUpDown2.Value;
Thread.Sleep(3000);
for (int i = 0; i < amount; i++)
{
if (isEvil == false)
{
if (button1.InvokeRequired)
{
button1.BeginInvoke( new Action(() => { button1.Text = "Start"; }) );
}
else
{
button1.Text = "Start";
}
break;
}
SendKeys.SendWait(message + "{ENTER}");
int j = (int)numericUpDown1.Value * 10;
Thread.Sleep(j);
}
if (button1.InvokeRequired)
{
button1.BeginInvoke( new Action(() => { button1.Text = "Start"; }) );
}
else
{
button1.Text = "Start";
}
}
Now you have duplicated code, so you might want to split it off into a separate method.
You need to be on the UI thread to update the UI.
Try something called the SynchronizationContext. There are plenty of examples when you google it.
If you're in WPF or Silverlight, you could use the Dispatcher. Again, lots of examples if you search those keywords in google or StackOverflow.
You must update your controls from the UI thread. This is how you would do it for winforms.
for (int i = 0; i < amount; i++)
{
if (isEvil == false)
{
button1.Invoke(new Action(() => button1.Text = "Start"));
break;
}
SendKeys.SendWait(message + "{ENTER}");
int j = (int)numericUpDown1.Value * 10;
Thread.Sleep(j);
}
This will block till button1 get's its text updated. If you don't want it to block, replace Invoke with BeginInvoke
Your best bet is to use a BackgroundWorker. It's a bit too wieldy to add a concise example here but there's a decent tutorial from O'Reilly
Something like this (not tested) should work:
private void StartTyping(object obj)
{
string message = obj.ToString();
int amount = (int)numericUpDown2.Value;
Thread.Sleep(3000);
for (int i = 0; i < amount; i++)
{
if (isEvil == false)
{
if(button1.InvokeRequired)
{
button1.BeginInvoke( new Action(() => { button1.Text = "Start"; }) );
}
else
{
button1.Text = "Start";
}
break;
}
SendKeys.SendWait(message + "{ENTER}");
int j = (int)numericUpDown1.Value * 10;
Thread.Sleep(j);
}
}

Categories

Resources