using System.Diagnostics; using System.Drawing; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Input; using System.Windows.Media.Imaging; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using adWin = Autodesk.Windows; namespace ShrlAlgo.Toolkit.Revit.Assist; public static class UIAssist { private static readonly string AddInPath = typeof(UIAssist).Assembly.Location; /// /// 查找内容 /// /// /// /// /// /// [DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr FindWindowEx( IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow ); [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetWindowText(IntPtr hWnd, string lpString); /// /// 面板上添加项 /// /// /// public static void AddAwItem(this adWin.RibbonPanel panel, adWin.RibbonItem item) { panel.Source.Items.Add(item); } /// /// 添加堆叠按钮组 /// /// /// public static void AddAwStackItems(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()); } } } /// /// 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; } /// /// 创建一个下拉式按钮。 /// /// 面板 /// 下拉按钮名称 /// /// 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 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 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; } /// /// 在Revit现有的标签下(如不存在则创建),创建创建面板 /// /// /// 创建的面板名称 /// 若Tab不存在,则创建对应名称的Tab /// public static RibbonPanel AddUIPanel(this UIControlledApplication application, string tabName, string panelName) { var ribbonTab = adWin.ComponentManager.Ribbon.Tabs.FirstOrDefault(tab => tab.Id.Equals(tabName)); if (ribbonTab == 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 adWin.RibbonItem ConvertToAwRibbonItem(this RibbonButton btn) { return (adWin.RibbonItem)btn.GetType() .InvokeMember( "getRibbonItem", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, Type.DefaultBinder, btn, null ); } /// /// 帮助文档 /// /// "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); } /// /// 查找面板 /// /// /// /// public static adWin.RibbonPanel FindAwPanel(this adWin.RibbonTab rt, string panelName) { return rt.Panels.FirstOrDefault(p => p.Source.Name == panelName); } /// /// 查找Tab /// /// 标签名 /// public static adWin.RibbonTab FindAwTab(string tabName) { return adWin.ComponentManager.Ribbon.Tabs.FirstOrDefault(tab => tab.Id.Equals(tabName)); } public static ToggleButton GetToggleButton(this RadioButtonGroup radioButtonGroup) { return radioButtonGroup.Current; } /// /// 创建按钮 /// /// /// /// /// /// RelayCommand /// 命令的参数 /// /// public static adWin.RibbonButton NewAwButton(string showText, Bitmap largeImage, Bitmap image, ICommand command, object parameter, string toolTip = default) { return new() { Image = image.ToBitmapSource(), LargeImage = largeImage.ToBitmapSource(), Size = adWin.RibbonItemSize.Standard, ShowText = true, Id = $"ID_{showText}", Name = $"Btn_{showText}", ToolTip = toolTip ?? showText, Text = showText, IsCheckable = true, CommandHandler = command, CommandParameter = parameter, //Orientation = System.Windows.Controls.Orientation.Vertical }; } /// /// 创建ToggleButton /// /// /// /// /// public static adWin.RibbonToggleButton NewAwToggleButton(string showText, Bitmap largeImage, string toolTip = null) { return new adWin.RibbonToggleButton() { LargeImage = largeImage?.ToBitmapSource(), Size = adWin.RibbonItemSize.Large, ToolTip = toolTip ?? showText, ShowText = true, Id = $"ID_{showText}", Name = $"Tog_{showText}", Text = showText, IsCheckable = true, Orientation = System.Windows.Controls.Orientation.Vertical, }; } /// /// 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; } /// /// 设置面板右下角箭头对话框 /// public static void SetAwDialogLauncher(this adWin.RibbonPanel panel, adWin.RibbonCommandItem item) { if (panel != null) { panel.Source.DialogLauncher = item; } } public static adWin.RibbonItem SetAwImage(this adWin.RibbonItem ribbonItem, Bitmap image) { ribbonItem.Image = image.ToBitmapSource(); return ribbonItem; } public static void SetAwImage(this adWin.RibbonItem ribbonItem, string uri) { ribbonItem.Image = new BitmapImage(new Uri(uri, UriKind.RelativeOrAbsolute)); } public static adWin.RibbonItem SetAwLargeImage(this adWin.RibbonItem ribbonItem, Bitmap largeImage) { ribbonItem.LargeImage = largeImage.ToBitmapSource(); return ribbonItem; } /// /// 从 URI 源添加 32x32px-96dpi 图像 /// /// 添加图标的按钮 /// 图标的相对或绝对路径 /// button.SetAwLargeImage("/RevitAddIn;component/Resources/Icons/RibbonIcon32.png") public static void SetAwLargeImage(this adWin.RibbonItem ribbonItem, string uri) { ribbonItem.LargeImage = new BitmapImage(new Uri(uri, UriKind.RelativeOrAbsolute)); } public static adWin.RibbonItem SetAwShowText(this adWin.RibbonItem ribbonItem, string text) { ribbonItem.Text = text; return ribbonItem; } public static adWin.RibbonItem SetAwToolTip(this adWin.RibbonItem ribbonItem, string tooltip) { ribbonItem.ToolTip = tooltip; return ribbonItem; } public static PushButtonData SetDescription(this PushButtonData pushButtonData, string longDescription) { pushButtonData.LongDescription = longDescription; return pushButtonData; } /// /// 设置面板右下角箭头对话框 /// public static void SetDialogLauncher(string tabName, string panelName) { var group = adWin.ComponentManager.Ribbon.Tabs .FirstOrDefault(tab => tab.Name == tabName) .Panels.FirstOrDefault(p => p.AutomationName == panelName) ?? throw new ArgumentException("未找到标签或面板"); group.Source.DialogLauncher = group.Source.Items.First() as adWin.RibbonCommandItem; } /// /// 设置面板右下角箭头对话框 /// public static void SetDialogLauncher(adWin.RibbonTab tab, string panelName) { var group = tab.Panels.FirstOrDefault(p => p.AutomationName == panelName) ?? throw new ArgumentException("未找到标签或面板"); group.Source.DialogLauncher = group.Source.Items.First() as adWin.RibbonCommandItem; } /// /// 双击选项 /// 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) { //拿到Autodesk.Windows的RibbonItem(转换) 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) }; } } /// /// 设置16x16的小图片 /// /// /// /// public static PushButtonData SetImage(this PushButtonData pushButtonData, Bitmap image) { pushButtonData.Image = image.ToBitmapSource(); return pushButtonData; } /// /// 使用Uri设置16x16的小图片 /// /// /// public static PushButtonData SetImage(this PushButtonData pushButtonData, string uri) { pushButtonData.Image = new BitmapImage(new Uri(uri, UriKind.RelativeOrAbsolute)); return pushButtonData; } /// /// 消息中心 /// /// /// 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); } /// /// 设置32x32-96dpi的大图片 /// /// /// /// public static PushButtonData SetLargeImage(this PushButtonData pushButtonData, Bitmap largeImage) { pushButtonData.LargeImage = largeImage.ToBitmapSource(); return pushButtonData; } /// /// 从 URI 源添加 32x32px-96dpi 图像 /// /// 添加图标的按钮 /// 图标的相对或绝对路径 /// button.SetAwLargeImage("/RevitAddIn;component/Resources/Icons/RibbonIcon32.png") public static void SetLargeImage(this PushButtonData pushButtonData, string uri) { pushButtonData.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); } } /// /// 设置显示文字 /// /// /// /// 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 = toolTipImage.ToBitmapSource(); return pushButtonData; } /// /// 通过判断keyTip来区分原始Tab与插件Tab,然后切换插件Tab的可见性 /// /// /// public static void SetVisible(string tabName, bool visible) { var ribbonControl = adWin.ComponentManager.Ribbon; var tab = ribbonControl.Tabs .FirstOrDefault( tab => !tab.IsContextualTab && !tab.IsMergedContextualTab && tab.KeyTip == null && tabName == tab.Name); if (tab != null) { tab.IsVisible = visible; } } #region 静态方法 /// /// 新建命令按钮 /// /// /// /// /// /// /// /// public static PushButtonData NewPushButtonData( string text, Bitmap largeImage = null, Bitmap image = null, string toolTip = null, string longDescription = null ) where Command : class, IExternalCommand { return new(typeof(Command).FullName, text, AddInPath, typeof(Command).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 Command : class, IExternalCommand where Availablility : class, IExternalCommandAvailability { return new(typeof(Command).FullName, text, AddInPath, typeof(Command).FullName) { LargeImage = largeBitmap?.ToBitmapSource(), Image = bitmap?.ToBitmapSource(), ToolTip = toolTip ?? text, LongDescription = longDescription, AvailabilityClassName = typeof(Availablility).FullName }; } #endregion }