I have a COM object with a GUI-component, lets call it "FileViewer". This FileViewer component can load a file by using a method, lets call it "LoadFile", that needs to use the UI thread to work. To use LoadFile, the FileViewer have to already been added and loaded into the GUI.
The problem is that the files that you load with LoadFile can be really big which causes the application UI to freeze for multiple seconds, making for a terrible user experience.
My quesion is: Is there anyway to call this LoadFile async without locking the UI thread even thought it requiring a UI thread to load.
What I've tried:
//My standard approach of doing things async
public async btnPressed (){
await new TaskFactory().StartNew(() => {
myFileViewer.LoadFile("C:\..."); //This still freezes the UI thread
});
}
//Maybe could prevent locking if it's not visible
myFileViewer.Parent = null;
myFileViewer.Visible = false;
myFileViewer.LoadFile("C:\..."); //This still freezes the UI
myFileViewer.Parent = this;
myFileViewer.Visible = true;
//Maybe could create the viewer in a new window "TestProxy" with a new "UI thread", load the file and then insert the viewer back into my first window
Thread thread = new Thread(() => {
new TestProxy().Show();
TestProxy.CreateViewerAndLoadFile("C:\..."); //Does actually not freeze my application
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
//this version actually worked, but the problem is that there seem to be no way to move the viewer back into my original window after loading it into this new one
I'm very sure that the Viewer class in itself does not have any support for doing LoadFile async, so I am more interested in a general solution of the problem. Just dosn't feel like it should be something that's impossible to do.
PS:
The name of the GUI component is "AxInventorViewControl" which I create an instance of and "LoadFile" is infact a row of code axInventorViewControl1.FileName = filePath;
Related
I need to create window with loading gif when my main window is rendering. I have read some articles and make a decision that for this purposes i need to create new thread. I did it like in this article
As a result I have something like that:
LoadingDialog _loadingDlg;
Thread loadingThread;
public void ShowLoading()
{
loadingThread = new Thread(new ThreadStart(loadingThreadWork));
loadingThread.SetApartmentState(ApartmentState.STA);
loadingThread.Start();
}
private void loadingThreadWork()
{
_loadingDlg = new LoadingDialog();
_loadingDlg.Show();
System.Windows.Threading.Dispatcher.Run();
}
public void HideLoading()
{
_loadingDlg.Dispatcher.InvokeShutdown();
}
First time when I call ShowLoading() and then HideLoading() everything works like I want. But when I call ShowLoading() at the second time I get an exception at
_loadingDlg.Show();
with message The calling thread cannot access this object because a different thread owns it.
How can this be? _loadingDlg is created in the previous line, and in the same thread.
In the loadingThreadWork you're creating the control, before the first run it's a null, so in first time you succeed. However, you're creating the dialog in a different thread, which is marked as an owner for the control.
At the next time you're calling the loadingThreadWork, control isn't null, and ant change for it from a different thread (and it is a different thread, because you're creating it again) leads to the exception you've got.
As you're using WPF, you probably should switch from threads to async operations, which are much more readable, supportable and predictable than your current solution.
I have a WPF program where my model need to load a "Out-of-Proc" (.exe) COM component in order to achieve some validations when a user make an action on the UI. I'd like to inform the user that a long action will take place to let him know that the application is busy, not just frozen. But any action on the UI does occur after the COM action is completed.
I think that any COM communication should be done on the main UI thread. It eliminates any solution that would run on another thread than the main (UI) thread.
I tried many options without success:
MSDN Dispatcher.PushFrame (DoEvents)
StackOverflow HCL (PushFrame)
Jason programming Blog (PushFrame)
I can't see how I can achieve a synchronous action from a model where I would need to refresh the UI.
My action has a property "IsLoading" that I subscribe from my view and where I try to update the UI according to its state but it seems that is not possible in WPF ???
Any other suggestions ?
Can I use async/await and do my COM action from another thread running another dispatcher (kind of complex) and will loose required synchronicity (user need results of COM operation to continue its work) ?
Mainly for Blindy...
Some clearer explications (more details about required synchronicity):
When a user click on a TreeView item, I load a grid then need to verify that data entered in the grid is still valid. To do validation, I need to load an application through COM and automate it to load a document, then parse it and verify the data in the grid (in the Model of the Grid in the view). That takes 10 seconds.
If I do that on another thread, then user can do an action to select to add a new row in the grid which still depends on the same COM application loaded with the previous document. I still need to wait for the application to load. It is a synchronous action. My application depends on that COM application with its loaded document to be in valid state for user to take more actions. But I need to give user some feedback on what I’m doing (start COM app and load on document). Doing COM action on another thread just report the problem later but do not solve the fact that user need to wait that the action would complete. I think I need to (force) update my WPF app but can’t find any (twisted) way to do it.
[UPDATE] As the OP has updated the question and specified he's using out-of-proc COM objects, the custom STA thread plumbing described below doesn't make sense. Now, a simple await Task.Run(() => { /* call the out-of-proc COM */}) is enough to keep the UI responsive. Kudos to #acelent for clarifying this point.
Recently I answered a related question: StaTaskScheduler and STA thread message pumping.
The solution was to create and use STA COM objects on a dedicated background STA thread which provides both message pumping and thread affinity for those COM objects.
I'd like to show how ThreadWithAffinityContext can be used in your case, with async/await:
dynamic _comObject = null;
ThreadWithAffinityContext _staThread = null;
// Start the long-running task
Task NewCommandHandlerAsync()
{
// create the ThreadWithAffinityContext if haven't done this yet
if (_staThread == null)
_staThread = new ThreadWithAffinityContext(
staThread: true,
pumpMessages: true);
// create the COM Object if haven't done this yet
if (_comObject == null)
{
await _staThread.Run(() =>
{
// _comObject will live on a dedicated STA thread,
// run by ThreadWithAffinityContext
_comObject = new ComObject();
}, CancellationToken.None);
}
// use the COM object
await _staThread.Run(() =>
{
// run a lengthy process
_comObject.DoWork();
}, CancellationToken.None);
}
// keep track of pending NewCommandHandlerAsync
Task _newCommandHandler = null;
// handle a WPF command
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
try
{
// avoid re-entrancy (i.e., running two NewCommandHandlerAsync in parallel)
if (_newCommandHandler != null)
throw new InvalidOperationException("One NewCommandHandlerAsync at a time!");
try
{
await _newCommandHandler = NewCommandHandlerAsync();
}
finally
{
_newCommandHandler = null;
}
}
catch (Exception ex)
{
// handle all exceptions possibly thrown inside "async void" method
MessageBox.Show(ex.Message);
}
}
The fact that we have offloaded the lengthy process _comObject.DoWork() to a separate thread doesn't automatically solve the other common UI-related problem:
How to handle the UI when the lengthy background operation is pending?
There is a number of options. E.g., you can disable the UI elements which fire NewCommand_Executed event, to avoid re-entrancy, and enable another UI element to allow the user to cancel the pending work (a Stop button). You should also provide some progress feedback, if your COM object supports that.
Alternatively, you can display a modal dialog before staring the long-running task and hide it when the task has completed. As far as the UI usability goes, modality is less desirable, but it's very easy to implement (example).
You can create and use COM objects on any thread, the marshaller will take care of running it on a background thread if your application uses the STA threading model. There's no need to funnel every call through the main UI thread.
As to your last question in comments, since you'll be running this on a background thread, you of course need to synchronize like usual with lock and Invoke the result back on the main thread when done. This is nothing specially related to COM, it's just how Windows threading works.
In short, stop using the UI thread for heavy duty, non-UI related work.
I used this once for WPF so as to force the screen to re-Paint :
I used auto-translation from VB so I hope it's correct
private Action EmptyDelegate = () => { };
[System.Runtime.CompilerServices.Extension()]
public void Refresh(UIElement uiElement)
{
uiElement.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, EmptyDelegate);
}
I´m quite new to WPF and to threading in WPF and have successfully implemented the bing maps control in my program. Problem is, the control takes a long time to load and slows down the program itself when opening it. The loading time has increased from about 2sec to about 20sec, which is not really acceptable.
My idea was, to load the bing maps control in a separate thread and thus not to decrease the performance. I´ve been trying to do that, but my UI keeps getting blocked by the loading process of the maps control.
Here´s an example using a Dispatcher:
private init()
{
Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate()
{
// Background thread: I would like to load the map here
Map map = new Map();
map.CredentialsProvider = providerKey;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
// Back to the UI thread
// How do I assign the loaded Map to this thread?
mapElementOnUI = map;
mapPanel.Children.Add(mapElementOnUI);
}));
}
));
thread.Start();
//Load the rest of the GUI here
}
If I handle the Threads like that I get an InvalidOperationException (no STA-Thread). If I change the code to the following, my UI blocks while loading the maps control:
private init()
{
Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate()
{
// Background thread
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
// Back to the UI thread
mapElementOnUI = new Map();
mapElementOnUI.CredentialsProvider = providerKey;
mapPanel.Children.Add(mapElementOnUI);
}));
}
));
thread.Start();
//Load the rest of the GUI here
}
I´ve also been trying to implement a solution via await and async without success. Is it possible at all to load the maps control in a separate thread? Could somebody help me out on this with a piece of code? Thanks a lot!
Well, in your second example all you're doing is starting up another thread that immediately switches back to the UI thread to create a control, so that's why your UI is blocking.
As for your first example, you can't create a control on one thread and use it on another (that's not the exception you were getting. You need to call SetApartmentState before calling Start). So you can't create the map control on a background thread and then load it into your window created on the main thread. This is prevented by the framework.
What you could do is create a separate window on its own UI thread and load the map control into that window. This would prevent your main application window from blocking, but it would require you to manage a second application window. Also, if you wanted objects in the main application thread and objects in the map control thread to interact with each other, you'd have to do a bunch of extra work handling cross-thread calls on both ends. You can read about this approach here.
As for the map control itself, I'm not familiar with it so I don't know if there's some other way to handle or defer loading without freezing your UI.
Ok, I´ve solved this now. #user1100269 gave a good hint resolving the exception I was experiencing: thread.SetApartmentState.STA. Additionally I had to create a new Map instance in the background thread without it beeing used anywhere - I didn´t really get that, but my problem is solved now. The map is loaded in the background and doesn´t block the UI anymore. Here´s the working code for anyone interested:
private void init()
{
Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate()
{
// Background Thread
Map map = new Map();
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
// Back to the UI Thread
var provider = new ApplicationIdCredentialsProvider(MyCredentials);
mapElementOnUI = new Map();
mapElementOnUI.CredentialsProvider = provider;
mapPanel.Children.Add(mapElementOnUI);
updateMapLocations();
}));
}
));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
We have a project that have a couple years development invested into and it now need a progress window because some process are very long. We deal with 3D CAD, advanced engineering, Simulations Third Party DLL, Web services, WCF services.
Now i am trying to add a generic process window you can start in 1 place and end anywhere else. that's not complicated per say. simple static class that open a window on MyClass.Open(string Message) and a MyClass.Close() to hide it once the heavy duty code completed. This works perfectly except the heavy code block the Thread.
The project is a mix of bunch of projects but this part is 99% WPF and i am fairly new to WPF threading as they don't behave like winforms. In winforms thread i create that open other forms are not block by main app heavy work. In WPF the copy pasted code the form is frozen.
I tried Thread and Background Worker without success. then to make sure it wasn't my window the issue or my static class i tried opening the window manually with a Form.Show(); and a Form.Close(); in one of the heavy work function an the form still showed and close properly but yet still frozen. then i removed the Form.Close(); and it started to move right after the work completed. Now i find very strange that the main thread when working at full capacity freeze other threads. I have an actual Exact copy in winform for that progress window except that instead being a Form with label i have a WPF window with a label in a grid.
I tried doing it the right way which is creating a thread for the heavy function but compiler now give me 2,404 errors. I would have at least a week of work just o try if something works and i'd rather not as changing the whole project like so would take at least a year and half and we mostly have 2-3 weeks to complete this so i am looking or any solution that might help finishing that. I can be very dirty. as long as it works.
thank you very much.
Edit :
#Baldrick requested more detail about thread.
I went very simple for thread.
public static class CWaitingMessage
{
private static frmWaiting window = new frmWaiting();
private static Thread t = null;
private static string Message = "";
public static void Open(string sMessage)
{
Message = sMessage;
t = new Thread(new ThreadStart(RunForm));
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
}
private static void RunForm()
{
try
{
window = new frmWaiting();
window.UpdateText(Message);
window.Show();
System.Windows.Threading.Dispatcher.Run();
}
catch
{
window.Close();
}
}
public static void Close()
{
if (t != null)
{
t.Abort("Completed");
}
}
}
If i use System.Windows.Threading.Dispatcher.Run(); instead while (ShowForm) { } the form isn't frozen but my thread disappear by that i mean it never reach f.Close();... ever.
Edit #2 :
Managed to make the abort of the thread using the dispatcher.Run but now i have a bigger issue. the program never stop. the thread start, then i call the abort with a special catch on thread exception and on abort i call a method in the window to save the amount of time it's been opened and close the form. I checked and the file is created so the abort does work but if Dispatche.Run is calle in a thread that is aboded it seems to live on his own. now i have created a monster that never stop running. i close my app completely an visual studio still see it as running.
I am very close to forget about a solution in WPF and go for a winform form to show that message as i know it works perfectly in winforms without that freeze up annoyance.
Edit #3 : Updated my current class i use. I have checked my old exampled and background worker i was only doing a normal form load then running a background worker to loop. the form was in a the same thread as the main code which is completely normal as Background worker are MTA and therefore block UI thread. for UI i need Threads which are STA.
I cannot call dispatcher.Invoke to test it. i couldn't find the assembly for it. my compiler and intellisense don't like it by default :)
The Thread class is not used so much these days as there are now much simpler ways of creating asynchronous methods. For example, please take a look at the Task Class page on MSDN for examples on how to easily create asynchronous methods in WPF.
If you are using .NET 4.5, you can also use the new async and await keywords for even easier asynchronous methods. Please see the await (C# Reference) page on MSDN for a detailed description of this new functionality with code examples.
Having said that, using a BackgroundWorker may actually be your best bet for implementing ProgressBar update functionality with its easy access to update the UI on the UI thread. Please take a look at the BackgroundWorker Class page on MSDN for a full example.
If you need specific help with something, please edit your question in order to add the relevant code.
Found it. after 3 days of work.
For people wondering how to open and close a thread in WPF without freezing he main thread when the new form have animation and no process work.
Code :
public static class CWaitingMessage
{
private static event Action CloseWindow = delegate { };
public static void Open(string sMessage)
{
Thread t = new Thread(delegate()
{
frmWaiting window = new frmWaiting(sMessage);
CloseWindow += () => window.Dispatcher.BeginInvoke(new ThreadStart(() => window.Close()));
window.Closed += (sender2, e2) => Window.Dispatcher.InvokeShutdown();
window.Show();
System.Windows.Threading.Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
public static void Close()
{
CloseWindow();
}
}
Note that no error handling and multiple window opening is supported. this is simple the skeleton working and ready to use. I know at least 2 handful of people that will be very interested in this piece of code.
If you required process also on that window background worker works perfectly in the window. This is very useful for mouse over over a grid being filled asynchronously and while you mouse over you can still open secondary window with another DataGrid and heavy work without issue.
I am working on a WPF project where I have to import data from a lot of single files.
The actual importing of those files and the data in them is being done in a backgroundworker doWork method.
It works like a charm, does the job and updating a progress bar also works perfectly.
Now though, depending on what I encounter in those files, I occasionally need to get a decision from the User before I can proceed processing the current file.
What is the best way to open a window/dialog, getting the values set in there back into the backgroundworker.doWork method and continue processing?
Is that even possible with a backgroundworker or do I need to keep that processing logic in the main/UI thread and update the progress bar from there somehow?
I hope some of you can give me some hints or point me to other resources since I have not found much useful information for my specific problem.
Background worker works in a different thread. You can not invoke UI directly from a background thread. One way of achiving what you are trying to do is by using a flag and Dispatcher to invoke UI for user input
bool WaitFor_Input = false; //flag to indicate user input status
private void ThreadRun()
{
//THIS IS IN Background worker thread
//do task
WaitFor_Input = true;
//ask for user input
Dispatcher.BeginInvoke(new Action(Show_Dialogue), null);
while (WaitFor_Input); //Background worker waits untill user input is complete
//continue further processing
}
private void Show_Dialogue()
{
//THIS IS IN UI THREAD
//show your dialogue box here, update data variables
//finally set
WaitFor_Input = false;
}
Keeping processing logic in a background thread is actually a good idea.
You can call the ShowDialog of an OpenFileDailog class on a new Thread or BackgroundWorker( this will also create a new thread)
But when you want to pass or update any property or control that is running on the main thread you will need to use the Disptacher like this
Dispatcher.BeginInvoke(new Action(() => { YourMethodThatUpdatesSomething(); }));