I am trying to create dynamically custom userControl in background thread.
This is my method where I am creating new thread:
var thread = new Thread(CreateItemInBackgroundThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
And this is method CreateItemInBackgroundThread:
var uc = new MyUserControl();
UserControl item = uc;
AllControls.Add(item);
//Here I am trying to add control to a current Tab
foreach (var currentTab in _allTabs)
{
currentTab.DocumentWindow.Dispatcher.BeginInvoke(new Action(() =>
{
if (tab.DocumentWindow.IsSelected)
{
tempTab = tab;
tempControl = item;
finish = true;
}
}));
}
This is my finish property
bool finish
{
get { return _finish; }
set
{
_finish = value;
if (_finish)
{
tempTab.AnimatedCanvas.Dispatcher.BeginInvoke(new Action(() => tempTab.AnimatedCanvas.Children.Add(tempControl)));
}
} // Here I get error - The calling thread cannot access this object because a different thread owns it
}
How can I avoid this error and why this error happend?
as the error says, you can't access the this object because a different thread owns it, so you can invoke that thread using Invoke(Delegate Method)
you can check if invoke required using tempTab.InvokeRequired
This error is coming because u must be doing the different tasks on the same thread like U cannot make a thread go async and update the UI using the same thread.It will cause conflict.because UI thread is the main thread.
You can use BAckground Worker Thread and subsribe its two eventHandlers to your events which you want to work on.. for eg-
BackgroundWorker Worker=new BackgroundWorker();
worker.DoWork+=Yorevent which will do the timeTaking Task();
Worker.RunWorkerCompleted+=YOurEvent which will Update your UI after the work is done();
worker.RunWorkerAsync();
RunWorkerAsync() will make your thread go async and work on background
this way it will not cause any thread Error too..
Related
I am trying to add a child control (UserControl) to a Grid and the changes are not reflecting. But if another child control (UserControl) is added to the same grid then layout gets updated and both child are visible. This operation perform on button click.
/*this Operation is perform in Backgroud Worker */
void func()
{
/*adding first User Control*/
addRemoveChild(true,FirstChild);//even tried to run this fuc with Dispatcher
FixButton();
addRemoveChild(false,FirstChild);
}
void addRemoveChild(bool isAdd,UserControl uc)
{
if (isAdd)
{
parentGrid.Children.Add(uc); /*parentGrid is Parent Grid*/
parentGrid.UpdateLayout();
return;
}
else
{
parentGrid.Children.Remove(uc);
parentGrid.UpdateLayout();
}
}
void FixButton()
{
/* here some operation is perform which takes 5 min to complete till then FirstChild is not visible*/
addRemoveChild(true,secondChild); /*When this Func run the first Child is visible*/
}
Your function is performed in a background worker: It's not done in the Dispatcher Thread. Every time you use a Dispatcher object (an object that has been created by the Dispatcher thread such as Controls) you should be in the Dispatcher thread.
The background worker is useful to perform task and update UI relative to the task's status in "real time".
You aren't using the background worker correctly. The code in the DoWork is executed in a separate thread whereas the ProgressChanged callback is executed in the Dispatcher thread.
What your code should look like this:
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (sender, args) => {
bw.ReportProgress(0);
FixButton();
bw.ReportProgress(100);
};
bw.ProgressChanged += (sender, args) => {
if (args.ProgressPercentage == 0) {
parentGrid.Children.Add(uc);
} else if(args.ProgressPercentage == 100) {
parentGrid.Children.Remove(uc);
}
};
bw.RunWorkerAsync();
By the way you don't need to call UpdateLayout() and your DoWork callback function should never use Dispatcher objects (remove addRemoveChild from FixButton function)
I'm beginner in c#,and i write this code for start the new thread:
Thread workerThread = new Thread(DoWork);
workerThread.Priority = ThreadPriority.Highest;
workerThread.Start();
in the up thread process some thing and show into the chart,every thing is okay,but when run and finish DoWork method,chart control visible set to false automatically!,my DoWork method is:
public void DoWork()
{
//.....some process and show into the process result into the chart
chart1.Visible = true;//this code not run
}
how can solve that?
You do not have access to UI elements from a different thread.
For Winforms:
How to update the GUI from another thread in C#?
For WPF:
Change WPF controls from a non-main thread using Dispatcher.Invoke
chart1.Dispatcher.Invoke(() =>chart1.Visible = true);
Change your Dowork method signature to accept object as parameter and pass Synchronization context to it:
void DoWork(object o)
{
SynchronizationContext cont = o as SynchronizationContext;
// your logic gere
cont.Post(delegate
{
// all your UI updates here
}, null);
}
Thread workerThread = new Thread(DoWork);
workerThread.Priority = ThreadPriority.Highest;
workerThread.Start(SynchronizationContext.Current);
I have my VM implemented INotifyPropertyChanged interface. I created another thread T for populating a list that I bind to Xaml. After list is populated, I call PropertyChanged in thread T, and my UI got refreshed correctly.
My question is in what case I would need to use Dispatcher? Why I don't need to use Dispatcher in my case? I thought Dispatcher is used when the code in other thread want to notify the changes to the UI thread by enqueuing the changes to the UI refresh queue, such as adding items to ObservableCollection from another thread, and UI thread will then pull data from the queue.
private List<string> _ListData;
public List<String> ListData
{
get
{
if (_ListData == null)
Initialise( () => ListData = ReturnSlow());
return _ListData;
}
set { _ListData = value; }
}
private List<string> ReturnSlow()
{
List<string> Test = new List<string>();
Test.Add("1");
Test.Add("2");
Thread.Sleep(2000);
return Test;
}
public void Initialise(Action initialiser)
{
Task t = new Task(() =>
{
initialiser();
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ListData"));
});
t.Start();
}
Your app has a main UI thread (usually ManagedThreadId==1). If you want to update the UI from an event that gets pull on some other thread you must use the dispatcher. A useful test here is the Dispatcher.CheckAccess() method that returns true if code is on UI thread and false if on some other thread. A typical call looks something like:
using System.Windows.Threading; // For Dispatcher.
if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}
If you're in the main window you can use:
Dispatcher.BeginInvoke(...
If you're in someother context eg a view model then use:
Application.Current.Dispatcher.BeginInvoke(
Invoke vs BeginInvoke
Use Invoke if you want the current thread to wait until the UI thread has processed the dispatch code or BeginInvoke if you want current thread to continue without waiting for operation to complete on UI thread.
MessageBox, Dispatchers and Invoke/BeginInvoke:
Dispatcher.Invoke will block your thread until the MessageBox is dismissed.
Dispatcher.BeginInvoke will allow your thread code to continue to execute while the UI thread will block on the MessageBox call until its dismissed.
CurrentDispatcher vs Current.Dispatcher!
Be ware of Dispatcher.CurrentDispatcher as my understanding of this is that is will return a Dispatcher for the current thread not the UI thread. Generally are you interested in the dispatcher on the UI thread - Application.Current.Dispatcher always returns this.
Additional note:
If you are finding you are having to check dispatcher CheckAccess often then a useful helper method is:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
Which can be called as:
DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});
I' have a timer that fires the following code. I need to show a form if the are a notice to show. The problem is that when thread is fired inside the thread and i set ConfiguracioGlobal.DicNotices[kvp.Key].Shown = true; the next run don't know that .Shown is true. When showing the form I get the following error: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on. I think I have to use delegates but I have no idea :(
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate
{
try
{
foreach (KeyValuePair<int, Notice> kvp in GlobalConfiguration.DicNotices)
{
if (!kvp.Value.Shown && DateTime.Now.Hour == kvp.Value.HourIni.Hour && DateTime.Now.Minute == kvp.Value.HourIni.Minute)
{
GlobalConfiguration.DicNotices[kvp.Key].Shown = true;
FrmNotices frmPopup = new FrmNotices(kvp.Key);
frmPopup.Show();
Application.Run(frmPopup);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error");
}
}));
thread.IsBackground = true;
thread.Start();
Any idea?
Thanks to all.
You're trying to have multiple UI threads, and beyond that your second UI thread is neither an STA thread nor a foreground thread (both are a problem) and finally, based on your error, it appears that you're accessing controls from the improper UI thread.
Just don't do this. Only use one UI thread and do all of your work from within that.
Don't start up a background thread to run these forms, show them all in the UI thread that you already have. If you want to ensure that only one form is shown at a time use ShowDialog, and if you want multiple forms to be shown and active simultaneously then use Show instead.
As long as they are STA threads, in theory you can have as many as you want to:
for(var i = 0; i < 4; i++)
{
var t = new Thread((s) =>
{
Application.Run(
new Form
{
Text = s.ToString()
});
});
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start(i);
}
The whole point of the backgroundWorker is to update the UI after a time-consuming task. The component works as advertised in my WPF app.
However in my test, the callback is not invoked on the calling thread.
[Test]
public void TestCallbackIsInvokedOnClientThread()
{
var clientId = Thread.CurrentThread.ManagedThreadId;
int callbackThreadId = -1;
var manualEvent = new ManualResetEventSlim(false);
var someUIControl = new TextBox();
var bw = new BackgroundWorker();
bw.DoWork += (s,e) => e.Result = 5 ; // worker thread
bw.RunWorkerCompleted += (s, e) =>
{
try
{
callbackThreadId = Thread.CurrentThread.ManagedThreadId;
//someUIControl.Text = callbackThreadId.ToString();
manualEvent.Set();
}
catch (System.Exception ex)
{
Console.Out.WriteLine(ex.ToString());
}
};
bw.RunWorkerAsync();
if (!manualEvent.Wait(5000))
Assert.Fail("no callback");
Assert.AreEqual(clientId, callbackThreadId);
}
Result Message: Assert.AreEqual failed. Expected:<15>. Actual:<10>. callback not invoked on client Thread
What am I missing ?
In the Unit Test I see behavior like
------ Run test started ------
MainThread Id =21
Worker Thread Id =9
Callback Thread Id =9
In the Wpf App, this would be
MainThread Id =1
Worker Thread Id =14
Callback Thread Id =1
Update:
With Justin's answer, made the following changes and now the test passes
Before creating the BackgroundWorker
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));
Instead of using a event for signalling between the threads, simulate a message pump
.
for (int i = 0; i < 3; i++)
{
control.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
Thread.Sleep(50);
}
The behavior is different dues to the different contexts that you are running under.
When you call bw.RunWorkerAsync(), the SynchronizationContext is captured. This is used to dispatch out the RunWorkerCompleted call.
Under WPF it will use DispatcherSynchronizationContext which will marshall the completed call back to the UI thread. Under the test, this marshalling is unnecessary so it remains on the background worker thread.
I belive that the calling thread must support messagepumping (mean, being STA apartment and having an associated Dispatcher) so the background worker can post the callback. If it does not, the background worker has no option but execute the callback in its own thread. If you want to test it, see this link.
I ran into a problem in my code where the user closing a window caused a save, that in turn used a BackgroundWorker to update the home window and it did not run the RunWorkerCompleted because the thread that started the BackgroundWorker had terminated when the window closed.
I had to change the closing window's save run in the home window's context so that after the BackgroundWorker completed, it had a thread to return to.
In my case I am using Windows Forms and controls don't have a Dispatcher property (see the answer in no definition for dispatcher).
Gishu's solution works as well if we use Dispatcher.CurrentDispatcher instead of the one in the control.
On test initialisation:
// I am using a field Dispatcher _dispatcher
_dispatcher = Dispatcher.CurrentDispatcher;
And then when waiting for the background task to be completed:
_dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Thread.Sleep(50);