using System.Diagnostics; using System.Drawing; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Media.Imaging; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using UIFramework; using UIFrameworkServices; using adWin = Autodesk.Windows; namespace Sai.Toolkit.Revit.Assist; public static class UIAssist { private static readonly string AddInPath = typeof(UIAssist).Assembly.Location; [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetWindowText(IntPtr hWnd, string lpString); private static BitmapSource ToBitmapSource(Bitmap bitmap) { return bitmap == null ? null : Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } /// /// Combobox是可以分组的 /// 组合框是与事件结合使用的 /// public static ComboBox AddComboBox( this RibbonPanel panel, ComboBoxData comboBoxData, params ComboBoxMemberData[] memberData ) { var comboBx = panel.AddItem(comboBoxData) as ComboBox ?? throw new ArgumentNullException(nameof(comboBoxData)); foreach (var item in memberData) { comboBx.AddItem(item); } comboBx.CurrentChanged += (sender, _) => { if (sender is ComboBox comboBox) { var member = comboBox.Current; TaskDialog.Show("组合框选择", "你的选择是: " + member.ItemText); } }; return comboBx; } /// /// 在Revit现有的标签下(如不存在则创建),创建创建面板 /// /// /// 创建的面板名称 /// 若Tab不存在,则创建对应名称的Tab /// public static RibbonPanel AddPanel(this UIControlledApplication application, string panelName, string tabName) { var ribbonTab = adWin.ComponentManager.Ribbon.Tabs.FirstOrDefault(tab => tab.Id.Equals(tabName)); if (ribbonTab is null) { application.CreateRibbonTab(tabName); return application.CreateRibbonPanel(tabName, panelName); } var ribbonPanel = application.GetRibbonPanels(tabName).Find(panel => panel.Name.Equals(panelName)); return ribbonPanel ?? application.CreateRibbonPanel(tabName, panelName); } /// /// 创建一个下拉式按钮。 /// /// 面板 /// 下拉按钮名称 /// /// public static PulldownButton AddPullDownButton(this RibbonPanel pnl, string name, params PushButtonData[] pbds) { if (pnl is null) { throw new ArgumentNullException(nameof(pnl)); } if (pbds is null) { throw new ArgumentNullException(nameof(pbds)); } var pbd = new PulldownButtonData(name, name); if (pnl.AddItem(pbd) is not PulldownButton result) { throw new InvalidCastException(); } result.ToolTip = pbd.ToolTip; result.LongDescription = pbd.LongDescription; result.LargeImage = pbd.LargeImage; foreach (var pbdl in pbds) { var btn = result.AddPushButton(pbdl); if (btn is null) { continue; } btn.ToolTip = pbdl.ToolTip; btn.LongDescription = pbdl.LongDescription; btn.LargeImage = pbdl.LargeImage; } return result; } /// /// 添加按钮 /// /// 命令类 /// 面板 /// 按钮名 /// 按钮图片 /// /// 控制可用性 public static PushButtonData AddPushButton(this RibbonPanel panel) where T : class, IExternalCommand { var data = new PushButtonData(typeof(T).FullName, "外部命令", typeof(T).Assembly.Location, typeof(T).FullName); panel.AddItem(data); return data; } /// /// 单选框 /// /// 面板 /// 分组名称 /// /// public static RadioButtonGroup AddRadioButtonGroup( this RibbonPanel pnl, string groupName, params ToggleButtonData[] toggleButtonData ) { if (pnl is null) { throw new ArgumentNullException(nameof(pnl)); } if (string.IsNullOrEmpty(groupName)) { throw new ArgumentNullException(nameof(groupName)); } RadioButtonGroupData groupData = new(groupName); var radioButtonGroup = (RadioButtonGroup)pnl.AddItem(groupData); foreach (var data in toggleButtonData) { radioButtonGroup.AddItem(data); } return radioButtonGroup; } /// /// 创建一个分割记忆按钮。 /// /// 面板 /// 名称 /// /// public static SplitButton AddSplitButton(this RibbonPanel pnl, string name, params PushButtonData[] array) { if (pnl is null) { throw new ArgumentNullException(nameof(pnl)); } if (array is null) { throw new ArgumentNullException(nameof(array)); } if (string.IsNullOrEmpty(name) || string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(array)); } var splitButton = new SplitButtonData(name, name); if (pnl.AddItem(splitButton) is not SplitButton result) { throw new InvalidCastException(); } result.ToolTip = splitButton.ToolTip; result.LongDescription = splitButton.LongDescription; result.LargeImage = splitButton.LargeImage; foreach (var buttonData in array) { result.AddPushButton(buttonData); } return result; } public static void AddStackItems(this adWin.RibbonRowPanel rowPanel, params adWin.RibbonButton[] ribbonButtons) { for (var i = 0; i < ribbonButtons.Length; i++) { rowPanel.Items.Add(ribbonButtons[i]); if (i < ribbonButtons.Length - 1) { //行打断=新建行 rowPanel.Items.Add(new adWin.RibbonRowBreak()); } } } public static void AddTextBox(this RibbonPanel panel, TextBoxData txtBoxData) { var txtBox = panel.AddItem(txtBoxData) as TextBox; txtBox!.PromptText = "输入提示"; txtBox.ShowImageAsButton = true; txtBox.EnterPressed += (sender, _) => { if (sender is TextBox textBox) { TaskDialog.Show("文本输入", "输入内容: " + textBox.Value); } }; txtBox.Width = 180; } public static ToggleButton AddToggleButton(this RadioButtonGroup group, string name) where T : IExternalCommand { //var addInPath = typeof(T).Assembly.Location; var toggleData = new ToggleButtonData(name, name, typeof(T).Assembly.Location, typeof(T).FullName); return group.AddItem(toggleData); } /// /// 帮助文档 /// /// "xxx.chm" /// public static ContextualHelp CreateContextualHelp(string helpFileSafeName) { //addin目录 FileInfo fileInfo = new(Assembly.GetExecutingAssembly().Location); var text = Path.Combine(fileInfo.Directory!.FullName, helpFileSafeName); if (new FileInfo(text).Exists) { return new ContextualHelp(ContextualHelpType.Url, text); } ImageAssist.ExtractResource(helpFileSafeName, text); return new ContextualHelp(ContextualHelpType.ChmFile, text); } /// /// 添加按钮 /// /// 命令类 /// 面板 /// 按钮名 /// 按钮图片 /// /// 控制可用性 public static PushButtonData CreatePushButton(string name) where T : class, IExternalCommand { return new PushButtonData(typeof(T).FullName, name, typeof(T).Assembly.Location, typeof(T).FullName); } /// /// 查找内容 /// /// /// /// /// /// [DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr FindWindowEx( IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow ); public static ToggleButton GetToggleButton(this RadioButtonGroup radioButtonGroup) { return radioButtonGroup.Current; } /// /// Revit默认打开文件对话框 /// /// 多个过滤器之间用”|“分隔,例如:"Revit文件(*.rfa;*.rvt;*.rft;*.rte;)|*.rfa;*.rvt;*.rft;*.rte" /// public static string OpenFile(string filter) { string filename = null; FileOpenDialog openFileDialog = new(filter) { DefaultFilterEntry = filter, ShowPreview = true, Title = "打开文件" }; if (openFileDialog.Show() == ItemSelectionDialogResult.Confirmed) { var mp = openFileDialog.GetSelectedModelPath(); filename = ModelPathUtils.ConvertModelPathToUserVisiblePath(mp); } return filename; } public static PushButtonData SetAvailability(this PushButtonData pushButtonData) where T : IExternalCommandAvailability { pushButtonData.AvailabilityClassName = typeof(T).FullName; return pushButtonData; } /// /// 指定决定按钮可用性的类别 /// /// 功能区中将受限制的按钮 /// 类型继承 /// T 类应与插件 "外部命令 "共享同一程序集 public static void SetAvailabilityController(this PushButton button) where T : IExternalCommandAvailability, new() { button.AvailabilityClassName = typeof(T).FullName; } public static PushButtonData SetDescription(this PushButtonData pushButtonData, string longDescription) { pushButtonData.LongDescription = longDescription; return pushButtonData; } /// /// 右下角箭头对话框 /// public static void SetDialogLauncher(string panelName) { var group = adWin.ComponentManager.Ribbon.Tabs .First() .Panels.FirstOrDefault(p => p.AutomationName == panelName); if (group != null) { group.Source.DialogLauncher = group.Source.Items.First() as adWin.RibbonCommandItem; } } /// /// 右下角箭头对话框 /// public static void SetDialogLauncher(this adWin.RibbonPanel panel, adWin.RibbonCommandItem item) { if (panel != null) { panel.Source.DialogLauncher = item; } } /// /// 双击选项 /// public static void SetDoubleClickAction(DoubleClickTarget target, DoubleClickAction clickAction) { var doubleOpt = DoubleClickOptions.GetDoubleClickOptions(); //DoubleClickAction dca = doubleOpt.GetAction(DoubleClickTarget.Family); doubleOpt.SetAction(target, clickAction); } /// /// 提示包含短视频 /// /// /// /// public static void SetExpandedVideo(this PushButtonData pushButtonData, string title, string videoPath) { var internalGetRibbonItemMethod = typeof(RibbonItem).GetMethod( "getRibbonItem", BindingFlags.NonPublic | BindingFlags.Instance ); if (internalGetRibbonItemMethod?.Invoke(pushButtonData, null) is Autodesk.Windows.RibbonButton button) { button.ToolTip = new adWin.RibbonToolTip { Title = title, ExpandedVideo = new Uri(videoPath) }; } } public static PushButtonData SetImage(this PushButtonData pushButtonData, Bitmap image) { pushButtonData.Image = ToBitmapSource(image); return pushButtonData; } public static void SetImage(this RibbonButton button, string uri) { button.Image = new BitmapImage(new Uri(uri, UriKind.RelativeOrAbsolute)); } /// /// 消息中心 /// /// /// public static void SetInfoCenterPaletteMsg(string head, string content) { var paletteMgr = Autodesk.Windows.ComponentManager.InfoCenterPaletteManager; var resultItem = new Autodesk.Internal.InfoCenter.ResultItem { Category = head, // 气泡标题 Title = content // 气泡内容 }; resultItem.ResultClicked += (_, _) => { TaskDialog.Show("气泡内容", "点击了气泡内容!"); }; // 当鼠标点击气泡内容时触发此事件 paletteMgr.ShowBalloon(resultItem); } public static PushButtonData SetLargeImage(this PushButtonData pushButtonData, Bitmap largeImage) { pushButtonData.LargeImage = ToBitmapSource(largeImage); return pushButtonData; } /// /// Adds a 32x32px-96dpi image from the URI source /// /// Button to which the icon will be added /// Relative or Absolute path to the icon /// button.SetLargeImage("/RevitAddIn;component/Resources/Icons/RibbonIcon32.png") public static void SetLargeImage(this RibbonButton button, string uri) { button.LargeImage = new BitmapImage(new Uri(uri, UriKind.RelativeOrAbsolute)); } /// /// 状态栏 /// /// public static void SetRevitStatusBarMsg(string msg) { var mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle; if (mainWindowHandle == IntPtr.Zero) { return; } var statusBar = FindWindowEx(mainWindowHandle, IntPtr.Zero, "msctls_statusbar32", string.Empty); if (statusBar != IntPtr.Zero) { SetWindowText(statusBar, msg); } } /// /// 基于Revit封装的RibbonButton设置其快捷键. /// /// RibbonButton. /// 快捷键字符串. /// public static bool SetShortCut(this Autodesk.Revit.UI.RibbonButton btn, string key) { if (btn == null) return false; var item = btn.GetType() .InvokeMember( "getRibbonItem", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, Type.DefaultBinder, btn, null ); return item != null && (item as Autodesk.Windows.RibbonItem).SetShortCut(key); } /// /// 基于通用库封装的RibbonCommandItem设置其快捷键. /// /// RibbonCommandItem. /// 快捷键字符串. /// public static bool SetShortCut(this Autodesk.Windows.RibbonItem commandItem, string key) { if (commandItem == null || string.IsNullOrEmpty(key)) return false; Autodesk.Windows.ComponentManager.Ribbon.FindItem( commandItem.Id, false, out var parentPanel, out var parentTab, true ); if (parentTab == null || parentPanel == null) return false; var path = string.Format("{0}>{1}", parentTab.Id, parentPanel.Source.Id); var cmdId = ControlHelper.GetCommandId(commandItem); if (string.IsNullOrEmpty(cmdId)) { cmdId = Guid.NewGuid().ToString(); ControlHelper.SetCommandId(commandItem, cmdId); } var shortcutItem = new ShortcutItem(commandItem.Text, cmdId, key, path) { ShortcutType = StType.RevitAPI }; UIFrameworkServices.KeyboardShortcutService.applyShortcutChanges( new Dictionary() { { cmdId, shortcutItem } } ); return true; } public static PushButtonData SetShowText(this PushButtonData pushButtonData, string text) { pushButtonData.Text = text; return pushButtonData; } public static PushButtonData SetToolTip(this PushButtonData pushButtonData, string tooltip) { pushButtonData.ToolTip = tooltip; return pushButtonData; } public static PushButtonData SetToolTipImage(this PushButtonData pushButtonData, Bitmap toolTipImage) { pushButtonData.ToolTipImage = ToBitmapSource(toolTipImage); return pushButtonData; } /// /// 通过判断keyTip来区分原始Tab与插件Tab,然后切换插件Tab的可见性 /// /// public static void SetVisible(string tabName) { var ribbonControl = adWin.ComponentManager.Ribbon; foreach (var tab in ribbonControl.Tabs) { if (!tab.IsContextualTab && !tab.IsMergedContextualTab && tab.KeyTip == null && tabName == tab.Name) { tab.IsVisible = !tab.IsVisible; } } } #region 静态方法 /// /// 新建命令按钮 /// /// /// /// /// /// /// /// public static PushButtonData NewButtonData( string text, Bitmap largeImage = null, Bitmap image = null, string toolTip = null, string longDescription = null ) where T : class, IExternalCommand { return new(typeof(T).FullName, text, AddInPath, typeof(T).FullName) { LargeImage = largeImage?.ToBitmapSource(), Image = image?.ToBitmapSource(), ToolTip = toolTip ?? text, LongDescription = longDescription }; } /// /// 新建可控的命令按钮 /// /// /// /// /// /// /// /// /// public static PushButtonData NewButtonData( string text, Bitmap largeBitmap = null, Bitmap bitmap = null, string toolTip = null, string longDescription = null ) where T : class, IExternalCommand where V : class, IExternalCommandAvailability { return new(typeof(T).FullName, text, AddInPath, typeof(T).FullName) { LargeImage = largeBitmap?.ToBitmapSource(), Image = bitmap?.ToBitmapSource(), ToolTip = toolTip ?? text, LongDescription = longDescription, AvailabilityClassName = typeof(V).FullName }; } #endregion }