C# windows forms - question about cross thread events - c#

Let's say i have created a class name myClass and this class has a property named myValue with any type, doesn't matter, like:
class myClass
{
public delegate void OverTheLimitDlg(int arg);
public event OverTheLimitDlg OverTheLimit;
public myClass()
{
myValue = 0;
}
private int myvalue = 0;
public int myValue
{
get { return myvalue;}
set
{
myValue = value;
if(value > 5)
OvertheLimit(value);
}
}
}
I have a winforms label named myLabel on form and i create an object typed myClass at Form Load event, subscribe its OverTheLimit event and start backgroundworker:
myClass myObj;
private void Form_Load(object sender, EventArgs e)
{
myObj = new myClass();
myObj.OverTheLimit += SubsMethod;
backgroundworker.RunWorkerAsync();
}
private void backgroundworker_DoWork(...)
{
myObj.myValue = 10;
//Some expressions.
}
private void SubsMethod(int someInt)
{
myLabel.Text = "Oh it's over the limit!";
}
Summary: i create a class that an object instantiated from it can fire an event. I make the object fire the event in a thread and it runs a method that affects a GUI object, an object created and runs at another thread. I didn't try it ever. What is going to happen in a situation like this? Does it cause error? Thanks.

What is going to happen in a situation like this?
myLabel.Text = "Oh it's over the limit!";
This line will throw an InvalidOperationException when it tries to edit the myLabel from the BackgroundWorker thread. WinForms controls must be changed from the thread that they are created on, this is why Control.InvokeRequired exists.
You can use the following modified version of SubsMethod() which will check if the event handler is running on another thread and then invoke the label change on the GUI thread if necessary.
private void SubsMethod(int someInt)
{
if (myLabel.InvokeRequired) {
myLabel.Invoke(new MethodInvoker(()=>SubsMethod(someInt)));
return;
}
myLabel.Text = "Oh it's over the limit!";
}

Related

Accessing controls in Form from a derived BackgroundWorker class results in cross-thread error

I'm working on writing a class which is derived from the System.ComponentModel.BackgroundWorker class. The reason I am doing so in my project is that I need a lot of information to be returned in different types of status update events, depending on which event is raised. When attempting to update any of the controls the main form from any of my update events, I am getting the following error:
System.InvalidOperationException: 'Cross-thread operation not valid:
Control '' accessed from a thread other than the thread it was created
on.'
The first control that I am attempting to update is a ToolStripStatusLabel, which does not have an .Invoke() method. I have created minimally verifiable example below. To recreate the error, simply create a new Windows Forms App (.NET Framework) project targeted to .NET 4.8 and copy paste the following code into the Form1.cs file:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private StatusStrip statusStrip1;
private ToolStripStatusLabel toolStripStatusLabel1;
private ToolStripProgressBar toolStripProgressBar1;
private Button button1;
private MyBGW myBGW;
public Form1()
{
InitializeComponent();
this.statusStrip1 = new StatusStrip();
this.toolStripStatusLabel1 = new ToolStripStatusLabel() { Text = "Starting Text" };
this.toolStripProgressBar1 = new ToolStripProgressBar();
this.button1 = new Button();
this.myBGW = new MyBGW();
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.toolStripStatusLabel1, this.toolStripProgressBar1});
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.button1);
this.button1.Click += Button1_Click;
this.myBGW.OnMyBGW_StatusChanged += MyBGW_OnMyBGW_StatusChanged;
}
private void Button1_Click(object sender, EventArgs e) { myBGW.RunWorkerAsync(); }
private void MyBGW_OnMyBGW_StatusChanged(object sender, MyBGW.MyBGW_StatusChanged_EventArgs e)
{
// The following two lines will throw the cross-threading exception
this.toolStripStatusLabel1.Text = e.StatusText;
if (e.PBarStyle != MyBGW.pBarStyles.NoChange) { this.toolStripProgressBar1.Style = (ProgressBarStyle)e.PBarStyle; }
}
}
public class MyBGW : BackgroundWorker
{
public enum pBarStyles { Block = 0, Continuous = 1, Marquee = 2, NoChange = -1 }
public delegate void MyBGW_StatusChanged_EventHandler(object sender, MyBGW_StatusChanged_EventArgs e);
public event MyBGW_StatusChanged_EventHandler OnMyBGW_StatusChanged;
public class MyBGW_StatusChanged_EventArgs : EventArgs
{
public string StatusText;
public pBarStyles PBarStyle;
public MyBGW_StatusChanged_EventArgs(string statusText, pBarStyles pBarStyle)
{
this.StatusText = statusText; this.PBarStyle = pBarStyle;
}
}
public new void RunWorkerAsync() { base.RunWorkerAsync(); }
private void myBGW_DoWork(object sender, DoWorkEventArgs e)
{
OnMyBGW_StatusChanged(this, new MyBGW_StatusChanged_EventArgs(DateTime.Now.ToString(), pBarStyles.Marquee));
System.Threading.Thread.Sleep(10000);
OnMyBGW_StatusChanged(this, new MyBGW_StatusChanged_EventArgs("Done", pBarStyles.Continuous));
}
public MyBGW() { base.DoWork += new DoWorkEventHandler(this.myBGW_DoWork); }
}
}
My best guess is that I am raising or consuming the event incorrectly which is causing the code to still be run on the worker thread instead of the main/UI thread, but I'm coming up short in my research on what I'm missing.
EDIT: this question is not related to Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on as it is not directly relying on a BackgroundWorker but is rather attempting to add additional events to a derived class, of which the addition of those events are causing the Cross-Thread exception. Also, the answer does not apply as the control attempting to be updated does not have the .Invoke method as the solution to that question stated.
The problem for this question is in relation to how the event was being raised, which was incorrectly, causing the consumption of that event to be on the wrong thread and raising the cross-thread exception.
The BackgroundWorker.DoWork event handler is supposed to do background work, and it's not intended for interacting with the UI. This handler is invoked on a ThreadPool thread, and interacting with UI components from any thread other than the UI thread is not allowed. The BackgroundWorker class offers two events that are raised on the UI thread¹, the ProgressChanged and the RunWorkerCompleted. You could take advantage of this, by invoking your StatusChanged event on the ProgressChanged event handler (or overriding the OnProgressChanged method), and passing your StatusChangedEventArgs as an argument of the ReportProgress method:
public class MyBGW : BackgroundWorker
{
public enum BarStyles { Block = 0, Continuous = 1, Marquee = 2, NoChange = -1 }
public delegate void StatusChangedEventHandler(object sender,
StatusChangedEventArgs e);
public event StatusChangedEventHandler StatusChanged;
public MyBGW() { this.WorkerReportsProgress = true; }
public class StatusChangedEventArgs : EventArgs
{
public string StatusText;
public BarStyles PBarStyle;
public StatusChangedEventArgs(string statusText, BarStyles pBarStyle)
{
this.StatusText = statusText; this.PBarStyle = pBarStyle;
}
}
protected override void OnDoWork(DoWorkEventArgs e)
{
this.ReportProgress(-1,
new StatusChangedEventArgs(DateTime.Now.ToString(), BarStyles.Marquee));
base.OnDoWork(e);
this.ReportProgress(-1,
new StatusChangedEventArgs("Done", BarStyles.Continuous));
}
protected override void OnProgressChanged(ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == -1 && e.UserState is StatusChangedEventArgs args)
StatusChanged?.Invoke(this, args);
else
base.OnProgressChanged(e);
}
}
¹ To be precise, the ProgressChanged and RunWorkerCompleted events are raised on the SynchronizationContext.Current which is captured when the BackgroundWorker.RunWorkerAsync is invoked.
Because toolStripStatusLabel1 And toolStripProgressBar1 runs inside a thread other than the main thread, it needs to be Invoke. And since ToolStripStatusLabel And ToolStripProgressBar itself does not have an Invoke method, we use its parent Invoke method.
change MyBGW_OnMyBGW_StatusChanged to :
private void MyBGW_OnMyBGW_StatusChanged(object sender, MyBGW.MyBGW_StatusChanged_EventArgs e)
{
InvokeIfRequired(this, ()=>
{
this.toolStripStatusLabel1.Text = e.StatusText;
});
if (e.PBarStyle != MyBGW.pBarStyles.NoChange)
{
InvokeIfRequired(this, () =>
{
this.toolStripProgressBar1.Style = (ProgressBarStyle)e.PBarStyle;
});
}
}
add InvokeIfRequired method
public void InvokeIfRequired(Control control, MethodInvoker action)
{
if (control.InvokeRequired)
control.Invoke(action);
else
action();
}
As mjwills has stated in the comments of the question, I was not raising the event properly, which was causing the event to be consumed on the same worker thread. After looking at the link for the .NET source code of the BackgroundWorker class, I can see that there is a bit of code, AsyncOperation.Post() that has the method protected virtual void OnStatusChangedin the code below raised in the main thread rather than the worker thread.
public class MyBGW : BackgroundWorker
{
public enum pBarStyles { Block = 0, Continuous = 1, Marquee = 2, NoChange = -1 }
private static readonly object statusChangedKey = new object();
private AsyncOperation asyncOperation = null;
public MyBGW() { base.DoWork += new DoWorkEventHandler(this.myBGW_DoWork); }
public delegate void StatusChanged_EventHandler(object sender, StatusChanged_EventArgs e);
public event StatusChanged_EventHandler StatusChanged
{
add { this.Events.AddHandler(statusChangedKey, value); }
remove { this.Events.RemoveHandler(statusChangedKey, value); }
}
protected virtual void OnStatusChanged(StatusChanged_EventArgs e) { ((StatusChanged_EventHandler)Events[statusChangedKey])?.Invoke(this, e); }
private void StatusReporter(object arg) { OnStatusChanged((StatusChanged_EventArgs)arg); }
public void UpdateStatus(StatusChanged_EventArgs e) { asyncOperation.Post(new System.Threading.SendOrPostCallback(StatusReporter), e); }
public class StatusChanged_EventArgs : EventArgs
{
public string StatusText;
public pBarStyles PBarStyle;
public StatusChanged_EventArgs(string statusText, pBarStyles pBarStyle)
{
this.StatusText = statusText; this.PBarStyle = pBarStyle;
}
}
public new void RunWorkerAsync() { asyncOperation = AsyncOperationManager.CreateOperation(null); base.RunWorkerAsync(); }
private void myBGW_DoWork(object sender, DoWorkEventArgs e)
{
UpdateStatus(new StatusChanged_EventArgs(DateTime.Now.ToString(), pBarStyles.Marquee));
System.Threading.Thread.Sleep(3000);
UpdateStatus(new StatusChanged_EventArgs("Done", pBarStyles.Continuous));
}
}
I don't fully understand the how and why, but it works. Hopefully someone can comment below with a better explanation.

BindingList.Add() doesn't work cross thread even with lock

I am just learn C#/.NET and I met this problem.
So in my solution i have 2 projects: winforms UI and dll with logic. In dll i have BindingList which provides datasource for listBox in UI.
UI:
public partial class Form1 : Form
{
private Class1 _class1;
public Form1()
{
InitializeComponent();
_class1 = new Class1(); // logic class insatce
listBox1.DataSource = _class1.BindingList;
}
private void button1_Click(object sender, EventArgs e)
{
_class1.Add();
}
private void button2_Click(object sender, EventArgs e)
{
_class1.Remove();
}
}
Logic class:
public class Class1
{
public BindingList<string> BindingList { get; set; } = new BindingList<string>() ;
public void Add()
{
var th = new Thread(() =>
{
lock (BindingList)
{
BindingList.Add("1");
}
}) {IsBackground = true};
th.Start();
// works fine
//BindingList.Add("1");
}
public void Remove()
{
if (BindingList.Count > 1)
{
BindingList.RemoveAt(0);
}
}
}
So the problem that if I just Run solution(ctrl + F5) all works fine, but in debug mod(F5) nothing happens when i press button. All answers I found say : "use lock" so i used lock and listbox still not react on adding elements to list. Please help me what i am doing wrong or where i missed something.
PS sorry for my English.
First, to be clear: you may or may not need to use lock here. That would depend on whether there are actually two or more threads accessing the BindingList<T> object concurrently, i.e. literally at the same time (e.g. two or more threads adding items to the list, or one thread adding items while another one is trying to read from the list). In your code example, this does not appear to be the case, and so it wouldn't be necessary. Regardless, the lock statement does something completely different than what is required to address the specific issue you're asking about, and in any case only works when threads use lock cooperatively on the same object (if only one thread calls lock, that doesn't help).
The basic issue is that the ListBox can't respond to events from BindingList when those events are raised on other than the UI thread. Typically, the solution to this would be to call Control.Invoke() or similar to execute the list-modifying operation in the UI thread. But in your case, the class that owns the BindingList isn't a UI object and so doesn't naturally have access to the Control.Invoke() method.
IMHO, the best solution preserves the UI thread knowledge in the UI object(s) involved. But doing it this way would require having the Class1 object hand over at least some of the control over the list to that UI object. One such approach would involve adding an event to the Class1 object:
public class AddItemEventArgs<T> : EventArgs
{
public T Item { get; private set; }
public AddItemEventArgs(T item)
{
Item = item;
}
}
public class Class1
{
public EventHandler<AddItemEventArgs<string>> AddItem;
public BindingList<string> BindingList { get; set; }
public Class1()
{
// Sorry, old-style because I'm not using C# 6 yet
BindingList = new BindingList<string>();
}
// For testing, I prefer unique list items
private int _index;
public void Add()
{
var th = new Thread(() =>
{
string item = (++_index).ToString();
OnAddItem(item);
}) { IsBackground = true };
th.Start();
}
public void Remove()
{
if (BindingList.Count > 1)
{
BindingList.RemoveAt(0);
}
}
private void OnAddItem(string item)
{
EventHandler<AddItemEventArgs<string>> handler = AddItem;
if (handler != null)
{
handler(this, new AddItemEventArgs<string>(item));
}
}
}
Then in your Form1:
public partial class Form1 : Form
{
private Class1 _class1;
public Form1()
{
InitializeComponent();
_class1 = new Class1(); // logic class instance
_class1.AddItem += (sender, e) =>
{
Invoke((MethodInvoker)(() => _class1.BindingList.Add(e.Item)));
};
listBox1.DataSource = _class1.BindingList;
}
private void button1_Click(object sender, EventArgs e)
{
_class1.Add();
}
private void button2_Click(object sender, EventArgs e)
{
_class1.Remove();
}
}
A variation on this theme would be to have two different "add" methods in Class1. The first would be the one you have now, which ultimately uses a thread. The second would be the one that would be required to be called from the UI thread, and which would actually add the item. In the AddItem event handler in the form, instead of adding the item directly to the list, the second "add" method would be called to do that for the form.
Which is best depends on how much abstraction you want in your Class1. If you're trying to hide the list and its operations from other classes, then the variation would be better. But if you don't mind updating the list from somewhere other than in the Class1 code, the code example above should be fine.
An alternative is to make your Class1 object thread-aware, similar to how e.g. BackgroundWorker works. You do this by capturing the current SynchronizationContext for the thread when the Class1 object is created (on the assumption that the Class1 object is created in the thread where you want to return to, to add an item). Then when adding an item, you use that context object for the add.
That looks like this:
public class Class1
{
public BindingList<string> BindingList { get; set; }
private readonly SynchronizationContext _context = SynchronizationContext.Current;
public Class1()
{
BindingList = new BindingList<string>();
}
private int _index;
public void Add()
{
var th = new Thread(() =>
{
string item = (++_index).ToString();
_context.Send(o => BindingList.Add(item), null);
}) { IsBackground = true };
th.Start();
}
public void Remove()
{
if (BindingList.Count > 1)
{
BindingList.RemoveAt(0);
}
}
}
In this version, no changes to Form1 are needed.
There are lots of variations on this basic scheme, including some which put the logic into a specialized BindingList<T> subclass instead. For example (to name a couple):
Cross-Thread Form Binding - Can it be done?
BindingList<> ListChanged event
Finally, if you want to really hack things together, you can just force the entire binding to reset any time the list has changed. In that case, you would not need to change Class1, but you would need to change Form1:
public partial class Form1 : Form
{
private Class1 _class1;
public Form1()
{
bool adding = false;
InitializeComponent();
_class1 = new Class1(); // logic class instance
_class1.BindingList.ListChanged += (sender, e) =>
{
Invoke((MethodInvoker)(() =>
{
if (e.ListChangedType == ListChangedType.ItemAdded && !adding)
{
// Remove and re-insert newly added item, but on the UI thread
string value = _class1.BindingList[e.NewIndex];
_class1.BindingList.RemoveAt(e.NewIndex);
adding = true;
_class1.BindingList.Insert(e.NewIndex, value);
adding = false;
}
}));
};
listBox1.DataSource = _class1.BindingList;
}
// ...
}
I don't really advise this approach. But if you have no way to change Class1, it's about the best you can do.

How to invoke a control within a class

I have a windows form with a button.
I click the button and it starts a method in a separate class. I start this method in a separate thread.
When this class.method finishes it raises an event back to the windows form class.
When this happens I start another method in that separate class that tells a system.windows.form timer (declared in that class) to be enabled and thus start processing.
But the timer does not start (I did put a break point inside the 'tick' event).
I am assuming that it is because I declared the timer outside of the calling thread right at the start of my code.
Normally, I would use this to invoke a method on the same thread...
this.invoke(mydelegatename, any pars);
But, 'this' cannot be called with an class because unassumingly it is related to the UI thread.
I know this all looks bad architecture and I can easily solve this problem by moving the timer to the UI thread (windows form class).
But, I have forgotten how I did this many years ago and it really is an attempt to encapsulate my code.
Can anyone enlighten me pls?
Thanks
The Code:
[windows class]
_webSync = new WebSync(Shared.ClientID);
_webSync.evBeginSync += new WebSync.delBeginSync(_webSync_evBeginSync);
Thread _thSync = new Thread(_webSync.PreConnect);
_thSync.Start();
private void _webSync_evBeginSync()
{
_webSync.Connect();
}
[WebSync class]
private System.Windows.Forms.Timer _tmrManifestHandler = new System.Windows.Forms.Timer();
public WebSyn()
{
_tmrManifestHandler.Tick += new EventHandler(_tmrManifestHandler_Tick);
_tmrManifestHandler.Interval = 100;
_tmrManifestHandler.Enabled = false;
}
public delegate void delBeginSync();
public event delBeginSync evBeginSync;
public void PreConnect()
{
while (true)
{
if (some condition met)
{
evBeginSync();
return ;
}
}
}
public void Connect()
{
_tmrManifestHandler.Enabled = true;
_tmrManifestHandler.Start();
}
private void _tmrManifestHandler_Tick(object sender, EventArgs e)
{
//NOT BEING 'HIT'
}
You have to call _tmrManifestHandler.Start(); enabling is not enough.
Using a System.Windows.Forms.Timer on another thread will not work.
for more info look here.
Use a System.Timers.Timer instead, be carefull of CrossThreadExceptions if you are using accessing UI elements.
public class WebSync
{
private System.Timers.Timer _tmrManifestHandler = new System.Timers.Timer();
public WebSync(object id)
{
_tmrManifestHandler.Elapsed += new System.Timers.ElapsedEventHandler(_tmrManifestHandler_Tick);
_tmrManifestHandler.Interval = 100;
_tmrManifestHandler.Enabled = false;
}
public delegate void delBeginSync();
public event delBeginSync evBeginSync;
public void PreConnect()
{
while (true)
{
if (true /* just for testing*/)
{
evBeginSync();
return;
}
}
}
public void Connect()
{
_tmrManifestHandler.Enabled = true;
_tmrManifestHandler.Start();
}
private void _tmrManifestHandler_Tick(object sender, EventArgs e)
{
//NOT BEING 'HIT'
}
}

Understanding this Event Example

After reading online tutorials regarding events , I think I almost have an idea of whats going on. I developed the following extremely simple code to trigger an event in case a value is greater than 5.I know the code is pretty useless but I am using it to get my point across. (Instead of a main I just used a button event to trigger the code.)
//declare the delegate
public delegate void MyDelegate(string str);
public class SomeClass
{
public event MyDelegate MyEventFromDelegate;
private int i;
public int I
{
get
{ return i; }
set
{
if (value > 5)
{
MyEventFromDelegate("Value Greater than 5");
i = 0;
}
else
{
i = value;
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{ InitializeComponent(); }
public void Method_To_Call(String rx)
{ MessageBox.Show("This method will be called if greater than 5");}
private void button1_Click(object sender, EventArgs e)
{
SomeClass a = new SomeClass();
a.MyEventFromDelegate +=new MyDelegate(Method_To_Call);
a.I = 12;
}
}
The only concern I have here is when we want to raise an event with the statement
MyEventFromDelegate("Value Greater than 5");
What point is passing a parameters to the event is at this point if later (at button click event) we are actually going to assign it a function to call every time an event is triggered.
In your very simple example - there is no point, because SomeClass instance "a" is very short-lived, and because you are not using rx parameter passed to Method_To_Call.
Your form method button1_Click is connected to the button's Click event through a delegate. Button does not know what code will execute when it is clicked. All it has to do is to signal that is has been clicked. That signal is implemented using a delegate.
Your could have defined your delegate as having an integer parameter where the checked value is passed. Then although the event method would be invoked only when value is greater than 5, inside the event method you could do things differently depending on the actual value.
//declare the delegate
public delegate void MyDelegate(int aValue);
public class SomeClass
{
public event MyDelegate MyEventFromDelegate;
private int i;
public int I
{
get
{ return i; }
set
{
if (value > 5)
{
MyEventFromDelegate(value);
i = 0;
}
else
{
i = value;
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void Method_To_Call(int aValue)
{
MessageBox.Show("This method signals that value is greater than 5. Value=" + aValue.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
SomeClass a = new SomeClass();
a.MyEventFromDelegate +=new MyDelegate(Method_To_Call);
a.I = 12;
}
}

Cross-thread operation not valid: Asynchronous delegates error

I've been trying to learn delegates.I just created a button,label and checkbox. If I click checkbox, the time format changes. If i click the button , i print the date accordingly. However when trying to use asynchromous delegate i.e., to use another thread, i am stuck with an error
public delegate void AsyncDelegate(bool seconds);
public partial class Form1 : Form
{
AsyncDelegate ad;
TimeZ t = new TimeZ();
public Form1()
{
InitializeComponent();
}
private void btn_async_Click(object sender, EventArgs e)
{
ad = new AsyncDelegate(t.GetTime);
AsyncCallback acb = new AsyncCallback(CB);
if (chk_sec.Checked)
{
ad.BeginInvoke(true, acb, null);
}
else
ad.BeginInvoke(false, acb, null);
}
public void CB(IAsyncResult ar)
{
t.Tim = ar.ToString();
ad.EndInvoke(ar);
lbl_time.Text = t.Tim;
}
and in another class library i get Timez used above. I add a reference of it in the project
public class TimeZ
{
private string tim;
public string Tim
{
get
{
return tim;
}
set
{
tim = value;
}
}
public string GetTime(bool seconds)
{
if (seconds)
{
return DateTime.Now.ToLongTimeString();
}
else
return DateTime.Now.ToShortTimeString();
}
}
However i get this error when i run the program:
Cross-thread operation not valid: Control 'lbl_time' accessed from a thread other than
the thread it was created on.
Can u help me out on how to solve this?
You cannot access forms and controls properties and methods from a thread that is not the form thread.
In windows, each window is bound to the thread that created it.
You can do that only with Control.BeginInvoke or the more useful System.Threading.SynchronizationContext class.
See http://msdn.microsoft.com/it-it/library/system.threading.synchronizationcontext(v=vs.95).aspx
See http://msdn.microsoft.com/it-it/library/0b1bf3y3(v=vs.80).aspx
It means, you have to post through synchronization context for example another async delegate in form thread.
public partial class Form1 : Form
{
AsyncDelegate ad;
TimeZ t = new TimeZ();
// Our synchronization context
SynchronizationContext syncContext;
public Form1()
{
InitializeComponent();
// Initialize the synchronization context field
syncContext = SynchronizationContext.Current;
}
private void btn_async_Click(object sender, EventArgs e)
{
ad = new AsyncDelegate(t.GetTime);
AsyncCallback acb = new AsyncCallback(CB);
if (chk_sec.Checked)
{
ad.BeginInvoke(true, acb, null);
}
else
{
ad.BeginInvoke(false, acb, null);
}
}
public void CB(IAsyncResult ar)
{
// this will be executed in another thread
t.Tim = ar.ToString(); // ar.ToString()???? this will not give you the time for sure! why?
ad.EndInvoke(ar);
syncContext.Post(delegate(object state)
{
// This will be executed again in form thread
lbl_time.Text = t.Tim;
}, null);
}
I don't know why you need an asynchronous callback to print time however :) really don't know why, thinking it is just some test code.

Categories

Resources