3

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!

Bogey
  • 4,926
  • 4
  • 32
  • 57
  • 2
    Best practice for creating windows in there own thread is to not doing that. There are a lot of questions on SO about this question for example [this](http://stackoverflow.com/questions/21882609/is-there-any-way-to-use-an-object-across-two-sta-threads-in-wpf/21882839#21882839) one where i had a long discussion about control handling in threads (window is also a Control).The Conclusion was to avoid this where its Possible because the syncing (yes you did it right with Dispatcher.Invoke) will cause a lot of trouble when not using it properly. Im no expert for excel but there must be an other way – Venson Jan 05 '16 at 09:59
  • 1
    I agree with @JPVenson and I can add that there is no big troubles with wpf windows in Excel addins if they are started from main Excel thread. – Alex Butenko Jan 05 '16 at 10:55
  • @Bogey you may threading the wrong part of your code. Could you post whats inside your actual Form? – Venson Jan 05 '16 at 11:04
  • @Alex Butenko: That's odd. I run into several issues, such as all keyboard input going to Excel and not to the WPF window. See e.g. discussion at https://exceldna.codeplex.com/discussions/266302 , where even the author of ExcelDna advised against doing this on Excel's own threads (first answer in thread). Several more examples from people with similar issues can be found via google, including addins for MS Word etc, recommended solutions were always tied to dedicated new threads/Dispatchers. I'm surprised you're not facing these issues in Excel!? – Bogey Jan 05 '16 at 12:07
  • @JPVenson: Thanks. Actually not much code needed to face these issues for me. E.g. a plain WPF window with nothing but a textbox in it; when I show this in Excel's standard thread (simply via new MyWindow();), impossible to type anything into the textbox, all keyboard input goes straight to Excel instead of the Window, calling windows into focus etc. again didn't help either. Suppose something in Office's calculation threads is interfering with the WPF Dispatcher/Message queue (?) or so. Issues don't appear when launching window in a new thread. – Bogey Jan 05 '16 at 12:12
  • @Bogey As i said i am not familiar with Excel Addins, but have you also tried to create a new winForms window instead of an WPF window? I read a lot about trouble with WPF windows but also a lot about WinForms windows that are working fine, so maybe its worth a try – Venson Jan 05 '16 at 12:21
  • @JPVenson Actually, my described problems seem to disappear when I run Dispatcher.Run(); after creating the window, and shut the dispatcher down again when the window is closed, all on Excel's own thread. However that completely blocks out Excel's calculation thread, so the application is in lockdown while the window is open - could imagine this can cause further problems. And of course impossible to open modeless windows that way (i.e. if Excel shall remain usable while window is open). I haven't tried WinForms, would like to stick to WPF - assume a separate thread will be my best/only choice – Bogey Jan 05 '16 at 12:30
  • 1
    @Bogey well that is Absolutly clear^^. Now i got your point. Dispatcher.Run() Will cause the Calling thread to Block until all Windows are closed or Dispatcher.Shutdown is called. That is behavior by Design. When excel does not provide you a Dispatcher for WPF you may have to create your own and YES of course inside your own thread. And YES an own Thread is not the best choice but your only one. But on the other side (as a Big friend of WPF) is it that worth? Just keep in mind that you are about to mix a lot of code for a single Window? My advice would be to use WinForms in that case – Venson Jan 05 '16 at 12:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/99815/discussion-between-jpvenson-and-bogey). – Venson Jan 05 '16 at 12:39

0 Answers0