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.)
Related
Relatively new to C# and coding in general (first post here). I have a WinForms local application where some information is displayed to the user in a ReadOnly(true) RichTextBox. Almost all my classes need to send information to that RichTextBox. To simplify this process, I created a method inside a static class that uses a locked delegate to send the information to that RichTextBox. Here is a sample:
static class MyClass
{
public delegate void MessageReceivedEventHandler(string message);
public static event MessageReceivedEventHandler messageReceivedEventHandler;
public static void MessageBox(string message)
{
lock (messageReceivedEventHandler)
{
//Thread.Sleep(20);
messageReceivedEventHandler?.Invoke(message);
}
}
}
partial class MyForm : Form
{
public MyForm()
{
MyClass.messageReceivedEventHandler += OnMessageReceived;
}
private void OnMessageReceived(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
private void Button1_click()
{
MyClass.MessageBox("This should be working!");
//Add more work here...
}
}
The code above would simply print "This should be working!" inside the RichtTextbox.
The problem is the text from richTextBox1 sometimes becoming empty. This issue seems to appear when the MessageBox method is being called in rapid succession. My assumption was that since I have diffent Tasks running at the same time (in other parts of my code), it probably is two Tasks attempting to use the same static ressource, hence the use of Lock. But I still have the issue.
Adding the Thread.Sleep(20) seems to fix the problem, but that is far from elegant/robust. It starts breaking up again when the time inside Sleep is <10ms.
Edit 1:
To clarify what I mean by "string becoming empty", it means the text from richTextBox1 is == "" at some points, which should not happen since the code is always inserting the text, not replacing it. The OnMessageReceived method is the only place where action is taken on the RichTextBox text.
Edit 2:
I saw many questions related to the other tasks running. First, yes it is a multi-threaded application. The only relation between those tasks and my main form is the "print" function I wrote above. To give more context, this application is used to control the position of stepper motors relative to an electrical signal. When doing so, I need to print important information in my main form. This is why losing the information in my RichTextBox (where I print the information) is an issue. The possible reason of why I am losing the text inside that RichTextBox should be the focus of this thread.
Keep in mind that this is a personnal side project, and not a large scale application.
Thanks,
Laurent
There are multiple problems in your code.
First, you should not lock on a public object, since that allows other threads to lock on the same object, risking interlocking your threads. Second, your symptoms suggest multiple threads are trying to access the ressources. Rather than depending on complex thread locking code, you'd rather schedule UI operations on the UI context, which will allow calling adding message from background tasks.
The best way to do that is to that is by using Control.BeginInvoke()
You can't copy your form instance everywhere, so we'll expose a static method. You could make the class a singleton, but if you need multiple instances that won't work. I'll give a more versatile example. When the static method is called, you don't have access to the form instance anymore, so we'll use IOC pattern with an event and delegate.
Let's make a private static event that all instances will register a callback to in the constructor. When the static method raises the static event, all instances callback will be called. The callback will schedule a modification of its text box.
partial class MyForm : Form
{
private class MessageWriteRequestedEventArgs : EventArgs
{
public string Message { get; }
public MessageWriteRequestedEventArgs(string message)
{
Message = message;
}
}
private static event EventHandler<MessageWriteRequestedEventArgs> MessageWriteRequested;
public MyForm()
{
MessageWriteRequested += OnMessageWriteRequested;
}
public static void WriteMessage(string message)
{
MessageWriteRequested?.Invoke(this, new MessageWriteRequestedEventArgs(message));
}
private void OnMessageWriteRequested(object sender, MessageWriteRequestedEventArgs e)
{
richTextBox1.BeginInvoke(() => WriteMessageSafe(e.message));
}
private void WriteMessageSafe(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
private void Button1_click()
{
// you're on ui context, you're safe to access local ui resources
WriteMessageSafe("This should be working!");
// if you have multiple MyForm instances, you need to use the event
WriteMessage("Broadcasting my tralala");
}
}
If you need to write to the textbox from anywhere else :
// do stuff
MyForm.WriteMessage("Ho Ho Ho !");
.NET already includes a class for reporting progress (or any other information) from an asynchronous operation in a thread-safe manner, Progress< T>. It doesn't need locking and even better, it decouples the sender and receiver. Many long-running BCL operations accept an IProgress<T> parameter to report progress.
You haven't explained what's going on in the form, or what task is reporting the data. Assuming the producer is another method in the same form, you could create a Progress<T> instance in the same method that starts the async operation, eg :
async void Button1_Click()
{
var progress=new Progress<string>(ReportMessage);
ReportMessage("Starting");
await Task.Run(()=>SomeLongOp(progress));
ReportMessage("Finished");
}
void SomeLongOp(IProgress<string> progress)
{
for(int i=0;i<1000000;i++)
{
...
progress.Report($"Message {i}");
...
}
}
void ReportMessage(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
By using IProgress< T>, the SomeLongOp method isn't tied to a specific form or global instance. It could easily be a method on another class
Publishing lots of messages
Let's say you have a lot of workers, doing a lot of things, eg monitoring a lot of devices, and want all of them to publish messages to the same Log textbox or RTF box. Progress< T> "simply" executes the reporting delegate or event handler on its original sync context. It doesn't have an asynchronous Report method, nor can it queue messages. In a really high-traffic environment, the synchronization switch can delay all workers.
The built-in answer to this is to use one of the pub/sub classes like ActionBlock< T> or a Channel.
An ActionBlock< T> processes the messages in its input queue in order, using a worker task that runs on the ThreadPool by default. This can be changed by specifying a different TaskScheduler in its execution options. By default, its input queue is unbounded.
One could use an ActionBlock to receive messages from multiple workers and display them on a textbox. The block can be created in the constructor, and passed to all workers as an ITargetBlock<T> interface :
ActionBlock<string> _logBlock;
public MyForm()
{
var options=new ExecutionDataFlowBlockOptions {
TaskScheduler=TaskScheduler.FromCurrentSynchronizationContext();
};
_block=new ActionBlock<string>(ReportMessage,options);
}
Now the fun begins. If the workers are created by the form itself, the workers can publish to the block directly :
public async void Start100Workers_Click(...)
{
var workers=Enumerable.Range(0,100)
.Select(id=>DoWork(id,_block));
await Task.WhenAll(workers);
}
async Task DoWork(int id,ITargetBlock<string> logBlock)
{
.....
await logBlock.SendAsync(message);
...
}
Or the block could be exposed through a public property, so other classes/forms in the application can post to it.
public ITargetBlock<string> LogBlock=>_block;
I'm going to show a simple way to do what I think you're after.
I started with a .NET Core 3.1 Win forms application. I added a rich text control to the form. I added a button to the form.
I added a TaskCompletionSource as a instance property - this will be used to control the tasks acting as workers which you described.
CancellationTokenSource sharedCancel = new CancellationTokenSource();
I created an interface to represent something that accepts messages as you described:
public interface IMyMessageSink
{
Task ReceiveMessage(string message);
}
I made my form support this interface.
public partial class Form1 : Form, IMyMessageSink
The ReceiveMessage method looks like this:
public Task ReceiveMessage(string message)
{
if(this.sharedCancel == null || this.sharedCancel.IsCancellationRequested)
return Task.FromResult(0);
this.Invoke(new Action<Form1>((s) => this.richTextBox1.Text = this.richTextBox1.Text.Insert(0, $"{message}\n")), this);
return Task.FromResult(0);
}
You'll see the Invoke handles the synchronization back to the UI thread.
This should probably use BeginInvoke and then convert the APM to async tasks which you can read about here. But for an SO answer the above simple code will suffice.
Also note there's no error handling. You'll want to add that to your generator and to the button handler.
Next I created a class to represent something that creates messages. This class takes the interface created and the cancellation token. It looks like this:
public class MyMessageGenerator
{
CancellationToken cancel;
IMyMessageSink sink;
public MyMessageGenerator(CancellationToken cancel, IMyMessageSink sink)
{
this.cancel = cancel;
this.sink = sink;
}
public async Task GenerateUntilCanceled()
{
try
{
while (!this.cancel.IsCancellationRequested)
{
await sink.ReceiveMessage(this.GetHashCode().ToString());
await Task.Delay(5000, this.cancel);
}
}
catch (OperationCanceledException)
{ }
}
}
In the button handler we create the message generators.
async void button1_Click(object sender, EventArgs e)
{
if (null == this.sharedCancel)
return;
await Task.Run(() => new MyMessageGenerator(this.sharedCancel.Token, this).GenerateUntilCanceled());
}
Finally I added an override for the form closing event:
protected override void OnClosing(CancelEventArgs e)
{
if (null != this.sharedCancel)
{
this.sharedCancel.Cancel();
this.sharedCancel.Dispose();
this.sharedCancel = null;
}
base.OnClosing(e);
}
If the application becomes larger and more complex you would likely benefit by adding services exposed using a DI container. You can read about adding DI to a winforms app here.
I'm developing an MDI application in C# with .NET 4.0.
Each MDI child will be a form with tabs that contains GroupBoxes with a DataGridView.
I implemented a class that is used to manage Threads.
This is the StartNewThread method in my ThreadManager class
public string StartNewThread(ThreadStart threadMethod, string threadName)
{
try
{
Thread thread = new Thread(() => threadMethod());
thread.Name = threadName + " (" + _threadCount++.ToString("D4") + ")";
thread.Start();
_threadList.Add(thread.Name, thread);
return thread.Name;
}
catch (Exception ex)
{
//Log and manage exceptions
}
return null;
}
To create the DataGridViews I used some Wizard component from Oracle Developer Tools for VS library. So, after creating the DataSource and so the DataSet, then I used drag&drop from DataSource tree to drag tables and automatically create DataGridViews.
This is the actual working code, behind the child form, automatically created.
public partial class ScuoleNauticheForm : Form
{
public ScuoleNauticheForm()
{
InitializeComponent();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.PERSONALE' table. You can move, or remove it, as needed.
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.NATANTI' table. You can move, or remove it, as needed.
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.SCUOLE' table. You can move, or remove it, as needed.
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
}
What I want to do now is manage all the load/query/insert/update/delete operations on separated threads. For now I tried to create a new Thread to load the data.
This i what I tried.
public partial class ScuoleNauticheForm : Form
{
private readonly ThreadManager _threadManager;
public ScuoleNauticheForm()
{
InitializeComponent();
_threadManager = ThreadManager.GetInstance();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
_threadManager.StartNewThread(LoadData, "LoadData");
}
#region DataBind
private void LoadData()
{
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.PERSONALE' table. You can move, or remove it, as needed.
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.NATANTI' table. You can move, or remove it, as needed.
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
// TODO: This line of code loads data into the 'dEVRAC_NauticheDataSet.SCUOLE' table. You can move, or remove it, as needed.
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
#endregion
}
It works only for half... There's no errors or exceptions, but if I load data that way, using a different Thread, the DataGridviews doesn't update and I don't see any data when opening the form, even if I move or resize it. Otherwise, using the automatically generated code, the DataGridViews are populated correctly.
But, since the wizard also add a navigation bar to the form to navigate through records, I noticed that it works, because it counts the correct number of records and I can use the arrows (first, previous, next, last) to move across records.
Here is an image showing my form.
See the navigation bar that is showing the correct number of total records (14) and allows me to navigate through them.
Do I need to use delegates? If so, I think it would be a mess... how many delegates should I create and for those methods? Or is there another solution?
-- UPDATE 1 --
I know that UI threads are automatically managed by .NET and so the programmer don't need to manage them with code. So, should it be a problem of synchronization with the .NET UI thread built in management? Maybe my thread launched by Form.Load() interferes with the UI thread managed by the .NET?
-- UPDATE 2 --
I tried to implement the solution proposed by faby. I replaced my Thread logic with Task logic. The behaviour of the application is the same, so everything that was working with Thread is now working also with Task. But the problem still remains. Since I'm on .NET 4.0 and not .NET 4.5, I could not use async and await. So I don't know if with that approach the UI will work correctly or not.
Any other suggestion valid for .NET 4.0?
do you consider the option of BackgroundWorker Class ?
implementing DoWork and ProgressChanged you can do in DoWork what you are doing in background thread and in ProgressChanged you can update the UI
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//long running task
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//update the UI components
}
update 1
another solution could be something like this
public Task LoadDataAsync()
{
return Task.Factory.StartNew( () =>
{
//code to fill your datagridview
});
}
then
public async Task ChangeUIComponents()
{
await LoadDataAsync();
// now here you can refresh your UI elements
}
update 2
to use async/await with framework 4.0 try with this NugetPackage (Microsoft.Bcl.Async)
I finally found a solution without using async/await and other libraries.
The problem was that I was executing the Fill() method of TableAdapter inside a new Task and so I needed to use InvokeRequired to set the binding source data source to the DataTable within the right thread.
So I used delegates. I changed the method called on the new Task and make it call 3 other methods (one for each DataGridView to fill) that call Fill() implementing the InvokeRequired check.
Now I see the creation of the UI and then, after a couple of seconds, the asynchronous filling of the DataGridViews.
This article was useful: Load data from TableAdapter async
Thanks to #faby for the suggestion to use Task instead of Thread. It was not the solution but it is a better way to do Threading.
Here's the final working code.
public partial class ScuoleNauticheForm : Form
{
private readonly TaskManager _taskManager;
public ScuoleNauticheForm()
{
InitializeComponent();
_taskManager = TaskManager.GetInstance();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
_taskManager.StartNewTask(LoadData);
}
#region Delegates
public delegate void FillPersonaleCallBack();
public delegate void FillNatantiCallBack();
public delegate void FillScuoleCallBack();
#endregion
#region DataBind
private void LoadData()
{
FillPersonale();
FillNatanti();
FillScuole();
}
public void FillPersonale()
{
if (PersonaleDataGridView.InvokeRequired)
{
FillPersonaleCallBack d = new FillPersonaleCallBack(FillPersonale);
Invoke(d);
}
else
{
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
}
}
public void FillNatanti()
{
if (NatantiDataGridView.InvokeRequired)
{
FillNatantiCallBack d = new FillNatantiCallBack(FillNatanti);
Invoke(d);
}
else
{
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
}
}
public void FillScuole()
{
if (ScuoleDataGridView.InvokeRequired)
{
FillScuoleCallBack d = new FillScuoleCallBack(FillScuole);
Invoke(d);
}
else
{
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
}
#endregion
}
-- Update 1 --
If the methods to call by the new Task are void and without any parameters, you can simplify a bit the above code by using Invoke((MethodInvoker) MethodName). The behaviour of the application is the same.
Here's the simplified version of the code.
public partial class ScuoleNauticheForm : Form
{
private readonly TaskManager _taskManager;
public ScuoleNauticheForm()
{
InitializeComponent();
_taskManager = TaskManager.GetInstance();
}
private void ScuoleNauticheForm_Load(object sender, EventArgs e)
{
_taskManager.StartNewTask(LoadData);
}
#region DataBind
private void LoadData()
{
// Since Fill Methods are void and without parameters,
// you can use the Invoke method without the need to specify delegates.
Invoke((MethodInvoker)FillPersonale);
Invoke((MethodInvoker)FillNatanti);
Invoke((MethodInvoker)FillScuole);
}
public void FillPersonale()
{
this.PersonaleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.PERSONALE);
}
public void FillNatanti()
{
this.NatantiTableAdapter.Fill(this.DEVRAC_NauticheDataSet.NATANTI);
}
public void FillScuole()
{
this.ScuoleTableAdapter.Fill(this.DEVRAC_NauticheDataSet.SCUOLE);
}
#endregion
}
I’m using a separate class that instantiates different barcode objects depending on the type of machine, which raise events once a barcode is read.
So to transmit it to the form, I also raise an UNIQUE event ‘myEvent’ that all my forms listens, but inside the form I have to use this.Invoke((Action) mymethod(argument)); to be able to update the components at the user interface, as long as the handler to myEvent is executed on another thread different from that of the form.
So I have this:
My class -> barcode.readed(..) is triggered, then from its handler, I Raise myEvent, that is captured on my Form, and from the handler for myEvent on my form, I execute the this.Invoke…
I know this is a somewhat standard procedure, but I’d like to get rid of that Invoke, and instead, calling directly to the myMethod(argument); function.
I think that this is related to make my class thread safe, but I don’t know how to implement that in my case.
In fact, if I use the original manufacturer barcode.readed() event from inside the form it does not need to call the invoke, as long as this is thread safe, but I don’t know how to mimic that, and I really need to wrap all the different barcode handlers inside a different project, for my forms to use only one ‘myEvent’ that returns the desired barcode, and thus, not repeating code.
Thanks in advance,
Roger Tranchez
If you inherit your class from control (basically create a new custom control), you can handle events on the UI thread without using Invokes as the control (your barcode reader class) is part of the UI thread.
Sounds like you are using a worker thread for reading barcodes, to keep the UI responsive. And the barcode object just runs on whichever thread created it.
You can centralise event handling (to avoid repeating code) and read barcodes on a worker thread as follows:
Wrap the barcode object inside a custom object/library MyBarcodeReader that exposes MyEvent.
In the constructor for MyBarcodeReader, capture the current SynchronizationContext to a class field syncContext. This will be your UI's SynchronizationContext if your form constructs MyBarcodeReader.
When you activate MyBarcodeReader (e.g. MyBarcodeReader.Execute), create the barcode object on a worker thread.
When you need to raise MyEvent, call syncContext.Send (this will be on the worker thread), passing a delegate whose purpose is to raise MyEvent. syncContext.Send will synchronise to the UI thread (like Control.Invoke). The code below illustrates this.
public class MyBarcodeReader
{
private readonly SynchronizationContext syncContext;
// Handler for barcode object's Readed event.
private void Barcode.Readed(Object sender, Event e)
{
// Block the worker thread to synchronize with the thread associated
// with SynchronizationContext.
syncContext.Send(SyncMyEvent, (Object)e);
}
// Raises MyEvent on the thread associated with SynchronizationContext,
// usually a UI thread.
private void SyncMyEvent(Object o)
{
if (MyEvent != null)
{
MyEvent((Event)o);
}
}
// Constructor.
public MyBarcodeReader()
{
syncContext = SynchronizationContext.Current;
}
}
The approach here will block the worker thread (same as Control.Invoke) but not block the UI thread. If you have one or more forms that subscribe to MyEvent, they don't need to use Control.Invoke; they don't even need to know about the worker thread.
There are some excellent online references on SynchronizationContext, see CodeProject and MSDN magazine.
I've found a solution here: Basically it passes over the form control to the class constructor, and then, inside that class it uses the form.Invoke to fire the event from the form ui thread.
CLASS:
using System;
using System.Windows.Forms;
using System.Threading;
namespace ThreadTest
{
public class WorkerClass
{
private Thread thr;
// UI control for update
public Control UIControl { get; set; }
public delegate void StatusUpdate(DateTime dateTime, string message);
public event StatusUpdate OnStatusUpdate;
// Starts thread
public void Start()
{
thr = new Thread(new ThreadStart(MainWorker));
thr.Start();
}
// Main thread worker
public void MainWorker()
{
int i = 0;
while (true)
{
string message = string.Format("Value of i={0}", i++);
FireStatusUpdate(DateTime.Now, message);
Thread.Sleep(1000);
}
}
// Fire thread safe event
private void FireStatusUpdate(DateTime dateTime, string message)
{
// UIControl is set and OnStatusUpdate has subscriber
if (UIControl != null && OnStatusUpdate != null)
{
if (UIControl.InvokeRequired)
{
UIControl.Invoke(new StatusUpdate(FireStatusUpdate),
new object[] { dateTime, message });
return;
}
OnStatusUpdate(dateTime, message);
}
}
}
}
FORM:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
WorkerClass worker = new WorkerClass();
// add event handler
worker.OnStatusUpdate += new WorkerClass.StatusUpdate(worker_OnStatusUpdate);
// add UI control to invoke
worker.UIControl = this;
worker.Start();
}
void worker_OnStatusUpdate(DateTime dateTime, string message)
{
label1.Text = dateTime.ToLongTimeString();
label1.Text += " " + message;
}
}
In my case, I've changed the type of event, from StatusUpdate to
EventHandler<MyEventArgs>
, being MyEventArgs this class:
public class MyEventArgs : EventArgs
{
public string MyString { get; set; }
}
Thank you !
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 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";
}
}