I'm writing an Excel Addin, from which I want to display WPF Windows. Launching these on the Main Thread ( = Excel's calculation thread) seems to mess up Excel, so I need to launch them in a seperate (STA-)Thread.
What I'm struggling with is how to best work with that Window from my main thread afterwards. Say I want to open the window in its own STA Thread, but later get or set any properties from my main thread (say change its size, position, or just simply displaying its window title for the sake of this example).
Is there any best practice to do this?
What I have currently done is this:
public class UserForm<T>
where T : Window
{
private T instance; // instance of the UserForm
protected ManualResetEventSlim signalInitialized = new ManualResetEventSlim(false); // Triggered when the instance has been created
private static StaTaskScheduler staTaskScheduler = new StaTaskScheduler(1);
protected Dispatcher dispatcher; // Dispatcher running the UserForm
private UserForm(Func<T> constructor)
{
Task.Factory.StartNew(
() =>
{
T win = constructor();
var helper = new WindowInteropHelper(win);
helper.Owner = ExcelDnaUtil.WindowHandle;
win.Show();
win.Closed += (sender1, e1) => win.Dispatcher.InvokeShutdown();
this.instance = win;
signalInitialized.Set(); // to signal that this.instance is now set and available to use
this.dispatcher = Dispatcher.CurrentDispatcher;
Dispatcher.Run();
},
CancellationToken.None,
TaskCreationOptions.None,
staTaskScheduler);
}
public static UserForm<T> Create<T>(Func<T> constructor)
where T : Window
{
return new UserForm<T>(constructor);
}
public static UserForm<T> Create<T>()
where T : Window, new()
{
return new UserForm<T>(() => new T());
}
public void Execute(Action<T> action)
{
WaitHandle.WaitAll(new WaitHandle[] { signalInitialized.WaitHandle });
this.dispatcher.Invoke(() => action(this.instance));
}
}
then I can do something like
var form = UserForm<MyWindowClass>.Create();
form.Execute(win => win.Show());
form.Execute(win => MessageBox.Show("Title of the window: " + win.Title));
This works, but I'm not sure if that's a clean / good way to do this? Are there any potential issues with this, or maybe a better way to achieve this?
Thanks!