I have the class called mainForm that it is main window of my program. I create a TextBox(this TextBox Logs program) object in this class and i want to write program status to it. I do this from mainForm and other object(by passing TextBox object to it) easily, But when i want to do that from another thread, it's complicated.
However, i am writing to TextBox by the thread that it runs the defined code in mainForm(using delegate).
My question is, How to write in the TextBox from thread that runs in another class?
public partial class mainForm : Form
{
TextBox log = new TextBox();
.
.
.
OtherClass o = new OtherClass(log);
}
class OtherClass
{
private TextBox log;
public otherClass(TextBox aLog)
{
log = aLog;
Thread thread = new Thrad(new ThreadStart(this.run));
thread.Start();
}
private void run()
{
log.Text = "Message";// I Can't Do This. Can I Use Delegate Here? How?
}
}
You can use Invoke/BeginInvoke:
log.BeginInvoke(
(Action)(() =>
{
log.Text = "Message";
}));
This allows the secondary thread to safely forward GUI changes to the GUI thread which is the only one that should apply them.
Another way using defined delegate - incidently Xt here can be reused for other methods as long as the signature is the same. Parameters can also be passed - (would then have parameters in the Xt delegate and Invoke of it would pass a coma separated list for each.
private void run()
{
XteChangeText();
}
private delegate void Xt();
private void XteChangeText()
{
if (log.InvokeRequired)
{
Invoke(new Xt(XteChangeText));
}
else
{
log.Text="Message";
}
}
Related
This question already has answers here:
Invoke in Class in c# winforms
(4 answers)
Closed 2 years ago.
so I want to access a label in my main form from a static Method in another class within another thread
How do I do that?
by the way, I used this code to start Threads
new Thread(new ThreadStart(WebHelper.Check)).Start();
The UI can only be altered by the UI Thread. If your thread is in the same class as the form, you could access the UI Thread by using the Control.Invoke method.
this.Invoke(new Action(() => {
//UI Code here
}));
However, if you want to access it from another class, I would recommend using events to access the UI's class, and then you can use Invoke.
//This would be in your class
public delegate void UIStuff(string text);
class AClass
{
public static event UIStuff ChangeLabel;
public static void StaticFunction() {
ChangeLabel.Invoke("Test");
}
}
//This would be your form class
public partial class Form1 : Form
{
public Form1(){
AClass.ChangeLabel += AClass_ChangeLabel; //Note you can press tab for auto complete
}
AClass_ChangeLabel(string text) {
this.Invoke(new Action(() => {
this.MainLabel = text;
}));
}
}
Note: I believe you could also change the label by creating a new instance in your static method Form1 f1 = new Form1();, but I don't believe this is what you want.
I have a problem using delegates to change a Textbox from a thread that isn't the main form thread.
I have two class, a main Form1.cs class with the UI and another class, LINClass.cs where I wrote a device functions.
In the Form1 I start a backgroundworker that poll the device continuously, and another thread that retrieve data from the device (RXTask()), all the functions of the two threads are from LINCLass.cs.
The thread that retrieve data from the device contains a delegate that point to a Form1.cs function that permit to change the Form1 textboxes:
public class LINClass : Form
{
private delegate void FormUpdater(int devnum, string rpm, string current, string temp);
//some other variables and procedure
public void RXTask()
{
FormUpdater frmUpdt = new FormUpdater(Form1.GUIupdate);
//other procedures and a loop containing the invoke...
this.Invoke(frmUpdt, new object[]{devnum, rpm,
current,
temperature});
}
The Form1 class contain the method invoked, written as below
public static void GUIupdate(int eWPnum, string rpm, string current, string temp)
{
//take the parameters and write them in the textbox
}
Now when I run the code, threads are running but I have an exception when invoke the function.
http://s13.postimg.org/9ohuj9d7r/exception.png
It says, "InvalidOperationException was not managed, Invoke or BeginInvoke cannot be called on a control until the window handle has been created"
You should use a command pattern and put the command classes into a queue from one thread, and let the other thread read from it.
You will need to prevent this.Invoke() from being called unless the form's window has been created.
The easiest way to do this is to override OnLoad() and set a flag:
private bool isLoaded;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Volatile.Write(ref isLoaded, true);
}
Then check the flag before you invoke:
public void RXTask()
{
FormUpdater frmUpdt = new FormUpdater(Form1.GUIupdate);
//other procedures and a loop containing the invoke...
if (Volatile.Read(ref isLoaded))
{
this.Invoke(frmUpdt, new object[]
{
devnum, rpm,
current,
temperature
});
}
}
(If your version of .Net doesn't have Volatile.Read()/Volatile.Write(), declare the flag as volatile instead.)
Can anyone explain or show why my event handler doesn't update my Windows Form textbox? I have put the event handler in the UI thread to update a textbox in my GUI window. An EventLaunch method in my UI Thread #1 SetOperation class initiates an event. The UI Thread #1 SetOperation class, OnChDetDisplay event handler completes but the Window Form textbox doesn't update to the assigned value. What am I missing to tie the event and handler to updating the textbox?
Thanks for any help anyone can share,
Below is some code:
// Class runs in Thread #2: Prepares message data for Windows Form GUI display and passes to UI Thread #1
public class Aag_PrepDisplay
{
private Aag_PrepDisplay mAagPrep;
public Aag_PrepDisplay AagPrep
{
get { return mAagPrep; }
set { mAagPrep = value; }
}
// Thread #2: prepares message for Windows Form GUI display in UI Thread #1
public void PrepareDisplay(/*stuff*/)
{
mAagPrep = new Aag_PrepDisplay();
// does message prep stuff
SetOperation setOp1 = new SetOperation();
setOp1.FireEvent(mAagPrep); // call to UI Thread #1 method to fire event to update GUI; passes object with data
}
}
// UI Thread #1 class is the Windows Form. Displays and updates all textboxes.
public partial class SetOperation : Form
{
public event Action<object> OnChDet; // declared delegate object event that passes an object
public SetOperation()
{
InitializeComponent();
OnChDet += chDetDisplayHandler; // hooks handler to event
}
// Thread #1: accepts object w/data from Thread #2; Fires an event to update GUI Textbox(s)
private void FireEvent(Aag_PrepDisplay aagPrep)
{
OnChDet(aagPrep);
}
// UI Thread #1 event handler.
public void chDetDisplayHandler(object name)
{
// **** Problem: event is triggered and value assigned, but doesn't update the GUI window Textbox ********
actFreqChan1.Text = "402.5"; // this is only a test to see if event handler will update Textbox
// Next step: updateAll(name); // pass the object from Aag_PrepDisplay class
}
//Thread #1: update GUI Textbox values
public void updateAll(object name)
{
// this is where data from the Thread #2 AagPrep object will assign and update Textbox values
}
}
Put a breakpoint on the problem line and tell us what you see.
Probably it won't get called and the problem is upwards, in the event infrastructure.
If it gets called, the problem is in the Text field's setter.
In both cases, the defect is not where you think it is.
I'd simplify the code. Probably I'm missing something but I'm giving this a try.
public partial class SetOperation : Form
{
public event Action<object> OnChDet;
public SetOperation()
{
InitializeComponent();
OnChDet += chDetDisplayHandler;
}
private void chDetDisplayHandler(object name)
{
ActFreqChan1.Text = "402.5";
}
}
You can then fire the event simply with:
mySetOperationInstance.OnChDet(myNameObject);
The question is WHO will fire the event? This is up to you to find out.
You'll have to put the above line somewhere.
As far as I can tell, you don't need to have:
ChanEventArg;
ChDetHandler;
Aag_DisplayEvent (this looks completely wrong);
EventLaunch() method.
The only thing you should care about is:
having an event;
attaching a handler;
invoking it with parameters when need be.
Do this: make a backup copy of your code and try to simplify.
If it doesn't help I'm sorry, revert to your backup. Otherwise you have done way too much and somewhere you've lost your bearings about how exactly the event is dispatched.
Probably the event handler throws an Exception that doesn't emerge to the UI and remains latent. The following code will prevent other threads than the one creating the control from throwing exceptions:
The official reference: MSDN on InvokeRequired
Similar question: Using InvokeRequired vs control.InvokeRequired
The longer explanation but really good: MSDN tutorial on Thread-Safety in WinForms
Wrap the thread safety protection (InvokeRequired and so on) around this assignment inside the event handler:
actFreqChan1.Text = "402.5";
I hope this will help you out. Otherwise you can still come back here.
Thanks pid. I went back and recreated the code to update the Textbox, when the event is fired from within the SetOperation() of Thread 1. The event handler updates the Textbox. I then tried to call a Thread 1 method from PrepareDisplay() of Thread 2 and fire the event from the Thread 1 method. The event handler doesn't update the Textbox. Next, I added the safe thread-call code to Thread 1 SetOperation class. The Textbox doesn't update with the safe thread-call code. I took it right out of the MSDN tutorial. It was hard to follow the code flow when I stepped thru it. It jumped back and forth between methods. It appeared the InvokeRequired gave a false. In either case, the Textbox should be updated to 402.5. Do you see something that I misplaced or other missing code?
Below is the entire code that I simulated. Thanks again for your willingness to tutor me on some of this.
namespace TstTxtBoxUpdate
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Aag_PrepDisplay aag_Prep1 = new Aag_PrepDisplay();
Thread AagPrepDisplayThread = new Thread(new ThreadStart(aag_Prep1.PrepareDisplay));
AagPrepDisplayThread.Start();
while(!AagPrepDisplayThread.IsAlive)
;
Thread.Sleep(1000);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SetOperation());
}
}
}
namespace TstTxtBoxUpdate
{
// Thread 1: UI
public partial class SetOperation : Form
{
private string text;
public event Action<object> OnChDet;
delegate void SetTextCallback(string text);
private Thread demoThread = null;
public SetOperation()
{
InitializeComponent();
OnChDet += chDetDisplayHandler;
}
public void FireEvent(Aag_PrepDisplay aagPrep)
{
OnChDet(mName);
}
private void chDetDisplayHandler(object name)
{
this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe));
this.demoThread.Start();
}
private void ThreadProcSafe()
{
this.SetText("402.5");
}
private void SetText(string text)
{
if(this.actFreqChan1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
// TextBox NOT updated when event called from FireEvent() that was called from Thread 2 PrepareDisplay()
// TextBox IS updated when event called from Thread 1: SetOperation() or FireEvent()
this.actFreqChan1.Text = text;
}
}
}
}
namespace TstTxtBoxUpdate
{
// Thread 2: Data prepare
public class Aag_PrepDisplay
{
#region Fields
private Aag_PrepDisplay mAagPrep;
#endregion Fields
#region Properties
public Aag_PrepDisplay AagPrepDisp;
public Aag_PrepDisplay AagPrep
{
get { return mAagPrep; }
set { mAagPrep = value; }
}
#endregion Properties
#region Methods
public void PrepareDisplay()
{
mAagPrep = new Aag_PrepDisplay();
SetOperation setOp1 = new SetOperation();
setOp1.FireEvent(mAagPrep); // calls Thread 1 method that will fire the event
}
#endregion Methods
}
}
I came across a situation which puzzled me at work today which I have simplified in the following code. This code builds and throws no exceptions during debug.
Suppose I have a WinForms app. In my main UI thread I spin off another thread which instantiates an object which in turn holds reference to a control (label1 in my example). I then call a method on my object (SetLabelText) which passes it's execution back onto the UI thread if required.
What stumped me was how, when we are back in the UI thread and executing SetLabelText, is .net CLR able to access the labelText variable when we are executing on a thread (ie the UI thread) which did not create the instance of Thing.
public partial class Form1 : Form
{
delegate void DoSomethingDelegate();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var t = new Thread(DoSomethingWithThing);
t.Start();
}
private void DoSomethingWithThing()
{
var thing = new Thing(this.label1);
thing.SetLabelText();
}
}
public class Thing
{
private Label label;
private string labelText = "new value";
delegate void SetLabelTextDelegate();
public Thing(Label label)
{
this.label = label;
}
public void SetLabelText()
{
if (this.label.InvokeRequired)
{
var setLabelDel = new SetLabelTextDelegate(SetLabelText);
this.label.Invoke(setLabelDel);
}
else
{
this.label.Text = this.labelText;
}
}
}
References to objects are available on any thread.
Threads are not sand-boxed from each other. They share resources unless you explicitly create non-shared resources.
Threads are execution contexts. Think of your application as a kitchen and each thread as a chef. They can work at the same time but if two of them try to use the same knife at the same time, things get messy. This is why c# has the lock keyword and other synchronization mechanisms.
WinForms has restrictions on access to controls because of the way WinForms renders.
I have three class, tow of them was UI class, in the mainForm class, I start a new form by execute
new LoginForm.ShowDialog();
in the LoginForm class, I write code about log in and log out, when the use loged in, I start a new thread to check if something need to be done,and update the databases; and here is the question, I don't know how to update a label that in the MainForm
I search this question and they told me I should to use Delegate.but it really puzzled me a lot cause they don't in a same class so I don't know how to use Delegate cross thread and cross different
Until now, my code is like this
MainForm.cs:
public partial class MainForm : Form
public delegate void testDelegate();
public MainForm()
{
InitializeComponent();
}
public void msg(string s)
{
label.Test = s;
}
}
LoginForm.cs:
JobDoer jD = new JobDoer();
Thread t2 = new Thread(new ThreadStart(jD.run));
t2.Start();
JobDoer:
public void run()
{
//tD();
tD = new MainForm.testDelegate(MainForm.msg);
//this.
Thread.Sleep(1000);
return;
}
what should I do next?
Taken from: Best Way to Invoke Any Cross-Threaded Code?
You also could use an extension method and lambdas to make your code much cleaner.
using System.ComponentModel;
public static class ISynchronizeInvokeExtensions
{
public static void InvokeEx<T>(this T #this, Action<T> action) where T : ISynchronizeInvoke
{
if (#this.InvokeRequired)
{
#this.Invoke(action, new object[] { #this });
}
else
{
action(#this);
}
}
}
So now you can use InvokeEx on any ISynchronizeInvoke and be able to access the properties and fields of implementing class.
this.InvokeEx(f => f.listView1.Items.Clear());
For this kind of thing theres the
System.ComponentModel.BackgroundWorker class.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-implement-a-form-that-uses-a-background-operation.
It handles the thread sync for you, plus it has a usefull prgress update event