Dispatcher.CurrentDispatcher? - c#

If I do this...
public PriorityQueue(Dispatcher dispatcher = null)
{
this.dispatcher = dispatcher ?? Dispatcher.CurrentDispatcher;
}
And then use it in a ViewModel (without passing any args) that is created through the XAML, this.dispatcher will point to the UI thread right?

If it is created from within the UI thread (which it would be, if instantiated inside XAML), then yes, it will point to the UI thread's Dispatcher.

Related

Dispatcher.CurrentDispatcher returns null

I'm trying to close a WPF window I created in a separate thread from the main hosting thread (The main thread is of a PDM application that I have no control over). The host application references my assembly (it's a plugin). I don't know but why the Dispatcher is always null. Creating the WaitView on the host application is not an option for me.
Thanks guys!
var WaitViewModel = new MVVM.ViewModels.WaitViewModel();
MVVM.Views.WaitView WaitView = default(MVVM.Views.WaitView);
Dispatcher dispatcher = default(Dispatcher);
var thread = new Thread(new ThreadStart(() =>
{
dispatcher = Dispatcher.CurrentDispatcher;
WaitView = new MVVM.Views.WaitView();
WaitView.Topmost = true;
WaitView.WindowStartupLocation = WindowStartupLocation.CenterScreen;
WaitView.DataContext = WaitViewModel;
WaitView.Show();
System.Windows.Threading.Dispatcher.Run();
}));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
'unrelated code here
if (dispatcher != null)
dispatcher.Invoke(()=>
{
WaitView.Close();
});
Two ways to do this:
Pass the view's dispatcher into the view model via the constructor.
public MyClass
{
public MyClass(Dispatcher dispatcher)
{
// use your view's dispatcher.
}
{
Use the Application default dispatcher.
Dispatcher dispatcher = App.Current.Dispatcher;
For clarity, a true view model will not use a dispatcher since it is on the UI thread. Nevertheless, you could use regular methods and have the view's dispatcher execute them on the View.
You should grab the dispatcher prior to creating the thread, then pass it into the thread.
In the spirit of dont do that, you shouldn't be creating any form of UI elements in other threads, even if they are marked as STA. Spawning child threads that just run to eternity is not so nice so is potentially multiple message pumps. So, your base design is kinda flawed.
Fix that and your other problems go away.
I hope you are not doing all this from say a console app that is attempting to make it look as though your windows are part of a different process?
Solution is to handle closing the view from the view code behind.
I have added a property called CloseRequest to the ViewModel.
View's code behind:
WaitViewModel WaitViewModel;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// get data context
WaitViewModel = this.DataContext as WaitViewModel;
WaitViewModel.PropertyChanged += WaitViewModel_PropertyChanged;
}
private void WaitViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName == "CloseRequest")
{
Dispatcher dispatcher = this.Dispatcher;
if (WaitViewModel.CloseRequest)
dispatcher.Invoke(() => {
this.Close();
});
}
}

"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();

WPF NotifyPropertyChange from different thread

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));
});

How to create a control properly from other thread (.net compact framework), with no reference to other control?

I have code that runs in a different thread than the UI's one, and it has to create a control (windows forms). However, I don't have a reference to any control from the UI (that way, I could use myControl.Invoke( methodThatAddsControlToUI ) ). Is there a way to do it in the .net compact framework?
I would be interested in a solution that doesn't use references to other controls, if possible (tracking all created forms, for example, would not be a good workaround, as my code will be in a library). In the full framework version, there is the Application.OpenForms property, but this doesn't exit in the CF.
EDIT:
The main purpose of this is calling a method on the UI thread:
class Worker
{
public MyMethod()
{
// I need to call a method on the UI (this code doesn't run in the UI thread),
// but I don't have any field in this object holding an UI control
// value. So, I can't write myControlField.Invoke(...),
// but I still need to call a method on the UI thread
}
}
Any suggestions?
From a library there's really no way to guarantee your thread context, so your safest bet is to have the consume provide the invoker and leave it to them to ensure it was created in the proper context. Something like this pattern:
class Foo
{
private Control m_invoker;
public Foo()
: this(null)
{
}
public Foo(Control invoker)
{
if (invoker == null)
{
// assume we are created on the UI thread,
// if not bad things will ensue
m_invoker = new Control();
}
else
{
m_invoker = invoker;
}
}
public void Bar()
{
m_invoker.Invoke(new Action(delegate
{
// do my UI-context stuff here
}));
}
}
I'm sorry if this isn't a real answer, but I think it may help:
The reason why WinForms has this approach -- using a Control or Form reference to access a Invoke method that enables you to run code on the UI Thread -- is that the only reason you should have to run a code in the UI Thread is if you are going to write/change the state of UI components.
Of course, if you are going to do that, you must have a reference to a UI component. So you'd have access to its Invoke method. I cannot think of any other reason you'd have to access the UI thread from a component other than to modify a visual element.
It must be invoke ... But invoke have to wait still main thread i mean you not get error this way but this is not exacly working parallel if you want to go more than one process at same time just create more then one thread
Thread thread = new Thread(new delegate_method(method));
thread.start ();
Thread thread2 = new Thread(new delegate_method(method2));
thread.start ();
handle two process same time
void method ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}
void method2 ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}
void finish_thread()
{
if(invoke.Required)
{
//Here you have to call delegate method here with UI
BeginInvoke(new delegate_method(finish_thread));
}
else
{
//Now you can control UI thread from here and also you finished background work
//Do something working with UI thread
textBox.Text = "";
}
}
//Declare this in class
public delegate void delege();
//Write this lines when you want to background thread start
Thread thread = new Thread(new ThreadStart(() => {
//Do what you what with backgorund threading , can not use any interface comand here
BeginInvoke(new delege(() => {
//Do here any main thread thread job,this can do interface and control jobs without any error
}));
}));
thread.Start();

Making a Controls.Add method call thread-safe

Let's say I have the following code:
public void Inject(Form subform)
{
this.tabControl1.TabPages[1].Controls.Add(subform);
this.Refresh();
}
How can I convert the Controls.Add() call to a thread-safe call, using Control.Invoke?
The only way to make Control.Add thread safe is to make sure it's called from the UI thread. This also implies that the Control being added is usable from the UI thread.
Here is a function though which produces a delegate which can add to a Control from any thread (presuming the added Control is OK on the UI thread).
public Action<Control> GetAddControl(this Control c)
{
var context = SynchronizationContext.Current;
return (control) =>
{
context.Send(_ => c.Controls.Add(control), null);
};
}
Then for a given Control you can pass the resulting delegate to any thread.
// From UI thread
Action<Control> addControl = c.GetAddControl();
// From background thread
addControl(subForm);

Categories

Resources