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);
Related
I have a big problem and hope you guys can help me...
I need to start a Thread called ListenOnSocket, no problem so far..
Thread ListenOnSocket = new Thread(newThreadStart(Form1.ListenOnSocket));
ListenOnSocket.Start();
But when I want to change the label from within ListenOnSocket, I get an object reference is required for the non-static field, method or property.
So normally you would passe the label on by doing this
public static void ListenOnSocket(Label lblstatus)
{
//i want to change the label from here.
lblstatus.text = "Hello";
}
but then I get
No overload for ListenOnSocket matches delegate System.Threading.Threadstart
in my threadstart.
Can anyone please help, I am really stuck, sorry if there is not much to go on I am quite new to C#.
You can use Lambda Expression to pass parameter.
Thread ListenOnSocket = new Thread( () => { Form1.ListenOnSocket(yourParameter); } );
ListenOnSocket.Start();
But you will get the CrossThreadException when the ListenOnSocket method execute. So you need to use BeginInvoke to set label text.
So search the CrossThreadException and why you will get it.
Note: I do not write the sample code for this, because searching is more beneficial.
You need to marshal this back to the UI thread:
public static void ListenOnSocket(Label lblstatus)
{
this.BeginInvoke(new Action(() =>
{
//i want to change the label from here.
lblstatus.text = "Hello";
});
}
It looks like you might actually want a ParameterizedThreadStart here.
You would pass the Label in as its parameter.
Control changes also need to be performed on the UI thread.
public void DoSomething()
{
// Actually a ParameterizedThreadStart, but you don't need to state this explicitly
//var listenOnSocket = new Thread(new ParameterizedThreadStart(ListenOnSocket));
var listenOnSocket = new Thread(ListenOnSocket);
// Pass the label as the ParameterizedThreadStart parameter
// TestLabel is a label within the form
listenOnSocket.Start(TestLabel);
}
private void ListenOnSocket(object statusLabelObject) // Parameter must be of type Object
{
var statusLabel = statusLabelObject as Label;
if (statusLabel == null)
throw new ArgumentException(#"Parameter must be of type Label", "statusLabelObject");
// Changes to controls must be performed on the UI thread.
BeginInvoke((Action)(() => statusLabel.Text = #"text"));
}
They key gotcha here is it's not valid to update a UI element (like a label) from a background thread.
If you have a long running task then you probably don't want to run that on the UI thread as it will block.
Assuming that you're creating a thread because you have something that runs for too long to run on the UI thread, it might be worth looking into way of marshalling calls from background threads onto the UI thread.
For more information on how to do this see How to update the GUI from another thread in C#? if you're looking to update the status from a long running task, you might want to look into background worker: MSDN: How to Use Background Worker which is a helper class designed to help with long running background tasks.
I've been trying to implement multi-class-thread GUI management. As In I want different threads spread across multiple classes in different .cs files to update the UI as needed.
I've searched stackoverflow and other sources and found that most people use Dispatcher.Invoke or something similar. So I decided to start testing...
So below is a thread in a class called wThread.cs,
public class wThread
{
public EventHandler SignalLabelUpdate;
public Dispatcher uiUpdate;
public wThread()
{
uiUpdate = Program.myForm.dispat;
//the uiUpdate seems to be null for some reason... If i am doing it wrong how do i get the dispatcher?
Thread myThread = new Thread(run);
myThread.Start();
}
Action myDelegate = new Action(updateLabel);
// is there a way i can pass a string into the above so updatelabel will work?
public void updateLabel(String text)
{
if (SignalLabelUpdate != null)
SignalLabelUpdate(this, new TextChangedEvent(text));
}
public void run()
{
while (uiUpdate == null)
Thread.Sleep(500);
for (int i = 0; i < 1000; i++)
{
//I hope that the line below would work
uiUpdate.BeginInvoke(new Action(delegate() { Program.myForm.label1.Text = "count at " + i; }));
// was also hoping i can do the below commented code
// uiUpdate.Invoke(myDelegate)
Thread.Sleep(1000);
}
}
}
Below is my form1.cs it's the pre-loaded code from visual studio 2012,
public partial class Form1 : Form
{
public Dispatcher dispat;
public Form1()
{
dispat = Dispatcher.CurrentDispatcher;
InitializeComponent();
wThread worker = new wThread();
}
}
Most of my questions are in the comments above but here are them listed:
The uiUpdate seems to be null for some reason... If i am doing it wrong how do i get the dispatcher? (wThread.cs problem)
uiUpdate = Program.myForm.dispat'
Is there a way i can pass a string into the above so updatelabel will work?
Action myDelegate = new Action(updateLabel);
I hope that the line below would work
uiUpdate.BeginInvoke(new Action(delegate() { Program.myForm.label1.Text = "count at " + i; }));
Was also hoping i can do the below commented code
uiUpdate.Invoke(myDelegate)
EDIT: I moved the wThread constructor wThread worker = new wThread() out of the form1 initialization area... and it fixed my nullpointer. Instead I move the wThread constructor into the static main void where the form is constructed... like Application.Run(myForm);
Unfortunately the wThread will not start until I close the UI.. What is the best thing to do about this? Make another thread before the Application.Run starts my Form and use that thread to start my real thread?
#1: In Form1 constructor, place a breakpoint at dispat = Dispatcher.CurrentDispatcher; and check what's the value. It may be just that the Dispatcher.CurrentDispatcher is null and you have to get it i.e. a bit later, not right in the ctor
#2 yes. Use Func<string> instead of action. The Invoke and BeginInvoke take a delegate, and also a set of params object[] that form the parameters of invocation of the delegate. Of course, the passed parameters array must exactly match the delegate signature, so when using Func<string> use a object[] with just one item: the string.
#3 - yes, that would work, as long as the i is a simple local variable thatcan be captured by the delegate closure. If it's foreach iterator or some nonlocal member, it can have problems, but, well, different story.
#4 - yes it would work, just remember about passing the params for Func<string> too.
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();
I have a UserControl with a tree on it. It uses multithreading to add nodes to it. I have a function called Expand which I need to execute after filtering completed and since I'm a newbie with multithreading I'm not sure how to do that. Here's my code:
class MyClass : UserControl
{
private Thread nThread;
private bool searchLoadCompleted = false;
private void Filter()
{
ClearTree();
this.nThread = new Thread(new ParameterizedThreadStart(AddFilteredResultsToTree));
this.nThread.IsBackground = true;
this.nThread.Start(someParameter);
}
private void AddFilteredResultsToTree(int someParameter)
{
myTree.Invoke(new MethodInvoker( ()=> this.searchLoadCompleted = false ));
myTree.Invoke(new MethodInvoker( ()=> AppendNode(......) ));
myTree.Invoke(new MethodInvoker( ()=> this.searchLoadCompleted = true ));
}
private void Expand()
{
}
}
I tried to add nThread.Join() into Expand() but it got stuck indefinitely. What should I do?
If the singlethreaded version of this is:
ClearTree();
AddFilteredResultsToTree(someparameter);
Expand();
Don't bother going multithreading, just do it on the same thread. The point of using multithreading there would be to let the main thread handle UI events, if you join the thread then you're basically just launching a background thread while freezing (not doing any work) in the main thread. Note that by calling Invoke you're actually delegating the execution of AddFilteredResultsToTree to the main thread anyway.
I'd suggest you simply call Expand from AddFilteredResult and use the Dispatcher to update the UI if needed or.
Another way to go (best in my opinion) would be to use the Async Pattern for this (example and tutorial here), and then on the AsyncCallback update the UI.
Calling Invoke will block both the GUI thread and your worker thread so there won't be any performance improvement over code without a worker thread.
We have a silverlight application which uses a dispatcher and I would appreciate any help explaining what the following codes does? (unfortunately the developer who wrote the code has left).
So what we have is the following:
public class ABC
{
private Dispatcher dispatcher;
private Thread threadRunner;
public void ABC()
{
threadRunner= new Thread(ThreadRunnerMethod)
{
IsBackground = true,
ApartmentState = ApartmentState.STA
};
threadRunner.Start();
}
private static void ThreadRunnerMethod()
{
Dispatcher.Run();
}
public void MainMethod()
{
dispatcher = Dispatcher.FromThread(threadRunner);
dispatcher.Invoke(new Action(() =>
// "DO SOME WORK WITH A COM OBJECT"
));
}
}
I have some basic experience with threading but I have no idea how this all works?
JD
It's the equivalent of Control.Invoke in Windows Forms, basically - it's just been separated into its own object.
As I understand it, Dispatcher.Run will basically start an event loop, and you can marshall calls into that event loop using Dispatcher.Invoke. Dispatcher.FromThread finds the Dispatcher object which is responsible for a given thread - so in this case, it finds the event loop running in the new thread.
So in your code, the delegate created with the lambda expression will execute in the newly created thread.