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, но я не понимаю, почему это не сработает.