VSTO: обращение к основному потоку Excel

У меня есть кнопка на листе Excel, которая запускает новый поток для обработки. Если я хочу внести какие-либо изменения в Excel (например, записать данные в ячейку с помощью Worksheet.Range("A1").Value = "info";), я думаю, что я должен использовать основной поток пользовательского интерфейса.

Как это можно сделать?

Обычно в Winforms я бы назвал Invoke для элемента управления, но объекты Excel.Application или Worksheet или Range не имеют метода Invoke.

Ответы

Ответ 1

Эта работа не нужна в потоке пользовательского интерфейса .net будет маршировать вызов для вас, но если вы совершаете повторные вызовы из фонового потока, вы можете столкнуться с проблемами производительности.

Но чтобы ответить на ваш вопрос конкретно, если у вас есть .net 3.5, в вашем событии загрузки надстройки добавьте это:

_dispatcher = Dispatcher.CurrentDispatcher;

И затем добавьте:

public Dispatcher Dispatcher { get {return _dispatcher;} }

Затем вы можете отправить в поток пользовательского интерфейса, перейдя

Globals.ThisAdd.Dispatcher.Invoke(()=>{/*stuff*/});

Если у вас нет .net 3.5, тогда есть несколько других методов синхронизации потоков, например, использование SynchronizationContext.Current вместо диспетчера.

Ответ 2

Это мое решение для VSTO AddIn с использованием WindowsForms. Вам не нужно использовать System.Windows.Forms.Control, чтобы использовать его:


Инициализация в классе ThisAddIn:

Добавьте эту строку в функцию "ThisAddIn_Startup":

this.TheWindowsFormsSynchronizationContext = WindowsFormsSynchronizationContext.Current 
                                           ?? new WindowsFormsSynchronizationContext();

Добавьте это новое свойство:

public SynchronizationContext TheWindowsFormsSynchronizationContext { get; private set; }

Тогда использование в рабочем потоке:

        Globals.ThisAddIn.TheWindowsFormsSynchronizationContext.Send(d =>
        {
            MyMethodToInvoke();
        }, null);   

Второе решение (не проверено): Возможно, вы также можете использовать:

        var invokerControl = new Control();
        invokerControl.CreateControl(); //Forces the control handle to be created
        invokerControl.Invoke(new MethodInvoker(MyMethodToInvoke));

Надеюсь, что это поможет, Йорг

Ответ 3

Вы пытались запустить BackgroundWorker с вашей кнопки? Это упрощает работу, поскольку события ProgressChanged и RunWorkerCompleted будут запускаться в основном потоке.

Я не пробовал это в среде Excel/VSTO, но я не понимаю, почему это не сработает.