I am relatively new to C# WPF projects and I have an issue with my code whereby the user interface "locks up" whilst I am running a task. Here is the relevant code:
public partial class MainWindow : Window {
// handles to ManagedDLAContainer objects
private ManagedDLA2DContainer dla_2d;
public MainWindow() {
InitializeComponent();
// initalise aggregate containers
dla_2d = new ManagedDLA2DContainer();
}
private void GenerateAggregate() {
// generate the 2D aggregate of size given by particles_slider value
Dispatcher.Invoke(new Action(() => { dla_2d.Generate((uint)particles_slider.Value); }));
// TODO: add particles to "canvas" on GUI as they are generated
}
private void GenerateButtonClick(object sender, RoutedEventArgs e) {
// set the coefficient of stickiness of aggregate
// to current value of stickiness_slider
dla_2d.SetCoeffStick(stickiness_slider.Value);
// start asynchronous task calling GenerateAggregate method
Task.Factory.StartNew(() => GenerateAggregate());
}
}
Here ManagedDLA2DContainer is a managed C++/CLI wrapper for some native, unmanaged C++ code and stickiness_slider is simply a Slider element of the WPF interface; whilst, similarly, particles_slider is another Slider element of the interface. I should note that the following code also results in the GUI hanging:
public partial class MainWindow : Window {
// lock object for multi-threading tasks
private static readonly object locker = new object();
// handles to ManagedDLAContainer objects
private ManagedDLA2DContainer dla_2d;
public MainWindow() {
InitializeComponent();
// initalise aggregate containers
dla_2d = new ManagedDLA2DContainer();
}
private void GenerateAggregate() {
// lock around aggregate generation
lock (locker) {
// generate the 2D aggregate of size given by particles_slider value
Dispatcher.Invoke(new Action(() => { dla_2d.Generate((uint)particles_slider.Value); }));
// TODO: add particles to "canvas" on GUI as they are generated
}
}
private void GenerateButtonClick(object sender, RoutedEventArgs e) {
// set the coefficient of stickiness of aggregate
// to current value of stickiness_slider
dla_2d.SetCoeffStick(stickiness_slider.Value);
Thread agg_gen_thread = new Thread(GenerateAggregate);
agg_gen_thread.Start();
agg_gen_thread.Join();
}
}
Any information which could help me understand what I might be doing wrong here is appreciated.
Also, if you are curious then all the code for this project is available here: https://github.com/SJR276/DLAProject
You start new thread in which you run GenerateAggregate. Then immediatly you dispatch all work back to user interface thread via Dispatcher.Invoke. So basically you are running long running task on UI thread and it blocks.
Instead, you should dispatch to UI thread only operations that require that (which update UI controls for example), not the whole operation.
Suppose your Generate function looks like this:
void Generate() {
MakeCpuIntensiveWork();
UpdateUIWithResults();
}
You only need to dispatch second part to UI thread. In your case, as we found out in comments, the only UI part is getting value of a slider. So you can split like this:
private void GenerateAggregate()
{
uint sliderValue = 0;
// generate the 2D aggregate of size given by particles_slider value
Dispatcher.Invoke(() => { sliderValue = (uint)particles_slider.Value; });
dla_2d.Generate(sliderValue);
// TODO: add particles to "canvas" on GUI as they are generated
}
Related
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
}
Use case
I'm developing a small application in C# that is called by another application to retrieve data from the Internet. It runs as a process on its own, but almost all of the interaction with it, is managed by the calling application. Therefor it does not have a GUI. However I'd like to add a progress bar using WPF that is shown during certain data retrievals that could take up to a minute. It's fairly easy to make an estimate of how much work is done and how much is left and therefor I find a progress bar suitable.
Research done
I have a fair understanding of threading after reading large parts of Albahari's pdf on threading (http://www.albahari.info/threading/threading.pdf). I have also read through a lot of posts on SO and MSDN in this matter. Most posts suggest the use of a background worker for the time consuming data retrieval while keeping the GUI in the main thread and therefor suggest solutions using a background worker. That feels awkward in this scenario though, where the main task is data retrieval and not GUI interaction.
I've spend a bunch of hours trying to make sense of different tutorials and forum posts while trying to conform them to my problem, but I have not succeeded and now I'm pretty much back to square one. Basically I'd like to end up with the following two classes outlined below:
ProgressBarWindow
public partial class ProgressBarWindow : Window
{
public ProgressBarWindow()
{
InitializeComponent();
}
public void setValue(int value)
{
// This function should be available from the main thread
}
}
Querier
Public class Querier
{
public List<Item> getItems()
{
// call ProgressBarWindow.setValue(0);
...
// call ProgressBarWindow.setValue(100);
// call ProgressBarWindow.Close();
}
}
It's my understanding that UI must run under single threads and therefor my ProgressBarWindow object could not be instantiated in a new thread while at the same time be available to the main thread (kind of).
Dispatcher.BeginInvoke appears to be my savior here but so far I haven't been able to figure out what should go into the Querier class and what to go in the ProgressBarWindow class. How can I make the two threads interact with the same instance of ProgressBarWindow?
Please ask if you need more details and I will try to clarify.
You can use the Progress class to update the UI with the current progress of a long running operation.
First create an instance of Progress in your UI:
Progress<int> progress = new Progress<int>(currentProgress =>
{
progressBar.Value = currentProgress;
//todo do other stuff
});
Then pass it to the long running process:
public List<Item> getItems(IProgress<int> progress)
{
progress.Report(0);
//todo do something
progress.Report(100);
}
Here is a generic function which i generally use:
public static void Invoke(this UIElement element,Action action)
{
element.Dispatcher.Invoke(action, null);
}
And to use it, simply call:
this.Invoke(() => ProgressBarWindow.SetValue(0));
So, in the getItems() function, you would have something along the lines of:
public List<Item> getItems()
{
ProgressBarWindow wnd;
MainWindow.Invoke(() => wnd = new ProgressBarWindow())
MainWindow.Invoke(() => wnd.SetValue(0))
...
MainWindow.Invoke(() => wnd.SetValue(100))
MainWindow.Invoke(() => wnd.Close())
}
Make sure you always have a way to get to the main window is anything (the one running from either App.xml, or App.Run(...). You can then issue any GUI actions through it (even if you have to create a new Loader window for example, as long as it's done within the main thread)
App.xaml
public partial class App : Application
{
private void Application_Startup_1(object sender, StartupEventArgs e)
{
Task.Factory.StartNew<List<int>>(() => Querier.GetItems());
}
}
ProgressBarWindow.xaml.cs
public partial class ProgressWindow : Window
{
public ProgressWindow()
{
InitializeComponent();
Querier.Start +=()=> Visibility = Visibility.Visible;
Querier.Stop += () => Visibility = Visibility.Collapsed;
Querier.ReportProgress +=OnReportProgress;
}
public void OnReportProgress(int value)
{
txtBox.Text = value.ToString();
}
}
ProgressBarWindow.xaml
<Grid>
<TextBox x:Name="txtBox"></TextBox>
</Grid>
Querier
public class Querier
{
public static event Action Start;
public static event Action Stop;
public static event Action<int> ReportProgress;
public static List<int> GetItems()
{
if (Start != null)
App.Current.Dispatcher.BeginInvoke(Start,null);
for (int index = 0; index <= 10; index++)
{
Thread.Sleep(200);
if (ReportProgress != null)
App.Current.Dispatcher.BeginInvoke(ReportProgress, index*10);
}
if (Stop != null)
App.Current.Dispatcher.BeginInvoke(Stop, null);
return Enumerable.Range(1, 100).ToList();
}
}
I am just trying to give an idea hope this will help.
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 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'm writing a simple Windows forms application to get me into the swing of things with Threads. So far what I have is working, but what I would like to do is contain it all in a seperate class rather than directly in my forms code.
I have a background thread that starts and retrieves data from a database. I then display that data in to a listbox.
private delegate void UpdateListValues(List<ListBoxItem> itemList);
private void form_main_Shown(object sender, EventArgs e)
{
// Set the loading text.
list_selection.Items.Add(ListHelpers.LoadingItem());
// Start the data access on a seperate thread.
Thread worker = new Thread(GetInvoicingData);
worker.IsBackground = true;
worker.Start();
}
private void GetInvoicingData()
{
// Query database
List<ListBoxItem> values = DAC.GetInvoicingAccounts();
// Display results
BeginInvoke(new UpdateListValues(DisplayList), new object[] { values });
}
private void DisplayList(List<ListBoxItem> itemList)
{
// Display each result
list_selection.Items.Clear();
for (int i = 0; i < itemList.Count; i++)
{
list_selection.Items.Add(itemList[i]);
}
}
The problem is that in the DisplayList method, I won't be able to access the list box (list_selection) because it's part of the form class. Does anyone have any suggestions on how I can do this.
Also, I'm new to threading so feel free to tell me I'm doing it absolutely wrong. I just used the example from http://www.codeproject.com/Articles/23517/How-to-Properly-Handle-Cross-thread-Events-and-Upd to get me to where I am now.
Thanks
How about something like this:
// Added the form's class declaration to highlight separation of thread code into a separate class, but may not be exactly the same as yours depending on naming
public class Form1 : Form
{
private readonly DataRetriever _dataRetriever;
private void form_main_Shown(object sender, EventArgs e)
{
// Set the loading text.
list_selection.Items.Add(ListHelpers.LoadingItem());
// Create the DataRetriever, and provide it with a delegate to DisplayList for returning data
_dataRetriever = new DataRetriever(DisplayList);
// Start retrieving data on a separate thread...
_dataRetriever.GetData();
}
private void DisplayList(List<ListBoxItem> itemList)
{
if (InvokeRequired)
{
// Ensure the update occurs on the UI thread
Invoke((Action)(() => DisplayList(itemList)));
return;
}
// Display each result
list_selection.Items.Clear();
foreach (var item in itemList)
{
list_selection.Items.Add(item);
}
}
}
// Separate class to hold thread code
public class DataRetriever
{
public delegate void UpdateCallbackDelegate(List<ListBoxItem> itemList);
private readonly UpdateCallbackDelegate _updateCallback;
public DataRetriever(UpdateCallbackDelegate updateCallback)
{
_updateCallback = updateCallback;
}
public void GetData()
{
var thread = new Thread(GetInvoicingData)
{
IsBackground = true
};
thread.Start();
}
private void GetInvoicingData()
{
// Not sure whether "DAC" is a static class, if it needs to be constructed
// in the DataRetriever's constructor, or passed to it as a parameter
_updateCallback(DAC.GetInvoicingAccounts());
}
}
As you can see, all the thread code is now in a separate class DataRetriever, and a delegate provided when constructing it to enable the retrieved data to be passed back to the form once the retrieval is complete. The method that handles the callback ensures that the call is marshalled to the UI thread to prevent cross-thread exceptions.
I would like to point out that this is not presented as the "best" way to do this, but merely as an answer to the question (how to separating threading code into a separate class). As others have mentioned, there are already mechanisms in place to do this sort of thing (e.g. BackgroundWorker). Some complexity has been omitted for clarity. For example, in the implementation presented here, if you were to call GetData() multiple times (with each call occurring before the previous ones have returned their data), you would have multiple queries occurring simultaneously, and as they are running asynchronously, may return their data in an arbitrary order. This may or may not be an issue in your case.