switching to dispatcher thread in WPF - c#

This is the follow up to this Question.
I have to update a ObservableCollection from a different Thread. I tried it with the following Code:
Thread t = new Thread( ()=>
{
while(true)
{
if (ErrorDetection.ErrorDetectionIO.doErrorDetection() == 1)
{
dataLine = ErrorDetection.ErrorDetectionIO.getDataLine();
if (mainWindow != null)
{
ISynchronizeInvoke target = mainWindow; // mainWindow needs to be an WindowsForm?
target.Invoke(
(Action)(() =>
{
mainWindow.setNewDataLine(dataLine);
}
), null);
}
}
}
} );
t.IsBackground = true;
t.Start();
ErrorDetectionIO.doErrorDetection() is in a c++/cli .dll and calls native c Code.
setNewDataLine is on the mainWindow and adds a Line to the Observable Collection.
If its called from a different Thread it causes an exception:
"This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."
The Problem is that ISynchronize Invoke does not seem to work with wpf? Threres an compiler error message that mainWindow canĀ“t be converted to ISynchronizeInvoke.
if I use
ISynchronizeInvoke target = mainWindow as ISynchronizeInvoke;
it can be compiled but target is null;

You can just use mainWindow.Dispatcher.Invoke instead of trying to cast to ISynchronizeInvoke. Dispatcher.Invoke will provide the correct marshaling for WPF.
Note that .NET 4.5 adds the ability for WPF to handle this automatically by setting BindingOperations.EnableCollectionSynchronization.

You should check out some of the many ThreadSafeObservableCollection implementations. These will wrap up the problem of updating an ObservableCollection from a background thread quite nicely!

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

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

Multithreading Calling a Delegate

To quote Marc Gravell:
///...blah blah updating files
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
someLabel.Text = newText; // runs on UI thread
});
///...blah blah more updating files
I'm looking to do this with WPF so can't use the invoke method. Any thoughts? This Threading stuff is doing my head in :/
MORE DETAIL
I began my new Thread like so
Thread t = new Thread (LoopThread);
t.Start();
t.Join();
But throughout LoopThread, I want to write to the UI.
UPDATE
Thanks to Jon Skeet for the Dispatcher.Invoke bit. Seems MethodInvoker is WinForms also. WPF equivalent?
UPDATE 2
Thanks Adriano for suggesting instead of System.Windows.Forms.MethodInvoker, using System.Action.
(You guys were right about the this parameter confusion, just need to build to remove errors.)
Bus since adding the SimpleInvoke, now I'm hit with
Extension method must be defined in a non-generic static class
on the line
public partial class MainWindow : Window
Any thoughts?
In WPF, you just use Dispatcher.Invoke instead of Control.Invoke.
The DispatcherObject class (which WPF classes derive from) exposes a Dispatcher property, so you just need:
Dispatcher.Invoke((Action) delegate {
someLabel.Text = newText; // runs on UI thread
});
If you're using C# 3 or higher (and .NET 3.5 or higher) you might want to add an extension method to DispatcherObject:
// Make this a new top-level class
public static class DispatcherObjectExtensions
{
public static void SimpleInvoke(this DispatcherObject dispatcherObject,
Action action)
{
dispatcherObject.Dispatcher.Invoke(action);
}
}
So you can just use:
// From within your UI code
this.SimpleInvoke(() => someLabel.Text = newText);
as compromise using SynchronizationContext:
// gui thread
var sc = SynchronizationContext.Current;
// work thread
sc.Post(s =>
{
someLabel.Text = newText
}, null);
Continuing from Jon Skeet comment, and you can call your extension like below
DispatcherObject.SimpleInvoke(() => someLabel.Text = newText);

using the objects which are created by other thread

I just want to change the window's background in another thread. there are two program, one is work right, and the other throw an InvalidOperationException.
The right code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Thread t = new Thread(new ParameterizedThreadStart(threadTest));
t.Start(#"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg");
}
void threadTest(object obj)
{
string path = obj as string;
this.Dispatcher.Invoke(new Func<object>(() => this.Background = new
}
}
the Error Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Thread t = new Thread(new ParameterizedThreadStart(threadTest));
t.Start(#"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg");
}
void threadTest(object obj)
{
string path = obj as string;
//this.Dispatcher.Invoke(new Func<object>(() => this.Background = new ImageBrush(new BitmapImage(new Uri(path)))));
ImageBrush background = new ImageBrush(new BitmapImage(new Uri(path)));
this.Dispatcher.Invoke(new Func<object>(() => this.Background = background));
}
}
the different between these codes is that, the error code create the ImageBrush object in the child thread.
So my question is that: in the wpf program, is the thread can only use the objects creates by own thread?
thanks for any reply.
Yes, you are right. Only the UI thread can use objects created by it. So, you can use the Dispatcher to "enqueue" the UI operations on it's proper thread.
Answering your second question, sure, there's a way to "pass" objects to the UI Thread. If you see the BeginInvoke structure (of the Dispatcher) it's:
public DispatcherOperation BeginInvoke(
Delegate d,
params Object[] args
)
Where the args is the params object array, there's where you put the params.
Now, if you are using some Freezable object (for example some Image, Brush, Transform or Geometry) then you need to object.Freeze(); before send it to the UI Thread.
Yes, correct, It's not only about WPF, but in general, about Windows programming.
You can not update UI object from other thread different from its own.
The reason for this is simply because, the message pumping and especially delivery to destination control of OS must be guaranteed. This is naturally valid for communication using SendMesage, but for PostMessage too.
If You create an object on a separate thread, You might use it on gui thread if You freeze it first. See Freezable objects.

Dispatcher.CurrentDispatcher?

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.

Categories

Resources