C# excel 添加 - 如何在长处理任务上直接从事件处理程序更新 sheet UI

C# excel add in - how to directly update sheet UI from event handler on long processing tasks

大家好。如果问题没有正确提出,请首先道歉,如果我错了,请纠正我。

我正在使用 Visual Studio 2015 开发 excel 插件。项目类型是 Excel 2013 和 2016 VSTO 加载项,我的问题是我想向用户显示一些指示,表明应用程序在单击按钮时正在加载数据,但是 UI 正在加载数据时更新,而不是在此之前...

例如:

private void MyCustomMenuItem_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
        {

            Excel.Worksheet sheet = Globals.ThisAddIn.Application.ActiveWorkbook.ActiveSheet;
            Excel.Shapes shapes = sheet.Shapes;
            shapes.AddTextEffect(Office.MsoPresetTextEffect.msoTextEffect1,
                "Loading please wait ...",
                "Arial",
                16,
                Office.MsoTriState.msoFalse,
                Office.MsoTriState.msoFalse,
                10,
                10
                );

            // assume that on the following line is a long processing calculation
            Thread.Sleep(3000);
            // And the problem is that the Shape will show after 3 seconds
}

我的问题是形状元素将在线程准备好工作后显示,在这种情况下,3 秒后。如果在 Thread.Sleep(3000) 的地方我有一些代码 运行 40 秒它将在 40 秒后显示文本 ...

我知道我做错了什么,没有正确更新 UI 线程 ...

问题:如何在长时间处理任务之前更新UI线程?

注意:我正在使用 Microsoft.Office.Interop.Excel 命名空间与 excel 应用程序进行交互。

我确实尝试显示一个自定义表单,其中只有文本 "Loading please wait" 但是在漫长的过程结束后表单再次显示......我尝试的另一件事是引发一个自定义事件并尝试从事件处理程序中显示一些内容,但仍然没有成功...

我想避免使用 Globals.ThisAddIn.Application.StatusBar = "Loading please wait ..." 方法并做一些对用户更友好的事情。

如果有人能指出我做错了什么,那就太好了。

提前致谢_

你 UI 是 "freezing" 因为 UI 线程正忙于做其他事情。

如果您使用的是 WPF,请阅读 this question

如果您使用的是 Win32 窗体,请阅读 this question

这不是 vstoexcel-interop 问题。你只需要小心线程。如果您希望 UI 在执行长进程时进行更新,那么您将希望该进程位于后台线程上。但是,对于您的情况,请记住以下几条规则(这不是完整列表):

  1. 不要直接从后台线程更新 UI。
  2. 不要从创建它的线程以外的线程修改 Excel 对象。

如果您想要 older/simpler 更新 UI 的方法,请查看 BackgroundWorkerReportProgress 方法。如果您想了解更现代的 Task,请查看上面的链接。

经过一些研究和实现,如果您有任何问题,请post一些代码示例。

你好_朋友,很抱歉回答晚了。

你完全正确,在你发布的 link 的帮助下,我成功地完成了我想做的事情。

我发布这个答案是为了防止其他人遇到这个问题并找到这个讨论。

这就是我所做的:

private void ShowLoadingTextMenuItem_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
{
    // This part is very important because on the following call  
    // to FromCurrentSynchronizationContext() you can get an exception
    if (SynchronizationContext.Current == null)
    {
        SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
    }

    var context = TaskScheduler.FromCurrentSynchronizationContext();
    var task = new Task(() =>
    {
        // Disable excel application interactivity
        // you can avoid this and user will be able to use the UI
        // but there is the possibility to get HRESULT: 0x800AC472 Exception
        // when the long processing ends and you try to update the UI
        // which is probably coming because main UI thread is busy  
        Globals.ThisAddIn.Application.Interactive = false;

        // This is a simple class that is just showing WordArt text
        // in the middle of screen to indicate the loading process
        LoadingOverlayHelper.StartLoading();
    });

    Task continuation = task.ContinueWith(t =>
    {
        Globals.ThisAddIn.Application.StatusBar = "Doing some long processing task ...";

        // At this point you start the long processing task
        Thread.Sleep(4000);

        // And when its over remove the processing text
        LoadingOverlayHelper.StopLoading();

        Globals.ThisAddIn.Application.StatusBar = "Ready";

        Globals.ThisAddIn.Application.Interactive = true;

    }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, context);

    // Execute the task !
    task.Start();
}

我还用我正在玩的代码制作了一个 github 存储库,所以任何人都可以得到它并玩它 - https://github.com/research-and-develop/ExcelAddInPlayground/

谢谢