Dispatcher.Invoke method doesn't work sometimes - c#

I am facing to a very strange issue when I use dispatcher.invoke method in wpf.
Background:
I defined a user control there is a DoWorkEventArgs to support some async work:
public class MyUserControl : UserControl
{
private BackgroundWorker bw;
public MyUserControl()
{
bw.DoWork += new DoWorkEventHandler(DoWorkMethod);
}
public void StartWork()
{
bw.RunWorkerAsync();
}
void DoWorkMethod(object sender, DoWorkEventArgs e)
{
this.Dispatcher.Invoke((System.Action)delegate()
{
//Add some item in a ListBox, this ListBox is defined in the user control.
TextBlock b = new TextBlock();
//some code
Listbox.Items.Add(b);
}
}
}
When a button click I created 2 instance of this user control and call there StartWork method:
MyUserControl control1 = new MyUserControl();
MyUserControl control2 = new MyUserControl();
control1.StartWork();
control2.StartWork();
Here is the problem, sometimes the ListBox in usercontrol1 is not updated, there is no item in it, sometimes this situation happend in the ListBox of usercontrol2, I debug them and I found the code runs normal, the ListBox.Items.Add method runs, and the results just don't come out.
If I change Dispatcher.Invoie to Dispatcher.BeginInvoke, then it's normal.
Is anyone know the reason?

Firstly, your use of the BackgroundWorker is invalid because you are trying to run the DowWork method on the UI thread, but the whole purpose of that method is that it runs on a background thread. If you want to know how to correctly implement a BackgroundWorker, then please see my answer to the How to correctly implement a BackgroundWorker with ProgressBar updates? question here on Stack Overflow.
However, if you just want to run some code asynchronously, you really don't need to use a BackgroundWorker these days. Instead, you can use the Task class to do something like this:
Task.Factory.StartNew(() => NameOfMethodToRunAsynchronously);
If you need to run part of the NameOfMethodToRunAsynchronously method on the UI thread, then you can return to your Dispatcher.Invoke call:
private void NameOfMethodToRunAsynchronously()
{
// Some long running process
Dispatcher.Invoke((System.Action)delegate()
{
//Add some item in a ListBox, this ListBox is defined in the user control.
TextBlock b = new TextBlock();
//some code
Listbox.Items.Add(b);
}
}
Finally, to answer your original concern about the Dispatcher class, please see the Dispatcher Class page on MSDN. From that page:
Provides services for managing the queue of work items for a thread.
Note that it says a thread and not the UI thread. This means that it could be the UI thread like you want, but not necessarily. In order to ensure that it will be for the UI thread, we can simply set it on the UI thread in the constructor of MainWindow.xaml.cs:
Dispatcher uiDispatcher = Application.CurrentDispatcher;
So to ensure that your Dispatcher will run on the UI thread, just use that instance:
uiDispatcher.Invoke((System.Action)delegate()
{
//Add some item in a ListBox, this ListBox is defined in the user control.
TextBlock b = new TextBlock();
//some code
Listbox.Items.Add(b);
}

Related

The calling thread cannot access this object because a different thread owns it.' error in Thread wpf

I have Window 1 in which on button click i am opening Window 2 in new thread.
Following is my code
private void Button_Click_2(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(() =>
{
Scanner w = new Scanner();
w.Show();
w.Closed += (sender2, e2) =>
w.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
Window 2 has form I am getting form values on Button click
private void EnterProduct(object sender, RoutedEventArgs e)
{
var data = ProductDetailsData;
LoadCurrentBetween objMain = new LoadCurrentBetween(); //new MainWindow();
objMain.fillorderform(data);
}
on button click of window 2 i am passing values of form to another View
public void fillorderform(dynamic data)
{
this.Dispatcher.Invoke(() =>
{
LoadCurrentdetails.Part = data.Part;
LoadCurrentBetween loadCurrentbtw = new LoadCurrentBetween();
Switcher.Switch(loadCurrentbtw);
});
} public static class Switcher
{
public static MainWindow pageSwitcher;
public static void Switch(UserControl newPage)
{
pageSwitcher.Navigate(newPage);
}
}
Following code is giving error at "this.Content = nextPage;"
The calling thread cannot access this object because a different thread owns it.
public void Navigate(UserControl nextPage)
{
this.Dispatcher.Invoke(() =>
{
var aa = nextPage.Dispatcher.CheckAccess();
this.Content = nextPage;
});
}
I have seen similar Questions asked by other developers but i am not getting how to fix.
pls help
WPF is very strict (compared to Windows forms) about requiring methods which update UI elements to be done on the main/UI thread. So you definitely want both windows to be in the main/UI thread. The error that you are seeing is what happens if you try to do UI work in WPF from a different thread, so you absolutely have to stop doing that. It's OK to have multiple windows open, all on the same UI thread.
If one of your windows is doing heavyweight processing that makes the UI lock up, then the easiest thing is probably to add the async keyword to your button click event, and put the work you are doing in another method which has an async keyword. Then, when you call the helper method, you use the await keyword.
I agree with others that BackgroundWorker and Task are two other ways to accomplish heavyweight processing in a background thread while still having a responsive UI. Tasks are easier to use than BackgroundWorker.
If you are using a BackgroundWorker, it may be good enough to use the RunWorkerCompleted event. If so, look at this post: How to use WPF Background Worker. If you are using a BackgroundWorker and you need to call a custom method in your UI class from the background thread, then pass the Dispatcher object for your window/dialog to the background thread (or get access to it some other way), and when it needs to call back into the UI, use Invoke with the Dispatcher object. By using Invoke, the method you are calling from the background thread will be executed on the UI thread.

"The calling thread must be STA, because many UI components require this" while creating/adding new wpf usercontrol

I have a usertaskpane in VSTO add-in. I'm adding there winformshost and elementhost to be able to use wpf controls inside usertaskpane.
I managed to add a main wpf control, but I am failing with adding child user control to that.
I have such method that initiates adding new wpf control:
private void MasterCheck()
{
this.pnlProgress.Visibility = System.Windows.Visibility.Visible;
//I'm using progress bar functionality in ReturnMasters method
Thread myNewThread = new Thread(() => Auditor.AuditMasterSlides(Globals.ThisAddIn.Application.ActivePresentation, this.pnlMaster, this, token));
token = new CancellationTokenSource();
myNewThread.Start();
this.pnlProgress.Visibility = System.Windows.Visibility.Collapsed;
}
public static void AuditMasterSlides(PPT.Presentation pres, Panel panel, MainProofingTaskPaneControl control, CancellationTokenSource cancToken)
{
IDictionary<string,MasterSlide> masterSlides = ReturnMasters(pres, cancToken, control);
control.ShowAndCollapse(panel);
control.RemovePanelChildren(panel);
if (masterSlides.Count>1)
{
//control.AddControlToPanel(panel, new MasterCheckControlOK());
}
else
{
control.AddControlToPanel(panel, new MasterCheckControlOK());
}
}
internal void RemovePanelChildren(Panel panel)
{
this.Dispatcher.Invoke(() =>
{
for (int i = panel.Children.Count - 1; i >= 0; i--)
{
panel.Children.RemoveAt(i);
}
});
}
internal void AddControlToPanel(Panel panel, Control control)
{
MasterCheckControlOK newControl = new MasterCheckControlOK();
this.Dispatcher.Invoke(() =>
{
panel.Children.Add(newControl);
});
}
And I'm getting error here:
public MasterCheckControlOK()
{
InitializeComponent();
}
How can I solve it to be able to:
use progress bar functionality (currently works)
add new wpf controls (does not work)
modify/remove controls (currently works)
You can only create UI controls on STA (single-threaded apartment) threads:
The calling thread must be STA, because many UI components require this
You can only access a control on the thread on which it was originally created. For example, you cannot create a control on a thread B and then try to add it to the Children collection of a control that was created on thread A.
So it makes no sense to create a control on a background thread if you intend to interact with it one way or another from the main thread. Then you will get this exception.
Bottom line: You should create all controls on the same thread and this thread should in most cases be the main UI/dispatcher thread. This will save you a whole lot of trouble.
When you create a control it has to happen in the main UI thread. Currently you are creating the control in another thread and then adding it to another. This will cause an exception.
You need to move the creation of the control to happen inside the invoke so it happens on the main UI thread.
You can't create UI controls in separate threads. The control needs to exist on the UI thread.
You might try having your threaded function do its work through your window's Dispatcher using its .Invoke() methods.
You probably want to make sure that ONLY the manipulation of your UI controls is done with the dispatcher otherwise you'll probably lock up the UI anyway.
public static void AuditMasterSlides(PPT.Presentation pres, Panel panel, MainProofingTaskPaneControl control, CancellationTokenSource cancToken)
{
IDictionary<string,MasterSlide> masterSlides = ReturnMasters(pres, cancToken, control);
this.Dispatcher.Invoke((() => control.ShowAndCollapse(panel));
...
}
As for the STA thread issue, you need to specify that your thread is an STA thread before you start it.
I did this by calling .SetApartmentState() on my thread:
thread1.SetApartmentState(ApartmentState.STA);
thread1.Start();

Create control in thread and loading photo async

I have a Windows Form, inside I have a Button and a Panel.
On button click I'm adding a control to the panel... as many as I want. This process is using Task Factory. Example:
private void ButtonClick()
{
// This line needs to happen on the UI thread...
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
CustomControl.PersonResult newControl = new CustomControl.PersonResult();
this.panel1.Controls.Add(newControl);
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
My PersonResult.cs control has the following layout:
The control has a picturebox and 3 lables.
Now, when a new PersonResult control is added to the form, I want to perform other background worker in order to get an image from the internet and place it in the picturebox.
So, the scenario is:
I press the button many times and immediately I will see the customcontrol added to the panel, but the picturebox of every control will be empty, but then images will start appearing as soon as the worker loads the image from internet and place it on the picturebox.
Any clue on how do implement this?
Thanks a lot
Any time you touch a control you must do so from the UI thread. You can do the actual downloading of the picture and other long-running tasks from your thread/task. From your thread you use Control.Invoke to invoke a delegate on the UI thread.
This MSDN article has a good example of using Control.Invoke with simple variables like strings and such:
http://msdn.microsoft.com/en-us/library/a1hetckb(v=vs.100).aspx
But often a helper class is used so you can pass more complex data between your delegates without resorting to big nasty object arrays.
Here's some sample code I did up:
private void button1_Click(object sender, EventArgs e) {
Task.Factory.StartNew(() =>
{
DataClass dataClass = new DataClass(this.textBox1);
Thread.Sleep(5000); // simulate long running task
dataClass.TextToPass = "set some text";
dataClass.updateTargetControl();
});
}
private class DataClass {
delegate void updateTextBoxOnUiThreadDelegate();
private TextBox _targetControl= null;
private updateTextBoxOnUiThreadDelegate _updateDelegate;
internal DataClass(TextBox targetControl) {
_updateDelegate = new updateTextBoxOnUiThreadDelegate(updateOnUiThread);
_targetControl = targetControl;
}
internal string TextToPass = "";
internal void updateTargetControl() {
_targetControl.Invoke(_updateDelegate);
}
private void updateOnUiThread() {
_targetControl.Text = this.TextToPass;
}
}
Something along the lines:
You don't have to add controls asynchronously. Add them in the GUI thread and every time create a new worker thread supplying it with the delegate from your control which will be called asynchronously (using BeginInvoke?) when the worker finished loading the image.
I am not quite sure I understand why you've wrapped a UI operation in its own Task.. when it isn't chained to an async Task.
Anyway.. PictureBoxes have a LoadAsync method. Why make this harder than it needs to be?
private void ButtonClick()
{
CustomControl.PersonResult newControl = new CustomControl.PersonResult();
this.panel1.Controls.Add(newControl);
newControl.PictureBox.WaitOnLoad = false;
newControl.PictureBox.LoadAsync("http://url-here.com/image.jpg");
}

Update controls created in another thread?

I have two threads.
Thread 1: WPF thread. Shows a Window with all the information.
Thread 2: Loops constantly, receiving information & updates the Window in thread 1.
I have the following interfaces.
IModuleWindow
{
void AddModule(IModule module);
void RemoveModule(IModule module);
}
IModule
{
UserControl GetSmallScreen();
UserControl GetBigScreen();
}
IModuleWindow is implemented by the WPF window in Thread 1
IModule is implemented by an object, is instantiated in Thread 2, and then sent to thread 1.
I want to Add the UserControls in IModule to the Window object in thread 1, and show them. IModule objects get updated constantly in thread 2 and they have to change their text.
Basically the idea is that this program is supposed to show the state of objects in thread 2 , which gets updated constantly.
What is the best way to accomplish this in WPF?
IMO the best idea is to use BackgroundWorker, with the very handy ReportProgress method and ProgressChanged event.
The ProgressChanged event is raised on the GUI thread, so you can perform your updates to the GUI directly. Here's how you code should look like:
// initialize the worker
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
// thread 2 (BackgroundWorker)
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// main loop
while(true)
{
// time-consuming work
// raise the event; use the state object to pass any information you need
ReportProgress(0, state);
}
}
// this code will run on the GUI thread
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// get your state back
object state = e.UserState;
// update GUI with state
}
It helped me lot to understand what i must do.
The scenario must be like that:
ObservableCollection images = new ObservableCollection();
TaskFactory tFactory = new TaskFactory();
tFactory.StartNew(() =>
{
for (int i = 0; i < 50; i++)
{
//GET IMAGE Path FROM SERVER
System.Windows.Application.Current.Dispatcher
.BeginInvoke((Action)delegate()
{
// UPDATE PROGRESS BAR IN UI
});
images.Add(("");
}
}).ContinueWith(t =>
{
if (t.IsFaulted)
{
// EXCEPTION IF THREAD IS FAULT
throw t.Exception;
}
System.Windows.Application.Current.Dispatcher
.BeginInvoke((Action)delegate()
{
//PROCESS IMAGES AND DISPLAY
});
});
You must use System.Windows.Application.Current.Dispatcher.BeginInvoke() for updating UI in WPF.
It would be nice to be able to use controls created at another thread,
thats what I want ideally
The short answer: forget it.
A UI control belongs to a single UI thread only. The best you can do here, is to create controls in main thread, prepare data in background thread, and update controls' properties in main (UI) thread again.
For data preparation I recommend use TPL.

C# threading issue

To play a bit with threading, delegates and backgroundworkers, I'm putting together a few small applications, I'm having a bit of trouble with one of them.
I've a Windows form, with a textbox, a button and a richttext.
When I press the button, the text in the textbox is used as a paramter to instantiate a class, like this:
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
backgroundWorker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.AppendText(response);
Application.DoEvents();
}
});
}).Start();
}
void OnUpdateTicker(string msg)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
label4.Text = msg;
Application.DoEvents();
});
}).Start();
}
}
When debugging I run into a 'textBox1.Lines' threw an exception of type 'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException'
Any tips on how to solve this problem?
First, there is no need to create new threads inside DoWork; the whole idea with the BackgroundWorker is that DoWork is executed on a separate thread. Second, since DoWork is executed on a separate thread and UI controls can be modified only on the UI thread, you need to invoke those updates correctly. So, a rewritten version of worker_DoWork could look like this:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.Invoke((Action) delegate { richTextBox1.AppendText(response); });
}
}
Note how the code does not explicitly spawn any new threads, and also how the AppendText method call is done through a Control.Invoke call, forcing it to execute on the UI thread.
The main reason is that the textbox is not owned by the background thread.
Your UI thread owns all the UI objects, and you're spinning up a background thread when a button is pressed. That background thread should not have access to any UI objects.
If you want the value of the textbox to be used, you'll need to pass it to your background thread another way.
Have a look here for an explanation (and solution).
You can only update controls on the main thread from the main thread itself, unless you explicitly tell your program that it's ok to do, by using the .Invoke method of the control.
From: http://www.albahari.com/threading/part3.aspx
Control.Invoke
In a multi-threaded Windows Forms application, it's illegal to call a method or property on a control from any thread other than the one that created it. All cross-thread calls must be explicitly marshalled to the thread that created the control (usually the main thread), using the Control.Invoke or Control.BeginInvoke method. One cannot rely on automatic marshalling because it takes place too late – only when execution gets well into unmanaged code, by which time plenty of internal .NET code may already have run on the "wrong" thread – code which is not thread-safe.

Categories

Resources