WPF开发Revit插件调用SendKeys.Send("{ESC}")引发UI不更新问题
问题根因
System.Windows.Forms.SendKeys.Send为同步阻塞执行,发送ESC按键到Revit后,Revit抛出的异常会打断当前WPF线程的消息路由,导致Checked/Unchecked事件的绑定更新、UI渲染逻辑未完成就被中止。- 发送ESC前主动激活了Revit窗口,WPF窗口的消息循环优先级被压低,属性变更通知无法及时被渲染线程处理。
- 绑定未显式指定双向模式与实时更新触发器,存在属性同步失败的隐患。
修复方案
1. 修改绑定配置
显式指定CheckBox的绑定为双向模式,属性变更时立即触发同步:
<CheckBox IsChecked="{Binding Path=IsMultiselection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Checked="multiselection_chk_Checked" Unchecked="multiselection_chk_UnChecked"/>
同时确认窗口的DataContext已正确赋值为你实例化的ViewModel对象。
2. 异步执行Revit按键操作
将发送ESC的逻辑放到WPF消息队列的低优先级任务中,等UI更新完成后再执行,避免阻塞UI逻辑。同时替换SendKeys为更稳定的Win32 PostMessage API发送按键消息,不会阻塞当前线程:
// 导入Win32 API [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); private const int WM_KEYDOWN = 0x0100; private const int VK_ESCAPE = 0x1B; private void multiselection_chk_Checked(object sender, RoutedEventArgs e) { MultiselectChanged = true; // 低优先级异步执行,等待WPF完成UI渲染后再操作Revit Dispatcher.BeginInvoke(new Action(() => { ActivateRevitWindow(); // 发送ESC到Revit主窗口 PostMessage(Autodesk.Revit.ApplicationServices.Application.MainWindowHandle, WM_KEYDOWN, (IntPtr)VK_ESCAPE, IntPtr.Zero); }), System.Windows.Threading.DispatcherPriority.Background); } private void multiselection_chk_UnChecked(object sender, RoutedEventArgs e) { MultiselectChanged = true; Dispatcher.BeginInvoke(new Action(() => { ActivateRevitWindow(); PostMessage(Autodesk.Revit.ApplicationServices.Application.MainWindowHandle, WM_KEYDOWN, (IntPtr)VK_ESCAPE, IntPtr.Zero); }), System.Windows.Threading.DispatcherPriority.Background); }
优化建议
- 尽量避免模拟按键操作Revit,可通过
UIDocument.Selection.SetElementIds(Array.Empty<ElementId>())直接清空选择,或者调用UIApplication.PostCommand(RevitCommandId.LookupPostableCommandId(PostableCommand.Cancel))触发原生取消命令,稳定性更高。 - 所有对Revit API的调用都建议通过
ExternalEvent触发,符合Revit的线程安全规范,避免跨线程异常。
内容的提问来源于stack exchange,提问作者adam.k
相关产品推荐
相关产品推荐

