在实际的Revit 二次开发项目中,经常需要与Revit 模型元素进行交互,那么除了借助于过滤器来获取对应元素之外,直接拾取元素(PickObjects 函数)也是常见的一种方式。然而在多选操作中,如果不是熟手,很容易找不到多选完成的按钮(其位置见图1,确实显得不起眼)。
图1
在Windows中,提供一种Hook机制,中文里常常被译作“钩子”或者“挂钩”,可以把Hook理解为Windows操作系统消息处理机制的一个平台;应用程序可以通过设置Hook对某个进程或窗口进行监视,即:对特定事件“挂钩”;一旦预定义特定事件发生,Windows操作系统即会向钩子hook发送通知消息,这时,应用程序可进行响应。
接下来我们来使用钩子自动实现点击多选操作中“完成”的按钮!
Step 1
首先我们在NuGet 中安装如图的工具包(MouseKeyHook);
Step 2
安装各种钩子;
public static class WindowsHelper { [DllImport("user32.dll", CharSet = CharSet.None, ExactSpelling = false)] public static extern bool EnumChildWindows(IntPtr hwndParent, CallBack lpEnumFunc, IntPtr lParam); public delegate bool CallBack(IntPtr hwnd, int lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpText, int nCount); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll", EntryPoint = "SendMessageA")] public static extern int SendMessage(IntPtr hwnd, uint wMsg, int wParam, int lParam); }
Step 3
注册鼠标和键盘监视事件,作者添加了一个右键的鼠标事件和一个空格的键盘事件;
public void Subscribe() { // Note: for the application hook, use the Hook.AppEvents() instead m_GlobalHook = Hook.GlobalEvents(); m_GlobalHook.KeyUp += GlobalHookKeyUpExt; m_GlobalHook.MouseUpExt += GlobalHookMouseUpExt; } private void GlobalHookMouseUpExt(object sender, MouseEventExtArgs e) { if (e.Button == MouseButtons.Right) { CompleteMultiSelection(); } } private void GlobalHookKeyUpExt(object sender, KeyEventArgs e) { // 32 represent Space if (e.KeyValue == 32) { CompleteMultiSelection(); } }
Step 4
获取Revit 主窗体下的所有句柄,找到“完成”按钮并发送Click消息;
private void CompleteMultiSelection() { var rvtWindow = Autodesk.Windows.ComponentManager.ApplicationWindow; var list = new List<IntPtr>(); var flag = WindowsHelper.EnumChildWindows(rvtWindow, (hwnd, l) => { StringBuilder windowText = new StringBuilder(200); WindowsHelper.GetWindowText(hwnd, windowText, windowText.Capacity); StringBuilder className = new StringBuilder(200); WindowsHelper.GetClassName(hwnd, className, className.Capacity); if ((windowText.ToString().Equals("完成", StringComparison.Ordinal) || windowText.ToString().Equals("Finish", StringComparison.Ordinal)) && className.ToString().Contains("Button")) { list.Add(hwnd); return false; } return true; }, new IntPtr(0)); var complete = list.FirstOrDefault(); WindowsHelper.SendMessage(complete, 245, 0, 0); }
Step 5
注销监视事件。
public void Unsubscribe() { m_GlobalHook.MouseUpExt -= GlobalHookMouseUpExt; m_GlobalHook.KeyUp -= GlobalHookKeyUpExt; //It is recommened to dispose it m_GlobalHook.Dispose(); }
Step 6
功能测试!
问题
运行钩子程序之后,图2的区域的按钮失效,无法关闭当前窗口,也无法实现ViewCube功能!
经多次尝试,需右击Revit模型显示区域,点击“取消”,即可恢复操作!
参考资料:
钩子让PickObjects功力大增—BIMCoder梁老师
Revit二次开发番外篇之改变PickObjects完成按钮的触发