Reputation: 149
So, I have a win form that calls a method
dvm.SetVoltage(excitationVoltage, rigNo);
which runs a task in another class
public void SetVoltage(double voltage, int rigNum)
{
Task.Run(() => ReadDVMWorker());
}
Once the worker is finished (voltage set) it triggers an event In the main Form1.cs
private void dvmVoltageSet(object sender, VoltageEventArgs e)
{
VoltageSet = e.VolSet;
TestLvdtNull();
}
Calling TestLvdtNull method:
private void TestLvdtNull()
{
tbMessage.Location = new Point((int)(x / 2 - 250), 150);
}
As soon as the tbMessage line is reached it causes an exception because it has started another thread other than the one tbMessage was created in, how can I prevent it from starting a new thread and continue using the Main thread please?
I have looked at singlethreadsynchronizationcontext, but couldn't make it compile and I know that you can invoke:
tbMessage.Invoke((Action)delegate
{
tbMessage.Location = new Point((int)(x / 2 - 250), 150);
});
But I have many controls with many attributes changing, there must be a way to keep the UI on the main thread?
Upvotes: 0
Views: 191
Reputation: 2497
All UI controls are created at one thread. That is by design in many UI frameworks. After you finish your task you have to return to the UI thread to access UI controls.
One option mentioned in comments is to use async/await where the part of the method after await
keyword is executed on the same thread as was before the async method.
// UI thread
await ReadDVMWorker(); // executed at ThreadPool
// UI thread
If you prefer to stay with Task, you can use ContinueWith
method with correct TaskScheduler
parameter, which ensures that you're back to UI thread. Eg. TaskScheduler.FromCurrentSynchronizationContext()
Async/await attempt code:
private async void button1_Click(object sender, EventArgs e)
{
// Call the method that runs asynchronously.
string result = await WaitAsynchronouslyAsync();
// Display the result.
textBox1.Text += result;
}
//The following method runs asynchronously.The UI thread is not
//blocked during the delay.You can move or resize the Form1 window
//while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
await dvm.SetVoltage(5, rigNo); //Task.Delay(10000);
return "Finished";
}
Upvotes: 1
Reputation: 149
After trying several different ways to solve the problem, I solved the problem by using SynchronizationContext.
This grabs the SyncronizationContext of the thread:
private SynchronizationContext _synchronizationContext;
SynchronizationContext uiContext = SynchronizationContext.Current;
Then after running my task in another class, where previously I was getting an cross thread call exception, I call the method that wants use the same UI thread:
uiContext.Post(MethodToCallOnTheSameUIThread, "string");
After this I can modify and update my textboxes and other controls!
You can check the thread id by:
int id = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Thread: " + id);
With thanks to Mike Peretz and his CodeProject
Upvotes: 0
Reputation: 777
You could have a method to update arbitrary controls
private void dvmVoltageSet(object sender, VoltageEventArgs e)
{
VoltageSet = e.VolSet;
TestLvdtNull(tbMessage);
TestLvdtNull(tbMessage2);
}
private void TestLvdtNull(Control control)
{
control.BeginInvoke((MethodInvoker)delegate()
{
control.Location += new Point((int)(x / 2 - 250), 150);
});
}
Upvotes: 0