A WPF app has an operation of loading a user control from a separate file using XamlReader.Load() method:
StreamReader mysr = new StreamReader(pathToFile);
DependencyObject rootObject = XamlReader.Load(mysr.BaseStream) as DependencyObject;
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
The process takes some time due to the size of the file, so UI becomes frozen for several seconds.
For keeping the app responsive I try to use a Background thread for performing the part of operation that is not directly involed in UI updating.
When trying to use BackgroundWorker I got an error: The calling thread must be STA, because many UI components require this
So, I went another way:
private Thread _backgroundThread;
_backgroundThread = new Thread(DoReadFile);
_backgroundThread.SetApartmentState(ApartmentState.STA);
_backgroundThread.Start();
void DoReadFile()
{
StreamReader mysr3 = new StreamReader(path2);
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action<StreamReader>)FinishedReading,
mysr3);
}
void FinishedReading(StreamReader stream)
{
DependencyObject rootObject = XamlReader.Load(stream.BaseStream) as DependencyObject;
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
}
This solves nothing because all time consuming operations remain in UI thread.
When I try like this, making all parsing in the background:
private Thread _backgroundThread;
_backgroundThread = new Thread(DoReadFile);
_backgroundThread.SetApartmentState(ApartmentState.STA);
_backgroundThread.Start();
void DoReadFile()
{
StreamReader mysr3 = new StreamReader(path2);
DependencyObject rootObject3 = XamlReader.Load(mysr3.BaseStream) as DependencyObject;
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action<DependencyObject>)FinishedReading,
rootObject3);
}
void FinishedReading(DependencyObject rootObject)
{
ContentControl displayPage = FindName("displayContentControl") as ContentControl;
displayPage.Content = rootObject;
}
I got an exception: The calling thread cannot access this object because a different thread owns it. (in the loaded UserControl there are other controls present which maybe give the error)
Is there any way to perform this operation in such a way the UI to be responsive?
Getting the XAML to load an a background thread is essentially a non-starter. WPF components have thread affinity and are generally speaking only usable from the threads they are created one. So loading on a background thread will make the UI responsive but create components which then cannot be plugged into the UI thread.
The best option you have here is to break up the XAML file into smaller pieces and incrementally load them in the UI thread making sure to allow for a message pump in between every load operation. Possibly using BeginInvoke on the Dispatcher object to schedule the loads.
As you found out, you can't use XamlReader.Load unless the thread is STA and even if it is, you will have to have it start a message pump and funnel all access to the controls it created through that. This is a fundamental way of how WPF works and you can't go against it.
So your only real options are:
Break down the XAML into smaller pieces.
Start a new STA thread for each Load call. After Load returns, the thread will need to start a message loop and manage the controls it created. Your application will have to take into account the fact that different controls are now owned by different threads.
I don't have exact solution but you can get some direction from following links.
http://www.codehosting.net/blog/BlogEngine/post/Opening-WPF-Windows-on-a-new-thread.aspx
http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
System.Xaml has a Xaml​Background​Reader class, perhaps you could get that to work for you. Parser the XAML on the background thread but build the objects on the UI thread.
You can call a method that allows to give control to another thread:
http://msdn.microsoft.com/en-us/library/ms748331.aspx
It is called .Freeze()
Related
I have to show a progress window in a different thread.
This is what I've done:
Thread loadT = new Thread(new ThreadStart(() =>
{
Loading ldd = new Loading();
ldd.SetContentMessage("Loading...");
ldd.Closed += (s, ec) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
ldd.ShowDialog();
}));
loadT.SetApartmentState(ApartmentState.STA);
loadT.Start();
//do something
loadT.Abort();
But I don't think this is the right way. I want to use this window for different processes and i also want to set the window on the top of the others. Which is the best approach?
Ty!
You should do this the other way around when you are waiting for data and stuff to load.
Loading ldd = new Loading();
ldd.SetContentMessage("Loading...");
ldd.ShowDialog();
Thread loadT = new Thread(new ThreadStart() =>
{
//Do stuff here
});
loadT.Start();
Then you can get setup some events and such to either post updates to the loading window, or just leave it as is. You can also either monitor the threads state within the Loading window and close itself when the thread is complete or close the window from the thread.
as an example you could modify Loading to take a Thread as its parameter.
Thread loadT = new Thread(new ThreadStart() =>
{
//Do stuff here
});
Loading ldd = new Loading(loadT);
ldd.ShowDialog();
You can then move the starting of the thread, and monitoring of the thread/closing the window into the Loading class and it can look after itself.
There are 900,000 ways you can do this. You can also use BackgroundWorkers instead of spawning a new Thread, or you can use async/await in .Net 4.5+. Threading like this has been exhaustively done in the past and there should be lots of resources on google to help you in whatever path you decide to take. The important takeaway from this is your window should really be on the UI thread, and your loading should be done on another thread, not the other way around.
You could refer to the following blog post for an example of how to launch a WPF window in a separate thread the right way: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/.
But you won't be able to mix controls that are created on different threads. A control can only be accessed on the thread on which it was originally created so it makes no sense to create a control on one thread and then trying to use it on another because this simply won't work because of the thread affinity.
Displaying a stand-alone top-level read-only window during the time a long-running operation is in progress is fine but you should probably close this window as soon as the operation has completed. You won't be able to move controls from this window to another one that was created on another thread anyway.
I am just starting to deal with STA/MTA issues, so apologies for the simplicity of the question. I couldn't find an answer I could actually understand at the bottom of the ladder here.
I am writing a plugin for another piece of software, and come to a point in a worker thread that I need to create some UI elements. I understand that I cannot do that from inside the worker thread since it is not an STA thread, and that I need to get back to the Main (or just another?) STA thread to create the UI elements. Some clarifications would help greatly.
Do all STA threads have the same 'rights', i.e. if the main thread is STA and creates a Window, adds some UI elements to it. Then spawns off another STA thread, and that second thread likewise creates some UI elements, are they doing it in the same 'space' (poor word choice, but I don't know what else to use) and can access each other's UI elements without causing death and destruction? Or do I need to explicitly jump back to the Main/Original STA thread and ONLY ever create UI elements from THAT (not just ANY) STA thread?
If that is the case (only 1 STA thread is allowed to make UI elements) how do I do that correctly? I have seen many posts that related to this but for some reason I can't quite catch what's going on, and would love a REAL simple answer.
Please no 'Here's a cool slick way of doing...' I just need the simple way of at the point of execution where I need some UI elements jumping back over to the main STA thread if that's what's necessary.
If it is not necessary, then I will just make that worker thread an STA thread and continue on my way, is that fair? Or am I courting disaster?
if the main thread is STA and creates a Window, adds some UI elements to it. Then spawns off another STA thread, and that second thread likewise creates some UI elements, are they doing it in the same 'space' [snip...] and can access each other's UI elements without causing death and destruction?
If Thread A and B are both STA, then they can each create and update their own UI elements, but not eachothers. Any other threads that want to affect the UI have to use one of the BeginInvoke style methods to ask the appropriate thread to do the update.
If it is not necessary, then I will just make that worker thread an STA thread and continue on my way, is that fair? Or am I courting disaster?
You may not be able to make the worker thread an STA thread if it's been set to MTA and initialized. You may have to make a new thread.
How do you do it? It seems like you want to use WPF (System.Windows.*) for your UI - so If the app that you are "plugging into" is also using WPF, you should be able to access it and re use it's UI thread. If not, you can make a new thread, and create a new Application on it and call Run. This should set up a dispatcher for you.
Something like this (pseudo code sort-of copied from some working code I have elsewhere)
Dispatcher dispatcher = null; // we 'get to' the UI thread via the dispatcher
if(Application.Current) { // re use an existing application's UI thread
dispatcher = Application.Current.Dispatcher;
} else {
var threadReadyEvent = new ManualResetEvent(false);
var uiThread = new Thread(() => {
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
var application = new Application();
application.Startup += (sender, args) => {
dispatcher = application.Dispatcher;
threadReadyEvent.Set();
};
// apps have to have a "main window" - but we don't want one, so make a stub
var stubWindow = new Window {
Width = 1, Height = 1,
ShowInTaskbar = false, AllowsTransparency = true,
Background = Brushes.Transparent, WindowStyle = WindowStyle.None
};
application.Run(stubWindow);
}){ IsBackground = true };
uiThread.Start();
threadReadyEvent.WaitOne();
threadReadyEvent.Dispose();
}
dispatcher.Invoke(() => {
// ask the UI thread to do something and block until it finishes
});
dispatcher.BeginInvoke(() => {
// ask the UI thread to do something asynchronously
});
and so forth
If a thread creates a control. Only this specific thread can interact with it, even if there are other STA threads.
In WinForms you would invoke a method on the control: Control.Invoke.In WPF you have the dispatcher to do it: Dispatcher.Invoke.
WinForms:
form1.Invoke(/* a delegate for your operation */)
WPF:
window1.Dispatcher.Invoke(/* a delegate for your operation */)
What you do is instead of changing an object in a "single apartment" you ask (invoke) the STA thread in control of it to do it (the delegate you invoke) for you. You also have BeginInvoke for doing it asynchronously.
I'm new to WPF (and computer science in general) and I was given a small project from my boss where there are 5 tabs, each that go to a different site. After logging in, the user is directed to the website of the 1st tab. In the background, the 4 other tabs should be loading in the background.
Each has the following name: "tabItem1", "tabItem2", "tabItem3", "tabItem4" up to "tabItem5"
Inside each there is a up to "webBrowser5".
I think that I have to use threading to load the pages in the background, but I'm not sure how to implement it. I tried creating 4 different threads in the MainWindow such as:
public MainWindow()
{
InitializeComponent();
Thread thread1 = new Thread(Update1);
thread1.SetApartmentState(ApartmentState.STA);
thread1.Start();
Thread thread2 = new Thread(Update2);
thread2.SetApartmentState(ApartmentState.STA);
thread2.Start();
Thread thread3 = new Thread(Update3);
thread3.SetApartmentState(ApartmentState.STA);
thread3.Start();
Thread thread4 = new Thread(Update4);
thread4.SetApartmentState(ApartmentState.STA);
thread4.Start();
}
private void Update1()
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
tabItem2.Focus();
}
);
}
private void Update2()
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
tabItem3.Focus();
}
);
}
//...Goes up to Update4
This seems to only focus on the last thread and also it doesn't do it the background. I appreciate any guidance and help. Thanks!
That IS what the dispatcher is doing, operating on the main thread. You told the main thread to set focus because the object is on the main thread.
The problem:
The answer lies in why you must do this. You cannot operate on a windows control in any other thread than the thread it was created on. You also can't create a control in one thread, and set it as a child of a control in another thread.
What this means to you... is that what your boss asked you to do can't be done. The only thing you can do in the background is calculate algorithms. At best, you'll be able to load data, and operate on data, and interpret data, but if you want to have that data be displayed in, or converted into, windows controls, you must do that on the main thread.
The only solution:
However, you can have multiple UIThreads. Which means you can create multiple Windows. So, the must-do alternative is to create windows on separate threads for each tab content, then host the thread-windows on each tab.
Threading windows
Host process window
Cross thread hosting
I do not agree with Xaade, I think you can do what you need using only this code:
Dispatcher.BeginInvoke((Action)(() =>
{
// load the pages
}), DispatcherPriority.Background, null);
That code will be executed in background, so, there you can load every webBrowser you need.
You just need to specify the DispatcherPriority to Background.
I am trying to run an application with at least two threads: One form for the user interface and one or more worker threads. They are jointly reading/writing from a static class through a number of other classes.
This is why I am passing an instance of the worker class to the display form. I guess that is why it goes wrong for me:
public class CoCoon
{
private Screen displayForm;
private Worker worker;
public ThreadedApp()
{
worker = new Worker (1024);
displayForm = new Screen(worker);
}
public void Run()
{
//thread 1: display form
Thread screenThread = new Thread(() => Application.Run(displayForm));
//thread 2: background worker
Thread workerThread = new Thread(worker.Run) {IsBackground = true};
screenThread.Start();
Thread.Sleep(1000); //if I don't wait a while, I get an ObjectDisposedException!
workerThread.Start();
}
The threads and objects are initiated just fine, but as soon after the Form_Load method is has been handled, an error is thrown on the Application.Run(displayForm) line above. It is an NotSupportedException, with a remark that I should use Control.Invoke. But I am not sure I understand, because I am not letting threads other than the display form's use the controls on it.
I am new to threading. Can anyone help me on my way? Thanks!
PS: One detail - I am developing this for the Windows Mobile platform.
EDIT After popular request hereby the Stack Trace
at Microsoft.AGL.Common.MISC.HandleAr(PAL_ERROR ar)\r\n at
System.Windows.Forms.Control.get_Visible()\r\n at
System.Windows.Forms.Form._SetVisibleNotify(Boolean fVis)\r\n at
System.Windows.Forms.Control.set_Visible(Boolean value)\r\n at
System.Windows.Forms.Application.Run(Form fm)\r\n at
CoCoonWM6.CoCoon.<Run>b__1()\r\n
I recommend that you only have one UI thread, the main thread. You can use your other threads for background operations, but keep all UI stuff on the main thread.
The UI thread should be the only one calling Application.Run. WinForms has other requirements for the UI thread (such as being STA), and those are satisfied by the main thread. In theory, it may be possible for WinForms to support two UI threads, but it's certainly not easy.
I normally recommend other forms of synchronization when you need to update UI controls from a background thread - TaskScheduler or SynchronizationContext. On the mobile platform, unfortunately, your only option is Control.Invoke.
Check out the stack trace for the exception (and post it). You are almost certainly accessing some Control from the worker thread.
This is how you can modify access to a Control (in this example a Label) after you find where you are accessing controls from non-UI threads:
if (label13.InvokeRequired)
{
ChangeTextDelegate changeText = new ChangeTextDelegate(anyChangeTextMethod);
label13.Invoke(changeText, new object[] { newText });
}
else
{
label13.Text = newText;
}
Looks like you're trying to use GUI elements in the background thread. That would explain why you have to call Sleep (otherwise the form and its controls don't finish loading before you try to use them) as well as the Control.Invoke exception (you can't use GUI elements from a non-UI thread). See the docs for Control.Invoke for how you should use it.
Since you don't have BackgroundWorker and Px in the CF, you're indeed forced to use threads directly - though the ThreadPool would probably be better than instantiating new threads, most of the time.
I am trying to reuse a UserControl and also borrow some logic that keeps track of progress. I'll try and simplify things. MyWindow.xaml includes a MyUserControl. MyUserControl has its own progress indicator (Formatting in progress..., Copying files..., etc.) and I'd like to mirror this progress somewhere in the MyWindow form. But, the user control has some logic I don't quite understand. I've read and read but I still don't understand the Dispatcher. Here's a summary of the logic in the user control that updates the progress.
this.Dispatcher.Invoke(DispatcherPriority.Input, (Action)(() =>
{
DAProgressIndicator = InfiniteProgress.AddNewInstanceToControl(StatusGrid, new SolidColorBrush(new Color() { A = 170, R = 128, G = 128, B = 128 }), string.Empty);
DAProgressIndicator.Message = MediaCardAdminRes.ActivatingCard;
ActivateInProgress = true;
}));
I thought I'd be smart and add an event to MyUserControl that would be called in the ActivateInProgress property set logic.
public bool ActivateInProgress
{
get
{
return _activateInProgress;
}
set
{
_activateInProgress = value;
if (ActivateInProgressHandler != null)
{
ActivateInProgressHandler(value);
}
}
}
I'm setting the ActivateInProgressHandler within the MyWindow constructor to the following method that sets the view model property that is used for the window's own progress indicator.
private void SetActivation(bool activateInProgress)
{
viewModel.ActivationInProgress = activateInProgress;
}
However, the window's progress indicator never changes. So, I'm convinced that the Dispatcher.Invoke is doing something that I don't understand. If I put a message box inside the SetActivation method, the thread blocks and the window's progress indicator is updated. I understand basic threads but this whole Dispatcher thing is new to me. What am I missing?
UPDATE: It seems to be working now. It turns out the progress was being updated so fast that it never got shown on the screen. But, I still would like to understand why the Dispatcher.Invoke was done (this was existing code that I didn't write). Why aren't the action contents done in line with the rest of the *.xaml.cs code?
Your last paragraph mentions threads twice, which raises the possibility that there are one or more background threads. But since you didn't mention what threads exist in the application, how they are created, how they interact, etc, I'll assume for the moment there is only one thread.
If the UI thread is the only thread, the problem is obvious: Your UI thread is busy running the task in progress, and doesn't take time to render the updated UI. If that's the problem, this will probably fix it:
viewModel.ActivationInProgress = activateInProgress;
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new Action(() => {}));
The BeginInvoke forces all Dispatcher operations above input priority to complete before the current thread continues.
The Dispatcher works on a queue. So it could be that the UI thread is blocking. You add more work in the queue via the Dispatcher but it will never get executed because the UI thread is blocking.
Maybe try this:
DispatcherFrame _frame = new DispatcherFrame();
Dispatcher.PushFrame(_frame);
This will put your work infront of the work already on the queue. So the UI thread will do the work and then block again.