async/await 工作不正确,Excel 应用程序级插件中的 Excel 事件
Incorrect async/await working, Excel events in Excel Application Level Add-in
我在加载项应用程序中使用 async /await 时遇到问题,我有自定义任务窗格,用户可以用它下载一些数据,所以有问题:
等待的操作完成后,控制不会返回到先前的执行上下文。我使用 windows 表单创建示例并粘贴相同的等待方法,它工作正常 - 我可以在等待操作后使用表单控件,但不能在加载项中使用,我必须调用自定义任务窗格 调用 方法。简单示例:
private void SearchPaneButton_Click(object sender, EventArgs e)
{
Search();
}
private async void Search()
{
var searchText = SearchTextBox.Text;
SearchPaneButton.Text = "Loading…";
var data = await new DataServiceClient().GetDataAsync(searchText);
this.Invoke((MethodInvoker)(() =>
{
SearchPaneButton.Text = "Search";
ToggleWorkbookEvents();
}));
}
在我的加载项中,我需要处理一些事件 - 工作表已更改,工作表 activated/deactivated,但这些事件消失并且在等待操作后不会被触发,我必须使用一些东西就像每次等待使用后的 "toggling"(remove+add) 事件。简单示例:
private void ToggleWorkbookEvents()
{
var excel = Globals.MyAddIn.Application;
//need to ensure event will not be set twice
excel.SheetActivate -= CheckSheetForData;
excel.SheetActivate += CheckSheetForData;
if (excel.ActiveWorkbook != null)
{
var activeSheet = Globals.MyAddIn.GetActiveWorksheet();
//need to ensure event will not be set twice
activeSheet.Change -= Worksheet_Changed;
activeSheet.Change += Worksheet_Changed;
}
}
所以 vsto 框架可能不支持最新版本的 .NET Framework 的新功能(如异步等待)?
这是 Office 插件的一个长期存在的问题:它们不提供 SynchronizationContext
。
正如我在博客中提到的,您可以通过 ensuring you have a proper SynchronizationContext
解决此问题。这有点 hacky,但它有效:
private async void SearchPaneButton_Click(object sender, EventArgs e)
{
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
await SearchAsync();
}
private async Task SearchAsync()
{
var searchText = SearchTextBox.Text;
SearchPaneButton.Text = "Loading…";
var data = await new DataServiceClient().GetDataAsync(searchText);
SearchPaneButton.Text = "Search";
ToggleWorkbookEvents();
}
至于 "toggling events" 问题,我不知道。
当涉及到您的第二个问题时 - 事件与工作表、工作簿等解除绑定。您必须将对目标对象的引用存储为绑定这些对象的 属性 或 class 字段事件。例如,在您的 ToggleWorkbookEvents
方法中,您将 Application
引用为局部变量,您可以尝试像这样存储它:
private Microsoft.Office.Interop.Excel.Application _application = Globals.MyAddIn.Application;
我认为这是由于目标事件处理程序在垃圾收集期间被删除所致。
我根据 Peter:
的回答写这篇文章
我在加载项应用程序中使用 async /await 时遇到问题,我有自定义任务窗格,用户可以用它下载一些数据,所以有问题:
等待的操作完成后,控制不会返回到先前的执行上下文。我使用 windows 表单创建示例并粘贴相同的等待方法,它工作正常 - 我可以在等待操作后使用表单控件,但不能在加载项中使用,我必须调用自定义任务窗格 调用 方法。简单示例:
private void SearchPaneButton_Click(object sender, EventArgs e) { Search(); } private async void Search() { var searchText = SearchTextBox.Text; SearchPaneButton.Text = "Loading…"; var data = await new DataServiceClient().GetDataAsync(searchText); this.Invoke((MethodInvoker)(() => { SearchPaneButton.Text = "Search"; ToggleWorkbookEvents(); })); }
在我的加载项中,我需要处理一些事件 - 工作表已更改,工作表 activated/deactivated,但这些事件消失并且在等待操作后不会被触发,我必须使用一些东西就像每次等待使用后的 "toggling"(remove+add) 事件。简单示例:
private void ToggleWorkbookEvents() { var excel = Globals.MyAddIn.Application; //need to ensure event will not be set twice excel.SheetActivate -= CheckSheetForData; excel.SheetActivate += CheckSheetForData; if (excel.ActiveWorkbook != null) { var activeSheet = Globals.MyAddIn.GetActiveWorksheet(); //need to ensure event will not be set twice activeSheet.Change -= Worksheet_Changed; activeSheet.Change += Worksheet_Changed; } }
所以 vsto 框架可能不支持最新版本的 .NET Framework 的新功能(如异步等待)?
这是 Office 插件的一个长期存在的问题:它们不提供 SynchronizationContext
。
正如我在博客中提到的,您可以通过 ensuring you have a proper SynchronizationContext
解决此问题。这有点 hacky,但它有效:
private async void SearchPaneButton_Click(object sender, EventArgs e)
{
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
await SearchAsync();
}
private async Task SearchAsync()
{
var searchText = SearchTextBox.Text;
SearchPaneButton.Text = "Loading…";
var data = await new DataServiceClient().GetDataAsync(searchText);
SearchPaneButton.Text = "Search";
ToggleWorkbookEvents();
}
至于 "toggling events" 问题,我不知道。
当涉及到您的第二个问题时 - 事件与工作表、工作簿等解除绑定。您必须将对目标对象的引用存储为绑定这些对象的 属性 或 class 字段事件。例如,在您的 ToggleWorkbookEvents
方法中,您将 Application
引用为局部变量,您可以尝试像这样存储它:
private Microsoft.Office.Interop.Excel.Application _application = Globals.MyAddIn.Application;
我认为这是由于目标事件处理程序在垃圾收集期间被删除所致。
我根据 Peter:
的回答写这篇文章