void itemCommand_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
{
var thread = new Thread(() =>
{
if (LoginCheck())
{
ItemWindow itw = new ItemWindow();
//Dispatcher.CurrentDispatcher.Invoke((System.Action)(() =>
//{
itw.Show();
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
//}));
Dispatcher.Run();
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
I keep getting error message of "The calling thread cannot access this object because a different thread owns it." on line "itw.show();" when this function called twice. It works fine for the first call, and after the window is closed and trying to open again, it fails. As I commented out "Invoke" method, it doesn't work with Dispatcher either. Please help me to find the solution.
Thank you.
----------------- Edit
The reason why I'm creating a new thread is because it is an Excel addin. I can't create windows from main thread which is the excel that collides with windows if i create them from main thread.
The thing I don't understand is that why does the new instance(ItemWindow) from new thread collide with old thread.
I created a simple test method in a new application that is called when I click the (only) button on my main form. The method looks like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(() =>
{
Window1 window = new Window1();
window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
window.Show();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
Window1 is a window class I made that has nothing but a single TextBlock on it. I can click that button as many times as I want, and it continues to open new windows with no issues (regardless whether or not I close the previous one first).
I suspect the issue is occurring in code that you are not showing us somewhere. You need to be very careful that nothing on your new thread attempts to access anything UI related from your main thread. Windows running on separate threads cannot communicate with each other unless they go through the dispatcher of the other thread. The exception you are seeing is thrown when any method or property of a DispatcherObject is accessed from a thread other than the one that created the object.
Taking a step back, why is it important that the new window be on its own thread? Unless the new window is going to monopolize the thread, it will probably run fine on the main thread. If you are running some long blocking operation, perhaps that operation alone should be moved to a thread rather than the entire window. I don't know what you are doing exactly, but it is something to think about.
EDIT: Realizing that you may not be running in a typical WPF application (looks like you might be in an Office plugin), I updated my test to launch the windows completely standalone on their own threads. However, I am still able to launch two windows in a row with no issues.
Here is my new test. This method and the test class Window1 are the entirety of my application.
[STAThread]
public static int Main(string[] args)
{
ThreadStart threadFunc = () =>
{
Window1 window = new Window1();
window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
window.Show();
System.Windows.Threading.Dispatcher.Run();
};
Thread thread = new Thread(threadFunc);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
thread = new Thread(threadFunc);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return 0;
}
So, there appears to be nothing inherently wrong with what you are trying to do, nor do I see any obvious issue in your code. I suspect that there is some invalid cross-thread communication happening somewhere in your custom window while it is being shown. (Either that, or you are running into an issue specific to Office plugins.)
You are trying to connect the event handler to the ItemWindow after it is already visible.
You need to switch the order from:
ItemWindow itw = new ItemWindow();
itw.Show();
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
to
ItemWindow itw = new ItemWindow();
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
itw.Show();
A possible cause are dependency properties. Dependency properties are a bit picky when it comes to threading.
Even if you don't define your own DepProps, your window will still have some and there is no way to get rid of them.
DepProps have a significant downside: they are thread bound and cannot be accessed from another thread. Which thread holds all the rights is defined by the thread that initializes the DepProps, in your case the first call to new ItemWindow(). After that first call your thread is set and you need that thread to access your DepProps.
For the first window that is no problem but the second one clearly had a different thread. I don't know exactly how DepProps do that, but you might try capturing and restoring the synchronization context of the first thread. Another option would be to capture the dispatcher of the first thread (not the main thread one)
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 have written a WordAddIn that allows the user to call on some metadata for the current document. Via a custom button in the ribbon, they can call a WPF. The WPF calling is realized as follows:
System.Windows.Application app = null;
and then in the method called by the button:
if (app == null)
{
app = new System.Windows.Application { ShutdownMode = ShutdownMode.OnExplicitShutdown };
app.Run();
}
MainWindow win = new MainWindow(graph);
app.Dispatcher.Invoke((Action)(() => { win.Show(); }));
The first time the button is clicked after Word started, nothing happens and it becomes impossible to edit content in the word document. The second time the button is clicked the WPF object loads and is shown, this works for any button click afterwards too. So it seems the first time the dispatcher is called, it hangs. How do I prevent this?
I'm not sure why you want to dispatch the call anyway since this is the same thread. You only need to marshall calls to UI thread when the "current" thread is not a UI thread, which is not your case - you only have one thread.
Secondly, Application.Run is a blocking method and should not be called in the Add-In context. You can't create a WPF application inside Add-In application. BTW, Application.Run always runs on the current thread, which in your case is the same one Word Add-In runs on. And this how it is supposed to be.
If I understand you correctly, you are creating a WPF application because you don't want to use WinForms technology but WPF. Do it without Application.Run and do not dispatch calls because there is no reason to do so.
I got help with this in the Microsoft WPF forums. Creating and calling app in a new thread does the trick, as app does indeed block the current thread it is called in.
if (app == null)
{
Thread thread = new Thread(new ThreadStart(() =>
{
app = new System.Windows.Application { ShutdownMode = ShutdownMode.OnExplicitShutdown };
autoResetEvent.Set();
app.Run();
}));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
else
{
autoResetEvent.Set();
}
autoResetEvent.WaitOne(); //wait until app has been initialized on the other thread
app.Dispatcher.Invoke((Action)(() => { new MainWindow(graph).Show(); }));
}
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'm creating a WPF MVVM application. I have a long process that I want to run in another thread whilst displaying a busy indicator to the user. The problem I have is as follows:
The IsBusy property of the BusyIndicator control is bound to the IsBusy public property of my view model which implements the INotifyPropertyChanged interface. If I run the code below with the Join then the user interface doesn't show the busy indicator as the main UI thread is waiting for the thread "t" to finish. If I remove the join then the Windows Form which is hosting the WPF closes too early. I know that accessing Windows Forms across threads is a big no no but as all I want to do is close the Form I figure the most simple solution is to move _hostForm.Close() to the end of the "DoLongProcess" method. Of course if I do that I get a cross threading exception. Can you please suggest the best approach to take in this situation?
<extToolkit:BusyIndicator IsBusy="{Binding Path=IsBusy}" >
<!-- Some controls here -->
</extToolkit:BusyIndicator>
private void DoSomethingInteresting() {
// Set the IsBusy property to true which fires the
// notify property changed event
IsBusy = true;
// Do something that takes a long time
Thread t = new Thread(DoLongProcess);
t.Start();
t.Join();
// We're done. Close the Windows Form
IsBusy = false;
_hostForm.Close();
}
The best thing to do in this situation, is before you actually invoke the closing of the form, you notify all your systems you are going to close, which will give you the opportunity to run any process at the end. When you are then finished and want to close the form from the other thread, you will need to call it on the UI thread using:
_hostForm.BeginInvoke(new Action(() => _hostForm.Close()));
It might be better, if you are likely to always close the form from another thread, to actually create a thread-safe version of the close method; i.e.:
public class MyForm : Form
{
// ...
public void SafeClose()
{
// Make sure we're running on the UI thread
if (this.InvokeRequired)
{
BeginInvoke(new Action(SafeClose));
return;
}
// Close the form now that we're running on the UI thread
Close();
}
// ...
}
Using this sort of approach, you can continue to update the form and it's UI while running asynchronous operations, then invoke the shutdown and cleanup when your done.
I suggest you to use the BackgroundWorker class. Follow this sample:
BackgroundWorker wrk = new BackgroundWorker();
wrk.WorkerReportsProgress = true;
wrk.DoWork += (a, b) =>
{
... your complex stuff here
};
wrk.RunWorkerCompleted += (s, e) =>
{
isBusy=false;
_hostForm.Close();
};
wrk.RunWorkerAsync();
The code inside the RunWorker complete is already on the UI thread. This solution is non blocking so you can see the isBusy changed and the UI react properly. The DoWork portion is executed in another thread, but you can use the ReportProgress functionality as well if you want.
Here is my suggestion for you. I would solve that with Tasks from TPL contained in .NET Framework since version 4:
private void DoSomethingInteresting()
{
IsBusy = true;
var task = new Task(() => DoLongProcess());
task.ContinueWith(previousTask =>
{
IsBusy = false;
_hostForm.Close();
}, TaskScheduler.FromCurrentSynchronizationContext());
task.Start();
}
Edit
Explanation: The work is done in one task in background. When this task is done, the second task .ContinueWith... is started automatically and it runs on the UI thread due to TaskScheduler.FromCurrentSynchronizationContext().
This is what i have at the moment, but when i call Dispatcher.BeginInvoke(), my UI freezes up:
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.WorkerReportsProgress = true;
ViewModel.GenerateReport(backgroundWorker, Dispatcher);
ViewModel:
backgroundWorker.DoWork += delegate(object s, DoWorkEventArgs args)
{
try
{
ReportViewModel reportViewModel = new ReportViewModel(SessionContext, Mediator, reportDashboardViewModel.ReportRequest, false);
dispatcher.BeginInvoke((Action)(() =>
{
ReportPreviewView reportPreviewView = new ReportPreviewView(reportViewModel);
reportPreviewView.ReportName = reportDashboardViewModel.ReportRequest.Report.ReportName;
ReportView = reportPreviewView;
}));
}
catch (Exception exception)
{
backgroundWorker.ReportProgress(0, "There Was an Error generating the report");
backgroundWorker.CancelAsync();
throw;
}
};
backgroundWorker.RunWorkerAsync();
The first line in dispatcher.BeginInvoke causes my UI to freeze up.
ReportPreviewView reportPreviewView = new ReportPreviewView(reportViewModel);
Note: ReportPreviewView creates the relevant view for the report request. May it be Devexpress, C1 or pivot reports.
As soon as i remove the dispatcher.BeginInvoke, i get this error:
The calling thread must be STA, because many UI components require this.
So my question is, what do i need to do to get around this?
The whole reason for using a BackgroundWorker was so that my UI stays responsive at all times.
I am new to multithreading,so maybe i got the structure all wrong...
Disptacher.BeginInvoke launches the code you encapsulated withing an Action ansynchronoulsy, but the Do_Work handler is also updated async by the BackgroundWorker, so you should't use it.
The main problem is that you are trying to access an UI instance from another thread, and windows disallow that: only the main thread can access an UI element instance.
The correct way to do it is using Control.Invoke if you are using windows Forms.
A deep article about the winforms threading model
Moral of the story, all UI elements needs to be created on the UI thread!
That being said, if the creation of the UI element takes a long time, the UI will freeze up. Take all the heavy lifting and put it in a Task or BackgroundWorker.
Then you will have a more responsive UI.