Update controls created in another thread? - c#

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.

Related

How to access the UI thread?

I have a Winform APP that does a simple task.
There is an event listener for a button that create a new thread:
ThreadStart work = (addToList);
Thread thread = new Thread(work);
thread.Start();
Now I need the second part of the calculation ot be done with the UI thread (so my instruction ask)
"
Note that you need to access the list box within the UI thread (hint: you can use Control.Invoke for WinForm
"
Any idea how to do so?
The Invoke method needs a delegate (reference to a function) and will schedule that on UI thread. Considering that all the elements on the form share the same thread as UI thread, you can use the Invoke method from either the form or any of its controls:
class MyForm
{
private void Button_Click(object sender, EventArgs e)
{
var thread = new Thread(new ThreadStart(FullCalculation));
thread.Start();
}
private void FullCalculation()
{
OffUIThreadCalculation();
this.Invoke(OnUIThreadCalculation); // Schedules OnUIThreadCalculation to run on the UI thread of `this`, aka the form
}
private void OffUIThreadCalculation()
{
// UI elements should not be used here
}
private void OnUIThreadCalculation()
{
// UI elements can be used here
}
}

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.

How to ask the GUI thread to create objects?

I've got the following program flow in my Windows Forms application (WPF is not a viable option unfortunately):
The GUI Thread creates a splash screen and a pretty empty main window, both inheriting Form.
The splash screen is shown and given to Application.Run().
The splash screen will send an event which triggers an async Event Handler which performs initialization, using the IProgress interface to report progress back to the GUI. (This works flawlessly.)
At some point during the initialization, I need to dynamically create GUI components based on information provided by certain plugins and add them to the Main Window.
At this point I'm stuck: I know I need to ask the GUI thread to create those components for me, but there is no Control I could call InvokeRequired on. Doing MainWindow.InvokeRequired works neither.
The only idea I could come up with was to fire an event which is connected to a factory in the GUI Thread, and then wait for that factory to fire another event which provides the created controls. However I am pretty sure there is a more robust solution. Does anyone know how to achieve this?
Using the comments on my question, especially the note about the continuation method which made me find this very useful question, I achieved the following:
The first part of initialization is performed asynchronously (no change).
The second part of the initialization (which creates the UI elements) is performed afterwards as a Continuation Task, in the context of the UI thread.
Apart from the rather short GUI initialization part, the Splash Screen is responsive (i.e. the mouse cursor does not change to "Waiting" once it hovers the Splash Screen).
Neither of the initialization routines knows the splash screen at all (i.e. I could easily exchange it).
The core controller only knows the SplashScreen interface and does not even know it is a Control.
There currently is no exception handling. This is my next task but doesn't affect this question.
TL;DR: The code looks somewhat like this:
public void Start(ISplashScreen splashScreen, ...)
{
InitializationResult initializationResult = null;
var progress = new Progress<int>((steps) => splashScreen.IncrementProgress(steps));
splashScreen.Started += async (sender, args) => await Task.Factory.StartNew(
// Perform non-GUI initialization - The GUI thread will be responsive in the meantime.
() => Initialize(..., progress, out initializationResult)
).ContinueWith(
// Perform GUI initialization afterwards in the UI context
(task) =>
{
InitializeGUI(initializationResult, progress);
splashScreen.CloseSplash();
},
TaskScheduler.FromCurrentSynchronizationContext()
);
splashScreen.Finished += (sender, args) => RunApplication(initializationResult);
splashScreen.SetProgressRange(0, initializationSteps);
splashScreen.ShowSplash();
Application.Run();
}
It is much easier to manage multiple forms and display one while the other is working or being constructed.
I suggest you try the following:
When application is started you create splash screen form so your Program.cs is like this
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SplashForm());
}
Inside the splash form constructor, create a new thread (I will use BackgroundWorker but there are other options like tasks) to build your main form.
public SplashForm()
{
InitializeComponent();
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
}
Now we need to write the SplashForm member functions to tell background worker what to do
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Perform non-GUI initialization - The GUI thread will be responsive in the meantime
// My time consuming operation is just this loop.
//make sure you use worker.ReportProgress() here
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
SetVisible(false);
MainForm mainForm = new MainForm();
mainForm.ShowDialog();
//instead of
//this.Visible = false;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
You might have noticed by now, I am using another member function to hide the splash screen. It is because you are now in another thread and you can't just use this.visible = false;. Here is a link on the matter.
delegate void SetTextCallback(bool visible);
private void SetVisible(bool visible)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetVisible);
this.Invoke(d, new object[] { visible });
}
else
{
this.Visible = visible;
}
}
When I run this sample project it shows the progress bar and then loads the MainForm windows form after hiding the SplashForm.
This way you can put any controls that you might need inside the MainForm constructor. The part you shortened as // Perform GUI initialization afterwards in the UI context should go into MainForm constructor.
Hope this helps.

Showing a progress bar that updates while creating a lot of controls

I've written a piece of code that creates a lot of controls and layouts them on a canvas to visualize a tree. Now this code can take a lot of time, especially since it sometimes has to query an external service to see if there are more child node.
So I would like to show a progress bar while this code is executing. For other parts of my program I use a background worker that reports progress. However since I have to create controls that are later interact-able I don't see how to use a background worker or other threading solution here.
Since this is WPF, I also can't call Application.DoEvents(). So my question is, how can I create a lot of controls while still being able to periodically update the visual part of the GUI?
For my other code I use an Adorner that I layout over the busy piece of my app, I would prefer a solution where I can keep using that, I would also still prefer a solution using BackgroundWorker, but I'm pretty sure that is not possible.
I've looked at other SO topics, but I can't find a good answer so far
Creating controls in a non-UI thread
Creating a WinForm on the main thread using a backgroundworker
Edit:
According to this MSDN article http://msdn.microsoft.com/en-us/magazine/cc163328.aspx the BackgroundWorker should automatically invoke asynchronously on the UI thread if required, but this is not the behaviour I'm seeing, since I still see a cross thread exception.
Edit2: nvm, that's not totally true: BackgroundWorker still needs to call Invoke?
Edit3: After some more reading and some tips, this is the solution I've come to. Anybody got any tips/hints?
// Events for reporting progress
public event WorkStarted OnWorkStarted;
public event WorkStatusChanged OnWorkStatusChanged;
public event WorkCompleted OnWorkCompleted;
private BackgroundWorker worker;
private delegate void GuiThreadWork(object state);
private PopulatableControlFactory factory = new PopulatableControlFactory();
public Canvas canvas;
public void PerformLayout(TreeNode node)
{
OnWorkStarted(this, "Testing");
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync(node);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
OnWorkCompleted(this);
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var workTuple = (Tuple<GuiThreadWork, TreeNode>)e.UserState;
workTuple.First.Invoke(workTuple.Second); //Or begin invoke?
if (OnWorkStatusChanged != null)
OnWorkStatusChanged(this, e.ProgressPercentage);
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
TreeNode node = (TreeNode)e.Argument;
Thread.Sleep(1000);
worker.ReportProgress(33, Tuple.New(Place(node), node));
Thread.Sleep(1000);
worker.ReportProgress(66, Tuple.New(Place(node.children[0]), node.children[0]));
Thread.Sleep(1000);
worker.ReportProgress(100, Tuple.New(Place(node.children[1]), node.children[1]));
}
private GuiThreadWork Place(TreeNode node)
{
GuiThreadWork threadWork = delegate(object state)
{
PopulatableControl control = factory.GetControl((TreeNode)state);
Canvas.SetLeft(control, 100);
Canvas.SetTop(control, 100);
canvas.Children.Add(control);
};
return threadWork;
}
In short: I use the progressChanged event of the background worker because this is always marshalled to the GUI thread. I pass it a tuple of a delegate and some state. This way I always create the control on the GUI thread and do all actions there, while still being flexible.
Generally I don't use BackgroundWorker often but I can suggest the following:
Logic for DoWork - its executed on non UI thread
get count of nodes so you can report real progress
begin building tree ( and call Invoke on UI Dispatcher so UI thread
is adding nodes) and report progress to ReportProgress as (already
added nodes)/(total count nodes) while enumerating through all nodes
in ProgressChanged simply update some ProgressBar with new value

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