VSTO开发:异步BackgroundWorker运行时禁用Excel操作
解决VSTO BackgroundWorker运行时用户操作Excel中断任务的问题
嘿,我刚好在开发Excel VSTO加载项时碰到过一模一样的问题!异步任务跑起来后用户乱点Excel确实会干扰后台进程,下面给你两种靠谱的解决方案,结合你的代码来改就行:
方法一:禁用Excel交互功能
Excel对象模型里有个Interactive属性,把它设为false就能直接锁住整个Excel界面,用户没法点击、编辑任何内容,完美阻止误操作。关键要记得在后台任务结束后一定要恢复这个属性,不然用户会彻底没法用Excel,只能重启软件。
修改你的代码:
首先在启动后台任务前禁用交互,然后在RunWorkerCompleted事件里恢复:
private void button1_Click(object sender, EventArgs e) { // 先锁住Excel界面,阻止用户操作 Globals.ThisAddIn.Application.Interactive = false; // 顺便把光标改成等待状态,给用户明确提示 Globals.ThisAddIn.Application.Cursor = Excel.XlMousePointer.xlWait; System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext()); BackgroundWorker bw = new BackgroundWorker(); bw.WorkerReportsProgress = true; bw.ProgressChanged += backgroundWorker3_ProgressChanged; bw.DoWork += backgroundWorker3_DoWork; // 注意你原来的事件名打错了,是RunWorkerCompleted不是WorkerCOmpleted bw.RunWorkerCompleted += backgroundWorker3_RunWorkerCompleted; bw.RunWorkerAsync(); } // 后台任务完成后的处理事件 private void backgroundWorker3_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // 无论任务成功、失败还是被取消,都要恢复Excel的交互能力 try { Globals.ThisAddIn.Application.Interactive = true; Globals.ThisAddIn.Application.Cursor = Excel.XlMousePointer.xlDefault; } catch (Exception ex) { // 哪怕恢复时出错,也要强制还原,不然用户会崩溃 MessageBox.Show($"恢复Excel操作权限时出错:{ex.Message}"); Globals.ThisAddIn.Application.Interactive = true; Globals.ThisAddIn.Application.Cursor = Excel.XlMousePointer.xlDefault; } // 处理后台任务的结果或异常 if (e.Error != null) { MessageBox.Show($"数据处理出错:{e.Error.Message}"); } else if (e.Cancelled) { MessageBox.Show("任务已取消"); } else { MessageBox.Show(e.Result.ToString()); } }
重要注意事项:
- 必须在UI线程设置
Interactive属性,绝对不能在DoWork事件里操作(因为DoWork是后台线程,直接访问Excel对象会触发跨线程异常)。 - 一定要在
RunWorkerCompleted里恢复属性,哪怕任务抛出异常,否则Excel会一直处于锁定状态。
方法二:显示模态等待窗体
如果觉得直接锁住Excel太生硬,可以做一个简单的等待弹窗(模态窗体),用户点击启动任务后弹出这个窗体,模态窗体会自动阻止用户操作Excel,同时还能显示进度条,体验更好。
大致实现步骤:
- 创建一个Windows Form(比如叫
WaitForm),里面加个ProgressBar和提示文字。 - 在启动后台任务前显示这个窗体,然后在
ProgressChanged事件里更新进度条,任务结束后关闭窗体。
// 声明等待窗体变量 private WaitForm _waitForm; private void button1_Click(object sender, EventArgs e) { _waitForm = new WaitForm(); // 显示模态窗体,自动阻止用户操作Excel _waitForm.Show(); // 搭配禁用交互效果更佳,双重保险 Globals.ThisAddIn.Application.Interactive = false; Globals.ThisAddIn.Application.Cursor = Excel.XlMousePointer.xlWait; System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext()); BackgroundWorker bw = new BackgroundWorker(); bw.WorkerReportsProgress = true; bw.ProgressChanged += (s, pe) => { // 更新等待窗体的进度条 _waitForm.progressBar1.Value = pe.ProgressPercentage; // 同时触发你原来的进度更新逻辑 backgroundWorker3_ProgressChanged(s, pe); }; bw.DoWork += backgroundWorker3_DoWork; bw.RunWorkerCompleted += (s, re) => { // 任务结束关闭等待窗体 _waitForm.Close(); // 恢复Excel交互 Globals.ThisAddIn.Application.Interactive = true; Globals.ThisAddIn.Application.Cursor = Excel.XlMousePointer.xlDefault; // 处理任务结果 if (re.Error != null) { MessageBox.Show($"数据处理出错:{re.Error.Message}"); } else if (re.Cancelled) { MessageBox.Show("任务已取消"); } else { MessageBox.Show(re.Result.ToString()); } }; bw.RunWorkerAsync(); }
这两种方法都能有效阻止用户操作Excel导致后台任务中断,你可以根据自己的需求选一种,我个人更推荐第二种,用户体验更好~
内容的提问来源于stack exchange,提问作者LCaraway
相关产品推荐
相关产品推荐

