I am trying to update GUI when dictionary value changes on WPF, MVVM. Basically, I got following dictionary on separate thread and library/project:
public static Dictionary<string, string> ProgressStageDictionary = new Dictionary<string, string>
{
{"Data Initiation", ""},
{"Data Import", ""}
};
A child library/project is a stand-alone application that doesn't know about GUI and won't have one. I try to update GUI from it I would have a project reference issue as GUI project reference to child project, not vice versa. That's why I cannot call GUI from that library using, for example, DispatcherHelper.CheckBeginInvokeOnUI.
GUI however as the main thread should know about child threads. So to achieve that I created an INotifyPropertyChanged Event:
public class ViewModelBase: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string caller = null)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
I connected it with my MVVM GUI variable
private List<StepItem> _stepItems { get; set; }
public List<StepItem> StepItems
{
get => _stepItems;
set
{
_stepItems = value;
OnPropertyChanged(nameof(Child.ProgressStageDictionary));
Thread.Sleep(250);
}
}
My GUI should update whenever Child.ProgressStageDictionary is update. However, my GUI is not catching that. What do I need to change to be able to watch variables updates on child threads?
I found an answer how to do it. I couldn't work out why my Observers does not catch update but I manage to do it using delegators.
On the main calculation thread, I create a method that dispatch update to GUI thread:
public void SuperStepProgressMethod(Dictionary<string, string> stepDictionary)
{
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
// Set step item list
StepItems = StepProgressHandler.UpdateStepList(stepDictionary);
});
}
After that when I am calling Project method I delegate MainViewModel method as an action to it.
public static void Run(
Action<Dictionary<string, string>> superStepProgressMethod)
{
ProgressStageDictionary["Data Initiation"] = Initiation();
superStepProgressMethod.Invoke(ProgressStageDictionary);#
}
Using this solution behind the scene threads are updating/running at following order:
GUI Thread -> Calculation Thread -> Separate Project Thread -> GUI Thread
Related
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 !
After I tried lots and lots of solutions I couldn't solve this problem by any means so I started to believe that there is no solution for this problem.
I have an object that contains complex attributes. E.g: List<SomeComplexObject>. I am running a method from this class on a worker thread to keep the GUI running until the worker thread finishes. When it finishes execution, I want to use the attributes of these objects to update GUI let's say I want to use List<SomeComplexObject> looping through this list and update the GUI. But each time I try to access this list the debugger throws an InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
I tried to make all attributes of this class volatile but with no hope I also used Lazy<T> class approach to solve but the same problem occurs.
Class that contain the worker function:
public class MainModules
{
#region Attributes
public VIDEO video;
public string VideoPath
{
get;
set;
}
LowLevelModule lowLevelOutput;
//this list that I want to use to Update GUI
public volatile List<FaceRecognitionModule> faceModuleOutput;
//worker function running on different thread
public void RunMainModules()
{
//some complex work to set the class attributes
}
}
Thread creation in GUI class
private void RunMainModules_BtnClick(object sender, RoutedEventArgs e)
{
// MainModule = new MainModules(mainModuleObj, Inpath, lif, keyframefolderpath, trdbpath, labelspath, rrankspath, alignmatpath, 11, 10);
this.LazyMainModule = new Lazy<MainModules>(this.InitLazyMainModule);
MainModuleThread = new Thread(this.RunMainModules);
MainModuleThread.Start(MainModule);
}
public MainModules InitLazyMainModule()
{
return new MainModules(mainModuleObj, Inpath, lif, keyframefolderpath, trdbpath, labelspath, rrankspath, alignmatpath, 11, 10);
}
public void RunMainModules(Object obj)
{
//MainModules mm = obj as MainModules;
MainModules mm = LazyMainModule.Value;
mm.RunMainModules();
this.Dispatcher.Invoke((Action)(() =>
{
this.InitSpeechRec_Btn.IsEnabled = true;
}));
}
When I try to access faceModuleOutput in class MainModules from GUI I got InvalidOperationException.
Image img = new Image();
//InvalidOperationException occurs here
img.Source = LazyMainModule.Value.faceModuleOutput[0].keyframes[1].keyframe;
To brief this post:
I want to access an object instantiated by a background thread from main thread but it throws
InvalidOperationException : The calling thread cannot access this object because a different thread owns it.
A UI control needs to be created/modified from the GUI Thread. Doing otherwise is illegal.
It seems that the MainModuleThread is (at least) creating and modifying an Image . This should be done in the GUI Thread (the one that called RunMainModules_BtnClick)
You cannot modify or even access pretty much anything that relates to the UI thread from another thread. This can get pretty extreme/annoying sometimes because you can't even get the value in a textbox or check if a checkbox is checked or not. If you want to perform an action on an object owned by the UI thread you need to invoke the UI thread to do it.
UIObject.Dispatcher.Invoke(() => {
//[Perform your action in here]
});
Finally I found the solution ... Class BitmapImage is thread-affine so it can't be accessed by multiple threads you need first to make it opened for reading only closed for writing so the compiler can guarantee that no threads will modify it's content
So the solution ... :
//keyframe here is a BitmapImage so on creation we must call keyframe.Freeze()
LazyMainModule.Value.faceModuleOutput[0].keyframes[1].keyframe;
class KeyFrame:
public class KeyFrame
{
public volatile BitmapImage keyframe;
public volatile List<string> personsNames;
public volatile List<string> categories;
public KeyFrame(BitmapImage keyframe, List<string> personsNames, List<string> categories)
{
this.keyframe = keyframe;
//here we call Freeze funcition on creation to make it modifiable
this.keyframe.Freeze();
this.personsNames = personsNames;
this.categories = categories;
}
}
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've written a WPF WizardFramework which performs some actions in the background using some BackgroundWorker. While processing it can happen that I have to update an ObservableCollection which is bound to my UI.
For this case I've written a ThreadableObservableCollection, which provides threadsafe methods for Insert, Remove and RemoveAt. Though I'm using .NET 4.5 I was not able to get BindingOperations.EnableCollectionSynchronization working without many other invalid access exceptions. My Collection looks like:
public class ThreadableObservableCollection<T> : ObservableCollection<T>
{
private readonly Dispatcher _dispatcher;
public ThreadableObservableCollection()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
public void ThreadsafeInsert(int pos, T item, Action callback)
{
if (_dispatcher.CheckAccess())
{
Insert(pos, item);
callback();
}
else
{
_dispatcher.Invoke(() =>
{
Insert(pos, item);
callback();
});
}
}
[..]
}
This is working as expected, while I am using the wizard in my application. Now I'm using NUnit to write some integrationtests for the application.
There's a listener which waits for the WizardViewModel to finish it's work and looking for some pages which are injected in the Steps-Collection. After the asyncrone work is done I can use Validate to check the viewmodel state.
Unfortunately I'm using a ManualResetEvent to wait for the wizard to close. This looks like following:
public class WizardValidator : IValidator, IDisposable
{
private WizardViewModel _dialog;
private readonly ManualResetEvent _dialogClosed = new ManualResetEvent(false);
[..]
public void ListenTo(WizardViewModel dialog)
{
_dialog = dialog;
dialog.RequestClose += (sender, args) => _dialogClosed.Set();
dialog.StepsDefaultView.CurrentChanged += StepsDefaultViewOnCurrentChanged;
_dialogClosed.WaitOne();
}
[..]
}
Now there's a problem:
While the Application is running the UI Thread is not blocked, the Collection can be updated without any problems. But in my testcases the "main" Thread where I initialize the ViewModel (and because of that the Collections) is an AppDomainThread which is blocked by the testcode. Now my ThreadsafeInsert wants to update the collection but cannot use the AppDomain Thread.
But I have to wait for the wizard to finish, how can I solve this kind of deadlock? Or is there a more elegant solution for this one?
edit:
I worked around this problem with a check if there's a user interface, and only then I invoke on the Application-Thread, otherwise I change the collection intentionally on another thread. This does not prevent the exception, but it is not recognized from the test... the items are inserted nevertheless, only the NotifyCollectionChanged-Handler is not called (which is only used in the UI anyway).
if (Application.Current != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
Steps.Insert(pos, step);
stepsView.MoveCurrentTo(step);
});
}
else
{
new Action(() => Steps.Insert(pos, step)).BeginInvoke(ar => stepsView.MoveCurrentToPosition(pos), null);
}
This is an ugly workaround and I am still interested in a clean solution.
Is there a way to use an alternate Dispatcher to create (e.g.) the whole ViewModel and use this to change my collection?
As I see the main problem that main thread is blocked and other operations are trying to be executed in main thread too? What about not to block main thread, like this:
// helper functions
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
// in your code:
while(!_dialogClosed.WaitOne(200))
DoEvents();
If it will not help then I guess need to try some SynchronisationContext workarounds.
I think the problems boil down to the fact that you create ObservableCollection that is tied to Dispatcher object.
Involving Dispatcher object directly is almost never good idea(as you just witnessed). Instead I would suggest you to see how others have implemented ThreadSafeObservableCollection. This is a little example I put together, it should illustrate the point:
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
private readonly object _lock = new object();
public ThreadSafeObservableCollection()
{
BindingOperations.CollectionRegistering += CollectionRegistering;
}
protected override void InsertItem(int index, T item)
{
lock (_lock)
{
base.InsertItem(index, item);
}
}
private void CollectionRegistering(object sender, CollectionRegisteringEventArgs e)
{
if (e.Collection == this)
BindingOperations.EnableCollectionSynchronization(this, _lock);
}
}
I have a BindingList<> of a class set to the DataSource property of a BindingSource, which is in turn set to the DataSource property of a DataGridView.
1.
It is my understanding that any additions to the list will fire a ListChanged event which will propagate through the BindingSource and then onto the DataGridView, which will update itself to display the change. This will happen because the events have been automatically hooked up. (Yes?)
This is all fine and good when all the work is done on the UI thread, but when the list is created and changed from a non-UI thread, ultimately a cross-thread exception occurs when the grid is updated. I can understand why this happens, but no how to fix it...
2.
What I am having a tough time understanding, is where should I best intercept the ListChanged event to try and marshal things onto the UI thread? I am guessing that I need a reference to the UI thread somehow to help do this?
I have read many posts/articles on this, but I'm struggling because I don't fully understand the mechanisms at work here.
I will never be changing any items once they are in the list, only adding them, and initially clearing the list.
(I am using .NET 2.0)
You can extend BindingList to use an ISynchronizeInvoke (implemented by System.Windows.Forms.Control) to marshal the event invokations onto the UI thread.
Then all you need to do is use the new list type and all is sorted.
public partial class Form1 : System.Windows.Forms.Form {
SyncList<object> _List;
public Form1() {
InitializeComponent();
_List = new SyncList<object>(this);
}
}
public class SyncList<T> : System.ComponentModel.BindingList<T> {
private System.ComponentModel.ISynchronizeInvoke _SyncObject;
private System.Action<System.ComponentModel.ListChangedEventArgs> _FireEventAction;
public SyncList() : this(null) {
}
public SyncList(System.ComponentModel.ISynchronizeInvoke syncObject) {
_SyncObject = syncObject;
_FireEventAction = FireEvent;
}
protected override void OnListChanged(System.ComponentModel.ListChangedEventArgs args) {
if(_SyncObject == null) {
FireEvent(args);
}
else {
_SyncObject.Invoke(_FireEventAction, new object[] {args});
}
}
private void FireEvent(System.ComponentModel.ListChangedEventArgs args) {
base.OnListChanged(args);
}
}
That view is fair enough. Under the covers, other objects such as CurrencyManager and Binding make sure controls are updated when the underlying data source changes.
Adding an item to a data bound BindingList triggers a series of events that end up trying to update the DataGridView. Since the UI can only be updated from the UI thread, you should add items to BindingList from the UI thread through Control.Invoke.
I assembled a quick sample creating a Form with a DataGridView, a BindingSource and a Button.
The button spins up another thread that simulates getting a new item for inclusion in the BindingList.
The inclusion itself is done back in the UI thread through Control.Invoke.
public partial class BindingListChangedForm : Form {
BindingList<Person> people = new BindingList<Person>();
Action<Person> personAdder;
public BindingListChangedForm() {
InitializeComponent();
this.dataGridView1.AutoGenerateColumns = true;
this.bindingSource1.DataSource = this.people;
this.personAdder = this.PersonAdder;
}
private void button1_Click(object sender, EventArgs e) {
Thread t = new Thread(this.GotANewPersononBackgroundThread);
t.Start();
}
// runs on the background thread.
private void GotANewPersononBackgroundThread() {
Person person = new Person { Id = 1, Name = "Foo" };
//Invokes the delegate on the UI thread.
this.Invoke(this.personAdder, person);
}
//Called on the UI thread.
void PersonAdder(Person person) {
this.people.Add(person);
}
}
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}