im new to C# language and i would appreciate any help/feedback on the following issue.
Basically, im trying to develop a program such that it has two classes class1(main thread) and class2. class1 has a button and class2 starts a thread and do some calculations. How can i update any property of the button in class1(main) from class2 within the thread in class2?
here is an example i would like to do
// CLASS 1 is a win form
class1:form
{
public btn_click()
{
// call function startthread from class2
btn.Enabled = false;
startthread()
}
}
// CLASS 2 is not a winform
class2
{
public startthread()
{
Thread worker = new Thread(doCalculation)
}
public doCalculation()
{
// do some calculation then
// blahhh
// blahhh
// HERE I NEED SOMEHOW ENABLED MY BUTTON
}
}
This depends a bit on the UI framework you're using. As an example, say you wanted to update the text written on the button.
If you're using WPF, you could use:
class1Instance.TheButton.Dispatcher
.BeginInvoke( (Action) () => class1Instance.TheButton.Content = "Foo" );
With Windows Forms, you'd use:
class1Instance.TheButton
.BeginInvoke( (Action) () => class1Instance.TheButton.Text = "Foo" );
You have to bring the modifying code back onto the the main thread, regardless whether its WPF or Winforms, cross-thread access to the UI is either explicitly disallowed (usually) or strongly discouraged and buggy.
There are a lot of ways to do this, in Winforms I typically use Control.Invoke like this:
public void UpdateMyTextBox(string NewText)
{
if(InvokeRequired)
Invoke(new Action<string>(UpdateMyTextBox), NewText);
else
myTextBox.Text = NewText;
}
In this example, the InvokeRequired property will check to see if it's being called on the UI thread, and if not, we will call Invoke which will place the request onto the UI thread.
In WPF, you watch to use the Dispatcher, and you do so in a very similar way:
public void UpdateMyWpfTextBox(string NewText)
{
if(!CheckAccess())
Dispatcher.Invoke(new Action<string>(UpdateMyWpfTextBox), NewText);
else
myTextBox.Text = NewText;
}
Not to sure what your trying to do here, but this is best an answer I can come up with, considering the questions. I'm going to assume this is a winforms application
Class2 is going to need a reference to the button in Class 1. So you'll need to pass that to it.
Once Class2 has reference to the button you can attempt to modify the property as
btn.Text = "New Text";
I'm going to assume again that you've tried this route, got an exception and so you posted your question. The reason you got the exception was that your btn and your class2 are running in separate threads. To get around this, you can call invoke on the button. I often handle this like this.
private void SetButtonText(string newText) {
if (btn.InvokeRequired) {
Invoke((MethodInvoker)(() => SetButtonText(newText)));
}
else {
btn.Text = newText;
}
}
Related
I'm trying to hide a form created on the main thread, from a secondary thread but I obviously get a cross-threading issue when I call the hide method. I'm new to this and don't really have a clue as to how to how to correct this. I've always just created a delegate to invoke my method if it's changing stuff created on the main thread, but I don't know how to do that here for the built-in hide method. Let me know if you need more information.
code:
public partial class MainForm : Form
{
ControlPanelForm m_controlPanel = new ControlPanelForm();
// ....
void MeterThread()
{
while (true)
{
// ....
if (EMOdetected)
{
m_controlPanel.Deinitialize();
m_controlPanel.Hide(); // **** //
}
}
}
}
Basically, my MainForm pulls up a control panel form that does some work. In the background I have a thread running and checking for stuff, one of which is an Emergency Off, at which point I want to shut my control panel down and then hide it.
If I try to invoke it right there,
m_controlPanel.Invoke(new EMOHandler(m_controlPanel.Hide)); // **** //
it doesn't look like it executes anything when i debug it. It seems to just pass over the command. Again, I'm new to this so any and all explanations are welcome.
There's no reason to check InvokeRequired or create an anonymous method. Simply write
mainForm.Invoke(new MethodInvoker(mainForm.Hide));
You haven't given any information code-wise but this is a common pattern for manipulating the UI thread from a non-UI thread.
if (mainForm.InvokeRequired)
{
mainForm.Invoke(new Action(() =>
{
mainForm.Hide();
}));
}
else
mainForm.Hide();
As a simple rule, which you already pointed out:
You should not access one window from another thread.
I would suggest you something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
new Thread(SampleFunction).Start();
}
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
return;
}
textBox1.Text += value;
}
void SampleFunction()
{
// Gets executed on a seperate thread and
// doesn't block the UI while sleeping
for(int i = 0; i<5; i++)
{
AppendTextBox("hi. ");
Thead.Sleep(1000);
}
}
}
This is a pretty nice example of how to use MultiThreading, which I got from here.
Though in your case, the method Hide already exists on MainForm and literally waits for you to be invoked, like others already pointed out:
mainForm.Invoke(new MethodInvoker(mainForm.Hide));
On click of a button I have this code with is should show a dialog on top of the current form and display text, wait for one second, change the text and then finally close it:
Form p = new Form();
p.ShowDialog();
p.Text = "Start.";
Thread.Sleep(1000);
p.Text = "Counting.";
Thread.Sleep(1000);
p.Text = "End.";
Thread.Sleep(1000);
p.Close();
However once it executes p.ShowDialog(); it stops the code until the form p is closed and it doesn't work as I intended it to. Can I get some guidance on this? Not necessarily the solution, but at least maybe some keywords I could google on?
UPDATE: due to the difficulties I am facing trying to access business logic, which is irrelevant to the problem, I am delaying providing the working example. Stay tuned and sorry :)
SOLUTION: what I did is in fact used Show() instead of ShowDialog(). Since i was impossible to access form from business logic, BackgroundWorker came in handy and was being used between them. I cannot share any code or the layout of the project structure, but in conclusion, the accepted answer's main statement was the key to the solution :)
That is the point of ShowDialog(). It creates a modal form and does not return control to the calling function until you are done. If it doesn't need to be modal, then use .Show(). If it does need to be modal, then put code in the Form Load method to update the text as needed.
http://msdn.microsoft.com/en-us/library/c7ykbedk.aspx
taken from the link above:
When this method is called, the code following it is not executed until after the dialog box is closed.
if you want to form to display whatever it is you want to display you should write the code inside the the form itself, do that in an eventhandler of the form show event.
As you have found, ShowDialog is a blocking method that does not return until the dialog is closed. Your code to change the text and handle the delay needs to be within the dialogue itself.
However, it's worth noting the next problem that you'll find: if you call Thread.Sleep(1000) from the UI thread, your application will become unresponsive for 1 second at a time. This is probably not what you're aiming for! I'd suggest you look into the Timer or BackgroundWorker classes to handle this more smoothly.
Check this out:
public partial class Form2 : Form
{
delegate void SetTextCallback(string text);
delegate void CloseFormCallback();
public Form2()
{
InitializeComponent();
new Thread(DoMagic).Start();
}
public void DoMagic()
{
this.SetText("Start.");
Thread.Sleep(1000);
this.SetText("Counting.");
Thread.Sleep(1000);
this.SetText("End");
Thread.Sleep(1000);
this.CloseForm();
}
private void CloseForm()
{
if (this.InvokeRequired)
{
CloseFormCallback c = new CloseFormCallback(CloseForm);
this.Invoke(c);
}
else
{
this.Close();
}
}
private void SetText(string text)
{
if (this.label1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.label1.Text = text;
}
}
}
I have a windows forms program with a form MainForm. On a button press I start a code that runs (pulses) on every 0.5secs on another thread. I want to modify many things, like labels, progressbars on my MainForm, from the Pulse method. How is this possible?
So I would like to know, how to interract with variables, values, in that thread, and the MainForm. Modify each other, etc..
On foo button click, I tell my pulsator to start.
Pulsator.Initialize();
Here is the Pulsator class:
public static class Pulsator
{
private static Thread _worker;
public static void Initialize()
{
_worker = new Thread(Pulse);
_worker.IsBackground = true;
_worker.Start();
}
public static void Close()
{
if (_worker != null)
{
_worker.Abort();
while (_worker.IsAlive || _worker.ThreadState != ThreadState.Stopped)
{
//closing
}
}
}
public static void Pulse()
{
if (_worker != null)
{
while (true)
{
SomeOtherClass.Pulse();
Thread.Sleep(500);
}
}
else
{
SomeOtherClass.Pulse(); // yeah I know this doesnt needed
}
}
}
SomeOtherClass Pulse method looks like :
public static void Pulse()
{
//here I will have several values, variables, and I want to show results,
// values on my MainForm, like:
Random random = new Random();
MainForm.label1.Text = random.Next(123,321).ToString(); // I hope you know what I mean
}
Of course it's much complicated, it's just a silly example.
Generally, in WinForms it's not safe to modify the state of visual controls outside the thread that owns the control's underlying unmanaged resources (window handle). You have to use the Control.Invoke method to schedule executing the modification on the control's owning thread.
As others already mentioned, you have to use Control.Invoke to change the UI controls from the background thread.
Another option is to use System.ComponentModel.BackgroundWorker (it's available in the form designer toolbox). You could then take a regular forms timer, to call the RunWorkerAsync-Method and do your background work in the DoWork event handler, which is automatically called from another thread.
From there, you can hand data back to the main thread, by calling ReportProgress. This will raise the ProgressChanged event in the main thread, where you are free to update all your UI controls.
Why not use a System.Timers.Timer?
E.g.:
trainPassageTimer = new Timer(500);
trainPassageTimer.AutoReset = true;
trainPassageTimer.Elapsed += TimeElapsed;
...
private void TimeElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
// Do stuff
// Remember to use BeginInvoke or Invoke to access Windows.Forms controls
}
C# 2 or higher (VS2005) has anonymous delegates (and C# 3 has lambdas which are a slightly neater version of the same idea).
These allow a thread to be started with a function that can "see" variables in the surrounding scope. So there is no need to explicitly pass it anything. On the downside, there is the danger that the thread will accidentally depend on something that it should not (e.g. a variable that is changing in other threads).
_worker = new Thread(delegate
{
// can refer to variables in enclosing scope(s).
});
It is a cross threaded operation in windows application done in c#, How can i change it ?
You can write a method which you can call from any thread:
private void SetLabel(string newText)
{
Invoke(new Action(() => SomeLabel.Text = NewText));
}
Then you can just call SetLabel("Update the label, please") from any thread.
However, your question title states “from another Form” rather than “from another thread”, so it is unclear what you actually mean. You don’t need multithreading if you just want to have multiple forms. You should use threads only for tasks, e.g. downloading a file, copying a file, calculating a value, etc., but not for Forms.
You need to use a delegate and invoke...
private delegate void SetLabelSub(string NewText);
private void SetLabel(string NewText)
{
if (this.InvokeRequired()) {
SetLabelSub Del = new SetLabelSub(SetLabel);
this.Invoke(Del, new object[] { NewText });
} else {
SomeLabel.Text = NewText;
}
}
Then you can just call SetLabel("New Text Here") from any thread
How about writing a more general method to change the Text property of any control in your form like:
private void SetText(Control control, string text)
{
if (control.InvokeRequired)
this.Invoke(new Action<Control>((c) => c.Text = text),control);
else
control.Text = newText;
}
This will work for labels, buttons, etc, from either the UI thread or any other thread.
If you're dealing with threads you need to use the form.Invoke() method, assuming you're passing the form instance into the other form.roughly
Form form1 = new Form()
Form form2 = new Form();
form2.CallingForm = form1; // make this property or what ever
inside form2 add some code like
form1.Invoke(someDelagate, value);
I don't do winforms that often but if you google form.Invoke you'll get some good examples of how to do cross thread operations.
Creating a .net application in C#, windows forms. How do I update the progress bar 1 step every cycle of a 100 cycle loop?
(I’m processing an excel sheet in the loop.)
The progress bar controls are in the UI class which connects to the controller class which connects to a custom class
(MVC pattern). The loop is in the custom class.
Do I need to send the UI class instance all the way down in each method or is there a better way?
Right now the progress bar updates after the loop finishes. Application.doevents and .update or .refresh don’t work.
Say the below is your class responsible for doing the work with the loop in it. Add an event to indicate your progress. Then from your UI simply handle that event and update the progress bar accordingly.
sealed class Looper
{
public event EventHandler ProgressUpdated;
private int _progress;
public int Progress
{
get { return _progress; }
private set
{
_progress = value;
OnProgressUpdated();
}
}
public void DoLoop()
{
_progress = 0;
for (int i = 0; i < 100; ++i)
{
Thread.Sleep(100);
Progress = i;
}
}
private void OnProgressUpdated()
{
EventHandler handler = ProgressUpdated;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
You might implement this by having a BackgroundWorker as part of your UI, where in the backgroundWorker.DoWork event you call looper.DoLoop(). Then in your handler for the looper.ProgressUpdated event you can call backgroundWorker.ReportProgress to increment your progress bar from the UI thread.
Note that it would probably make more sense to include the progress itself in the information carried by your ProgressUpdated event (I just didn't feel like writing out a new class deriving from EventArgs to illustrate this; you probably get the picture anyway).
Also note that the above really doesn't make sense unless you're executing your code with the loop on a separate thread from the UI thread. Otherwise, all of your work is getting done before the next UI refresh anyway, so your progress bar would not be providing any value (it would just go from 0 to 100 when the loop completed).
Just an example of how this sort of thing can be achieved.
I usually have one class that does the invocation checks on the UI. UI -> "UpdaterClass" -> other class.
The Updater class has predefined methods and references to the UI controls. So Updater.StepProgressBar() is what I call to step the UI progress bar. I pass the Updater class reference to any class that is going to need to update the UI directly.
This way, all UI updates from different threads are handled by one class. Not the most generic way to impliment it, but it never fails.
Example psuedocode:
class Updater()
{
public ProgressBar pb;
delegate void UpdateProgressBar();
public StepProgressBar()
{
if(pb.InvokeRequired)
{
BeginInvoke(new UpdateProgressBar(this.StepProgressBar);
}
else
{
pb.Step();
}
}
}
Something like that.
You could use a delegate. When your background process creates the custom class, tie on a delegate that gets called from the custom class to report update. Then you can react to that call in the UI layer and update the progress bar from there.
e.g. (warning, psuedocode):
MyCustomClass class = new MyCustomClass();
class.ProgressUpdated += (s,e)=>{ /* update UI */};
class.StartLoop();