From 15f02c5231b84486933f20c18f1131f6188abfd3 Mon Sep 17 00:00:00 2001 From: GeKtvi <61162497+GeKtvi@users.noreply.github.com> Date: Sat, 24 Jan 2026 22:28:36 +0300 Subject: [PATCH 01/11] Added context menu and popup items --- .../Errors/SolidDnaErrorCode.cs | 5 + .../CommandManager/CommandManager.cs | 20 +- .../Item/CommandManagerItemExtensions.cs | 122 ++++++ .../ContextMenuItems/CommandContextBase.cs | 70 ++++ .../CommandContextCreatedBase.cs | 124 ++++++ .../ContextMenuItems/CommandContextGroup.cs | 46 +++ .../CommandContextGroupCreated.cs | 54 +++ .../ContextMenuItems/CommandContextIcon.cs | 43 ++ .../CommandContextIconCreated.cs | 42 ++ .../ContextMenuItems/CommandContextItem.cs | 38 ++ .../CommandContextItemCreated.cs | 44 +++ .../ContextMenuItems/ICommandCreatable.cs | 21 + .../Item/ContextMenuItems/ICommandCreated.cs | 14 + .../CommandManager/Item/ICommandItem.cs | 24 ++ .../Item/ICommandManagerItem.cs | 24 +- .../SolidDna.CommandItems/CommandItems.cs | 369 ++++++++++++++++++ .../SolidDna.CommandItems/CommandItems.sln | 30 ++ .../SolidDna.CommandItems.csproj | 18 + 18 files changed, 1085 insertions(+), 23 deletions(-) create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/CommandManagerItemExtensions.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextBase.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextCreatedBase.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroup.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroupCreated.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIcon.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItem.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItemCreated.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/ICommandCreatable.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/ICommandCreated.cs create mode 100644 SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ICommandItem.cs create mode 100644 Tutorials/09-CommandItems/SolidDna.CommandItems/CommandItems.cs create mode 100644 Tutorials/09-CommandItems/SolidDna.CommandItems/CommandItems.sln create mode 100644 Tutorials/09-CommandItems/SolidDna.CommandItems/SolidDna.CommandItems.csproj diff --git a/SolidDna/CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs b/SolidDna/CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs index 539b1cc2..ce3f11db 100644 --- a/SolidDna/CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs +++ b/SolidDna/CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs @@ -238,6 +238,11 @@ public enum SolidDnaErrorCode /// SolidWorksCommandItemPositionError = 12010, + /// + /// There was an error while trying to activate a Context Menu Item that was already activated + /// + SolidWorksCommandContextMenuItemReActivateError = 12011, + #endregion #region Export Data (13,000) diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/CommandManager.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/CommandManager.cs index 8efc465f..b08fa177 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/CommandManager.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/CommandManager.cs @@ -23,6 +23,11 @@ public class CommandManager : SolidDnaObject /// private readonly List mCommandFlyouts = []; + /// + /// A list of all created command context menu items + /// + private readonly List mCommandContextItems = []; + /// /// Unique ID for flyouts (just increment every time we add one) /// @@ -81,6 +86,16 @@ public CommandManagerGroup CreateCommandTab(string title, int id, List + /// Creates context menu items from the provided collection of objects + /// + /// The collection of command items to create + public void CreateContextMenuItems(IEnumerable commandItems) + { + foreach (var item in commandItems) + mCommandContextItems.AddRange(item.Create()); + } + /// /// Create a command group from a list of items. Uses a single list of items, separators and flyouts. /// NOTE: If you set to false, you should pick a new ID every time you change your tab. @@ -123,7 +138,7 @@ public CommandManagerGroup CreateCommandGroupAndTabs(string title, int id, List< // Track all flyouts for all add-ins that use SolidDNA mCommandFlyouts.AddRange(commandManagerItems.OfType()); - + // Create the group group.Create(this, title); @@ -326,6 +341,9 @@ public override void Dispose() // Remove all command flyouts mCommandFlyouts?.ForEach(RemoveCommandFlyout); + // Dispose all command context menu items + mCommandContextItems?.ForEach(x => x.Dispose()); + mCommandContextItems.Clear(); base.Dispose(); } } \ No newline at end of file diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/CommandManagerItemExtensions.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/CommandManagerItemExtensions.cs new file mode 100644 index 00000000..710893fe --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/CommandManagerItemExtensions.cs @@ -0,0 +1,122 @@ +using System.Collections.Generic; +using System.Linq; + +namespace CADBooster.SolidDna; + +/// +/// Provides extension methods for converting objects to +/// +public static class CommandManagerItemExtensions +{ + /// + /// Converts a collection of objects to objects + /// + /// The collection of objects to convert + /// A collection of objects + public static IEnumerable AsCommandContextItems(this IEnumerable items) + => items.AsCommandContextItems(SelectionType.Everything); + + /// + /// Converts a collection of objects to objects + /// + /// The collection of objects to convert + /// The selection type for all items + /// A collection of objects + public static IEnumerable AsCommandContextItems(this IEnumerable items, + SelectionType selectType) + => items.Select(x => x.AsCommandContextItem(selectType)); + + /// + /// Converts a single object to an object + /// + /// The object to convert + /// An object + public static CommandContextItem AsCommandContextItem(this CommandManagerItem item) + => item.AsCommandContextItem(SelectionType.Everything); + + /// + /// Converts a single object to the object + /// + /// The object to convert + /// The selection type for the item (defaults to ) + /// A . Cloned new item + public static CommandContextItem AsCommandContextItem(this CommandManagerItem item, SelectionType selectType) + => new CommandContextItem() + { + Name = item.Name, + Hint = item.Hint, + OnClick = item.OnClick, + OnStateCheck = item.OnStateCheck, + SelectionType = selectType, + VisibleForAssemblies = item.VisibleForAssemblies, + VisibleForDrawings = item.VisibleForDrawings, + VisibleForParts = item.VisibleForParts + }; + + /// + /// Converts a single object to an object + /// + /// The object to convert + /// + /// Absolute path to the image files that contain the single icon. + /// Based on a string format, replacing {0} with the size. For example C:\Folder\Icon{0}.png + /// If batch icon files are provided, SolidWorks uses the first icon (no index support). + /// + /// An object + public static CommandContextIcon AsCommandContextIcon(this CommandManagerItem item, string iconPathFormat) + => item.AsCommandContextIcon(iconPathFormat, SelectionType.Everything); + + /// + /// Converts a single object to the object + /// + /// The object to convert + /// + /// Absolute path to the image files that contain the single icon. + /// Based on a string format, replacing {0} with the size. For example C:\Folder\Icon{0}.png + /// If batch icon files are provided, SolidWorks uses the first icon (no index support). + /// + /// The selection type for the item (defaults to ) + /// A . Cloned new item + public static CommandContextIcon AsCommandContextIcon(this CommandManagerItem item, string iconPathFormat, SelectionType selectType) + => new CommandContextIcon() + { + Hint = item.Hint, + IconPathFormat = iconPathFormat, + OnClick = item.OnClick, + OnStateCheck = item.OnStateCheck, + SelectionType = selectType, + VisibleForAssemblies = item.VisibleForAssemblies, + VisibleForDrawings = item.VisibleForDrawings, + VisibleForParts = item.VisibleForParts + }; + + /// + /// Converts a collection of objects to objects + /// + /// The collection of objects to convert + /// + /// Absolute path to the image files that contain the single icon. + /// Based on a string format, replacing {0} with the size. For example C:\Folder\Icon{0}.png + /// If batch icon files are provided, SolidWorks uses the first icon (no index support). + /// + /// A collection of objects + public static IEnumerable AsCommandContextIcons(this IEnumerable items, + string iconPathFormat) + => items.AsCommandContextIcons(iconPathFormat, SelectionType.Everything); + + /// + /// Converts a collection of objects to objects + /// + /// The collection of objects to convert + /// + /// Absolute path to the image files that contain the single icon. + /// Based on a string format, replacing {0} with the size. For example C:\Folder\Icon{0}.png + /// If batch icon files are provided, SolidWorks uses the first icon (no index support). + /// + /// The selection type for all items + /// A collection of objects + public static IEnumerable AsCommandContextIcons(this IEnumerable items, + string iconPathFormat, + SelectionType selectType) + => items.Select(x => x.AsCommandContextIcon(iconPathFormat, selectType)); +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextBase.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextBase.cs new file mode 100644 index 00000000..8dd7f40b --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextBase.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CADBooster.SolidDna; + +/// +/// A command context menu item base class +/// +public abstract class CommandContextBase +{ + private bool _isCreated; + + #region Public Properties + + /// + /// The help text for this item. + /// + public string Hint { get; set; } + + /// + /// True to show this item in the context menu when an assembly is open. + /// + public bool VisibleForAssemblies { get; set; } = true; + + /// + /// True to show this item in the context menu when a drawing is open. + /// + public bool VisibleForDrawings { get; set; } = true; + + /// + /// True to show this item in the context menu when a part is open. + /// + public bool VisibleForParts { get; set; } = true; + + /// + /// The action to call when the item is clicked + /// + public Action OnClick { get; set; } + + /// + /// The action to call when the item state requested + /// + public Action OnStateCheck { get; set; } + + /// + /// The selection type that determines where the context menu will be shown + /// + public SelectionType SelectionType { get; set; } = SelectionType.Everything; + + #endregion + + /// + /// Creates the command context item for the specified document types + /// + /// The path to use for hierarchical naming. If empty, the item's name is used + /// A list of created command context items + /// Thrown if the item has already been created + public virtual IEnumerable Create(string path = "") + { + if (_isCreated) + throw new SolidDnaException( + SolidDnaErrors.CreateError(SolidDnaErrorTypeCode.SolidWorksCommandManager, + SolidDnaErrorCode.SolidWorksCommandContextMenuItemReActivateError)); + + _isCreated = true; + + return Enumerable.Empty(); + } +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextCreatedBase.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextCreatedBase.cs new file mode 100644 index 00000000..90916cec --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextCreatedBase.cs @@ -0,0 +1,124 @@ +using System; + +namespace CADBooster.SolidDna; + +internal abstract class CommandContextCreatedBase : ICommandCreated, ICommandItem +{ + /// + /// Gets the unique callback ID for this command context item + /// + public string CallbackId { get; } = Guid.NewGuid().ToString("N"); + + /// + /// Gets the name of this command context item + /// + public abstract string Name { get; } + + /// + /// Gets the hint text for this command context item + /// + public string Hint { get; } + + /// + /// Gets the selection type that determines where this item is shown + /// + public SelectionType SelectionType { get; } + + /// + /// Gets the document type (Assembly, Part, or Drawing) for which this item is created + /// + public DocumentType DocumentType { get; } + + /// + /// Gets the action to call when this item is clicked + /// + public Action OnClick { get; } + + /// + /// Gets the action to call when the state of this item is checked + /// + public Action OnStateCheck { get; private set; } + + /// + /// Initializes a new instance of the class + /// + /// The command context item to create + /// The full name of the item, including its hierarchical path + /// The document type (Assembly, Part, or Drawing) for which this item is created + public CommandContextCreatedBase(CommandContextBase commandContextBase, DocumentType documentType) + { + Hint = commandContextBase.Hint; + OnClick = commandContextBase.OnClick; + OnStateCheck = commandContextBase.OnStateCheck; + SelectionType = commandContextBase.SelectionType; + DocumentType = documentType; + + // Listen out for callbacks + PlugInIntegration.CallbackFired += PlugInIntegration_CallbackFired; + + // Listen out for EnableMethod + PlugInIntegration.ItemStateCheckFired += PlugInIntegration_EnableMethodFired; + + Logger.LogDebugSource("Context menu item created"); + } + + /// + /// Fired when a SolidWorks callback is fired + /// + /// The name of the callback that was fired + private void PlugInIntegration_CallbackFired(string name) + { + if (CallbackId != name) + return; + + // Call the action + OnClick?.Invoke(); + } + + /// + /// Fired when a SolidWorks UpdateCallbackFunction is fired + /// + /// The arguments for user handling + private void PlugInIntegration_EnableMethodFired(CommandManagerItemStateCheckArgs args) + { + if (CallbackId != args.CallbackId) + return; + + // Call the action + OnStateCheck?.Invoke(args); + } + + /// + /// Disposing + /// + public void Dispose() + { + /// I can't find the way to remove the item + + /// It always returns false, and the item isn't removed. + //var removeMenuPopupItemResult = AddInIntegration.SolidWorks.UnsafeObject.RemoveMenuPopupItem2( + // (int)DocumentType, + // SolidWorksEnvironment.Application.SolidWorksCookie, + // (int)SelectionType, + // FullName, + // $"{nameof(SolidAddIn.Callback)}({CallbackId})", + // $"{nameof(SolidAddIn.ItemStateCheck)}({CallbackId})", + // Hint, + // string.Empty + //); + + /// It always returns true, but the item isn't removed. + //var removeFromPopupMenuResult = AddInIntegration.SolidWorks.UnsafeObject.RemoveFromPopupMenu( + // CommandId, + // (int)DocumentType, + // (int)SelectionType, + // true + //); + + /// Besides, the user can hide it using . + + // Stop listening out for callbacks + PlugInIntegration.CallbackFired -= PlugInIntegration_CallbackFired; + PlugInIntegration.ItemStateCheckFired -= PlugInIntegration_EnableMethodFired; + } +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroup.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroup.cs new file mode 100644 index 00000000..7b7f5c64 --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroup.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; + +namespace CADBooster.SolidDna; + +/// +/// Represents a command context group in SolidWorks +/// +public class CommandContextGroup : ICommandCreatable +{ + private bool _isCreated; + + #region Public Properties + + /// + /// The name of this command group + /// + public string Name { get; set; } + + /// + /// Context menu items in this group + /// + public List Items { get; set; } + + #endregion + + /// + /// Creates the command context group and its items + /// + /// The hierarchical path for the group + /// A list of created command context items + /// Thrown if the group has already been created + public IEnumerable Create(string path) + { + if (_isCreated) + throw new SolidDnaException( + SolidDnaErrors.CreateError(SolidDnaErrorTypeCode.SolidWorksCommandManager, + SolidDnaErrorCode.SolidWorksCommandContextMenuItemReActivateError)); + + _isCreated = true; + + return Enumerable.Repeat(new CommandContextGroupCreated(Name, path, Items), 1); + } + + public override string ToString() => $"ContextGroup with name: {Name}. Count of sub items: {Items.Count}"; +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroupCreated.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroupCreated.cs new file mode 100644 index 00000000..1c4f1aa7 --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroupCreated.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Linq; + +namespace CADBooster.SolidDna; + +/// +/// Represents a created command context group in SolidWorks +/// This class handles the creation and disposal of a group of context menu items +/// +internal class CommandContextGroupCreated : ICommandCreated +{ + /// + /// Gets the unique callback ID for this command context group + /// + public string CallbackId => string.Empty; + + /// + /// Gets the name of this command context group + /// + public string Name { get; } + + /// + /// A list of created command items within this group + /// + private readonly List _createdItems; + + /// + /// Initializes a new instance of the class + /// + /// The name of the group + /// The hierarchical path for the group + /// The list of command items to include in the group + public CommandContextGroupCreated(string name, string path, List items) + { + Name = name; + + // Construct the full name for the group using the provided path + var fullName = string.IsNullOrEmpty(path) ? $"{Name}" : $"{path}@{Name}"; + + // Create all child items and store them in the list + _createdItems = items + .SelectMany(x => x.Create(fullName)) + .ToList(); + } + + /// + /// Disposing + /// + public void Dispose() + { + // Dispose all child items + _createdItems.ForEach(x => x.Dispose()); + } +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIcon.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIcon.cs new file mode 100644 index 00000000..546a54a8 --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIcon.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; + +namespace CADBooster.SolidDna; + +/// +/// Represents a context icon in SolidWorks +/// +public class CommandContextIcon : CommandContextBase, ICommandCreatable +{ + /// + /// Absolute path to the image files that contain the single icon. + /// Based on a string format, replacing {0} with the size. For example C:\Folder\Icon{0}.png + /// If batch icon files are provided, SolidWorks uses the first icon (no index support). + /// + public string IconPathFormat { get; set; } + + /// + /// The text that displays on mouse hover. In other words, it is the tooltip for the icon button + /// + string ICommandCreatable.Name => Hint; + + /// + /// Creates the command context icon for the specified document types + /// + /// Not used for icon + /// A list of created command context icons + /// Thrown if the item has already been created + public sealed override IEnumerable Create(string path = "") + { + _ = base.Create(); + + var created = new List(); + + if (VisibleForAssemblies) + created.Add(new CommandContextIconCreated(this, DocumentType.Assembly)); + if (VisibleForDrawings) + created.Add(new CommandContextIconCreated(this, DocumentType.Drawing)); + if (VisibleForParts) + created.Add(new CommandContextIconCreated(this, DocumentType.Part)); + + return created; + } +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs new file mode 100644 index 00000000..0a25ce93 --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs @@ -0,0 +1,42 @@ +using SolidWorks.Interop.sldworks; +using System.Runtime.InteropServices; + +namespace CADBooster.SolidDna; + +/// +/// Represents a created command context icon in the SolidWorks +/// +internal class CommandContextIconCreated : CommandContextCreatedBase +{ + /// + /// Gets the name of this command context item + /// + public sealed override string Name => Hint; + + /// + /// Initializes a new command context icon in the SolidWorks UI + /// + /// The icon configuration + /// The document type this icon applies to + public CommandContextIconCreated(CommandContextIcon commandContextIcon, + DocumentType documentType) : base(commandContextIcon, documentType) + { + // The list of icons. There should be a one multi sized icon. + var icons = Icons.GetArrayFromDictionary(Icons.GetFormattedPathDictionary(commandContextIcon.IconPathFormat)); + + // Get the SolidWorks frame and add the menu icon + var frame = (IFrame)AddInIntegration.SolidWorks.UnsafeObject.Frame(); + + _ = frame.AddMenuPopupIcon3( + (int)DocumentType, + SelectionType, + Hint, + SolidWorksEnvironment.Application.SolidWorksCookie, + $"{nameof(SolidAddIn.Callback)}({CallbackId})", + $"{nameof(SolidAddIn.ItemStateCheck)}({CallbackId})", + SelectionType.GetCustomFeatureNames(), + icons); + + _ = Marshal.ReleaseComObject(frame); + } +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItem.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItem.cs new file mode 100644 index 00000000..0517a090 --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItem.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; + +namespace CADBooster.SolidDna; + +/// +/// A command context menu item +/// +public class CommandContextItem : CommandContextBase, ICommandCreatable +{ + /// + /// The name of this command + /// + public string Name { get; set; } + + /// + /// Creates the command context item for the specified document types + /// + /// The path to use for hierarchical naming. If empty, the item's name is used + /// A list of created command context items + /// Thrown if the item has already been created + public sealed override IEnumerable Create(string path = "") + { + _ = base.Create(path); + + var fullName = string.IsNullOrEmpty(path) ? $"{Name}" : $"{path}@{Name}"; + + var created = new List(); + + if (VisibleForAssemblies) + created.Add(new CommandContextItemCreated(this, fullName, DocumentType.Assembly)); + if (VisibleForDrawings) + created.Add(new CommandContextItemCreated(this, fullName, DocumentType.Drawing)); + if (VisibleForParts) + created.Add(new CommandContextItemCreated(this, fullName, DocumentType.Part)); + + return created; + } +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItemCreated.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItemCreated.cs new file mode 100644 index 00000000..585e7361 --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItemCreated.cs @@ -0,0 +1,44 @@ +namespace CADBooster.SolidDna; + +/// +/// A command context menu item +/// +internal class CommandContextItemCreated : CommandContextCreatedBase +{ + /// + /// Gets the name of this command context item + /// + public sealed override string Name => _name; + + /// + /// Gets the command ID assigned by SolidWorks for this item + /// + public int CommandId { get; } + + private readonly string _name; + + /// + /// Creates the command context item for the specified document types + /// + /// The path to use for hierarchical naming. If empty, the item's name is used + /// A list of created command context items + /// Thrown if the item has already been created + public CommandContextItemCreated(CommandContextItem commandContextItem, + string fullName, + DocumentType documentType) : base(commandContextItem, documentType) + { + _name = commandContextItem.Name; + + /// We have , but it adds a group, and there's no possibility to add a root item. + CommandId = AddInIntegration.SolidWorks.UnsafeObject.AddMenuPopupItem3( + (int)DocumentType, + SolidWorksEnvironment.Application.SolidWorksCookie, + SelectionType, + fullName, + $"{nameof(SolidAddIn.Callback)}({CallbackId})", + $"{nameof(SolidAddIn.ItemStateCheck)}({CallbackId})", + Hint, + SelectionType.GetCustomFeatureNames() + ); + } +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/ICommandCreatable.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/ICommandCreatable.cs new file mode 100644 index 00000000..d2e623f6 --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/ICommandCreatable.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace CADBooster.SolidDna; + +/// +/// Represents an interface for creating command items in the SolidWorks +/// +public interface ICommandCreatable +{ + /// + /// Name of the command item + /// + string Name { get; } + + /// + /// Creates the command item for the specified path + /// + /// The path to use for hierarchical naming. If empty, the item's name is used + /// A list of created command items + IEnumerable Create(string path = ""); +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/ICommandCreated.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/ICommandCreated.cs new file mode 100644 index 00000000..a80aea28 --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/ICommandCreated.cs @@ -0,0 +1,14 @@ +using System; + +namespace CADBooster.SolidDna; + +/// +/// Represents an interface for a created command item in SolidWorks +/// +public interface ICommandCreated : IDisposable +{ + /// + /// Gets the name of the created command item + /// + string Name { get; } +} diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ICommandItem.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ICommandItem.cs new file mode 100644 index 00000000..a34fb58a --- /dev/null +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ICommandItem.cs @@ -0,0 +1,24 @@ +using System; + +namespace CADBooster.SolidDna; + +/// +/// Represents an interface for a command item +/// +public interface ICommandItem +{ + /// + /// The unique Callback ID + /// + string CallbackId { get; } + + /// + /// The action to call when the item is clicked + /// + Action OnClick { get; } + + /// + /// The action to call when the item's state is requested + /// + Action OnStateCheck { get; } +} \ No newline at end of file diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ICommandManagerItem.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ICommandManagerItem.cs index 0a67d130..2ce617ad 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ICommandManagerItem.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ICommandManagerItem.cs @@ -1,37 +1,17 @@ -using System; +namespace CADBooster.SolidDna; -namespace CADBooster.SolidDna; - -/// -/// A menu item that can be added to a toolbar or Tools menu using the command manager. -/// -public interface ICommandManagerItem +public interface ICommandManagerItem : ICommandItem { /// /// True if the command should be added to the tab /// bool AddToTab { get; set; } - /// - /// The unique Callback ID (set by creator) - /// - string CallbackId { get; } - /// /// The command ID for this flyout item /// int CommandId { get; } - /// - /// The action to call when the item is clicked - /// - Action OnClick { get; set; } - - /// - /// The action to call when the item state is requested - /// - Action OnStateCheck { get; set; } - /// /// The position of the item in the list. Specify 0 to add the item to the beginning of the toolbar or menu, or specify -1 to add it to the end. /// After creating the item, we set this to the actual position (flyouts and separators are not included in the count) and we use the actual position to get the . diff --git a/Tutorials/09-CommandItems/SolidDna.CommandItems/CommandItems.cs b/Tutorials/09-CommandItems/SolidDna.CommandItems/CommandItems.cs new file mode 100644 index 00000000..1220cee7 --- /dev/null +++ b/Tutorials/09-CommandItems/SolidDna.CommandItems/CommandItems.cs @@ -0,0 +1,369 @@ +using CADBooster.SolidDna; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using static CADBooster.SolidDna.SolidWorksEnvironment; + +namespace SolidDna.CommandItems; + +/// +/// Register as a SolidWorks Add-in +/// +[Guid("0BE99D58-34E4-4036-B4CF-EDDD52B1EDAD"), ComVisible(true)] // Replace the GUID with your own. +public class SolidDnaAddinIntegration : SolidAddIn +{ + /// + /// Specific application start-up code + /// + public override void ApplicationStartup() + { + + } + + public override void PreLoadPlugIns() + { + + } + + public override void PreConnectToSolidWorks() + { + + } +} + +/// +/// Register as SolidDna Plug-in +/// +public class MySolidDnaPlugin : SolidPlugIn +{ + #region Public Properties + + /// + /// My Add-in description + /// + public override string AddInDescription => "An example of Command Items, Flyouts and Menu"; + + /// + /// My Add-in title + /// + public override string AddInTitle => "SolidDNA CommandItems"; + + /// + /// Toggle value + /// + private bool mToggle; + + #endregion + + #region Connect To SolidWorks + + public override void ConnectedToSolidWorks() + { + CreateMenus(); + } + + public override void DisconnectedFromSolidWorks() + { + + } + + /// + /// Create a toolbar with a flyout, separators and other items, plus a Tools menu. + /// + private void CreateMenus() + { + var imageFormat = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "icons{0}.png"); + + // Create a flyout (group) with a list of items + var flyout = ParentAddIn.CommandManager.CreateFlyoutGroup2( + title: "CreateFlyoutGroup2 Example", + CreateCommandItems(), + mainIconPathFormat: imageFormat, + iconListsPathFormat: imageFormat, + tooltip: "CreateFlyoutGroup2 tooltip", + hint: "CreateFlyoutGroup2 hint", + CommandManagerItemTabView.IconWithTextBelow, + CommandManagerFlyoutType.FirstItemVisible + ); + + // Create a command group / command tab / toolbar + var commandManagerItems = CreateCommandItemsForToolbar(flyout); + + ParentAddIn.CommandManager.CreateCommandTab( + title: "CreateCommandTab Example", + id: 150_000, + commandManagerItems: commandManagerItems, + mainIconPathFormat: imageFormat, + iconListsPathFormat: imageFormat); + + // Create a command menu / Tools menu + ParentAddIn.CommandManager.CreateCommandMenu( + title: "CreateCommandMenu Example", + id: 150_001, + commandManagerItems: CreateCommandItems()); + + Action onContextMenuItemClick = () => System.Windows.MessageBox.Show("Context menu item clicked"); + + ParentAddIn.CommandManager.CreateContextMenuItems( + [ + new CommandContextIcon + { + Hint = "Icon Hint", + OnClick = () => System.Windows.MessageBox.Show("Context icon clicked"), + OnStateCheck = args => args.Result = CommandManagerItemState.DeselectedEnabled, + // Example only. Use indexed single icon instead. + IconPathFormat = imageFormat, + SelectionType = SelectionType.InContextFeatures + }, + new CommandContextItem + { + Name = "RootItem", + Hint = "RootItem Hint", + OnClick = onContextMenuItemClick, + OnStateCheck = args => args.Result = CommandManagerItemState.SelectedEnabled, + SelectionType = SelectionType.Component + }, + new CommandContextGroup + { + Name = "RootGroup", + Items = + [.. + CreateCommandItems().AsCommandContextItems(SelectionType.Component), + new CommandContextItem + { + Name = "PlaneItem", + Hint = "PlaneItem Hint", + OnClick = onContextMenuItemClick, + SelectionType = SelectionType.DatumPlane + }, + new CommandContextGroup + { + Name = "SubGroup", + Items = + [ + new CommandContextItem + { + Name = "SubSubItem", + Hint = "SubSubItem Hint", + OnClick = onContextMenuItemClick, + SelectionType = SelectionType.Component + }, + new CommandContextGroup + { + Name = "SubSubGroup", + Items = + [ + new CommandContextItem + { + Name = "SubSubSubItem", + Hint = "SubSubSubItem Hint", + OnClick = onContextMenuItemClick, + SelectionType = SelectionType.Component + } + ] + }, + ] + } + ] + } + ]); + } + + /// + /// Create a list of command items for a toolbar, Tools menu and a flyout. Create this list for each menu so its IDs are unique. + /// + public List CreateCommandItems() => + [ + // We cant hide item in ToolBar by document type, but it can be disabled manually + new CommandManagerItem + { + Name = "Item for assembly", + Tooltip = "Item tool tip", + ImageIndex = 0, + Hint = "Item disabled in tool bar by active dock type (Assembly)", + VisibleForDrawings = false, + VisibleForAssemblies = false, + VisibleForParts = false, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab DeselectedDisabled item clicked!"), + OnStateCheck = (args) => + { + if(Application.ActiveModel?.IsAssembly is true) + args.Result = CommandManagerItemState.DeselectedDisabled; + } + }, + new CommandManagerItem + { + Name = "DeselectedDisabled item", + Tooltip = "DeselectedDisabled item Tooltip", + ImageIndex = 0, + Hint = "DeselectedDisabled item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab DeselectedDisabled item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.DeselectedDisabled + }, + new CommandManagerItem + { + Name = "DeselectedEnabled item", + Tooltip = "DeselectedEnabled item Tooltip", + ImageIndex = 1, + Hint = "DeselectedEnabled item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab DeselectedEnabled item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.DeselectedEnabled + }, + new CommandManagerItem + { + Name = "SelectedDisabled item", + Tooltip = "SelectedDisabled item Tooltip", + ImageIndex = 2, + Hint = "SelectedDisabled item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab SelectedDisabled item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.SelectedDisabled + }, + new CommandManagerItem + { + Name = "SelectedEnabled item", + Tooltip = "SelectedEnabled item Tooltip", + ImageIndex = 0, + Hint = "SelectedEnabled item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab SelectedEnabled item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.SelectedEnabled + }, + new CommandManagerItem + { + Name = "Hidden item", + Tooltip = "Hidden item Tooltip", + ImageIndex = 1, + Hint = "Hidden item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab Hidden item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.Hidden + }, + new CommandManagerItem + { + Name = "Toggle item", + Tooltip = "Toggle item Tooltip", + ImageIndex = 2, + Hint = "Toggle item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => mToggle = !mToggle, + OnStateCheck = (args) => + args.Result = mToggle ? CommandManagerItemState.SelectedEnabled : CommandManagerItemState.DeselectedEnabled + } + ]; + + /// + /// Create a list of command items for a toolbar. A toolbar can hold normal items (buttons), flyouts and separators, all of which implement ICommandManagerItem. + /// So the reuse the list of command items from this example, we cast them to ICommandManagerItem first. + /// Add a separator and a flyout + /// + /// + public List CreateCommandItemsForToolbar(CommandManagerFlyout flyout) => + new List + { + // Add the flyout (group), which also contains a list of items + flyout, + + // Add a separator + new CommandManagerSeparator(), + + // Add a list of items + new CommandManagerItem + { + Name = "DeselectedDisabled item", + Tooltip = "DeselectedDisabled item Tooltip", + ImageIndex = 0, + Hint = "DeselectedDisabled item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab DeselectedDisabled item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.DeselectedDisabled + }, + new CommandManagerItem + { + Name = "DeselectedEnabled item", + Tooltip = "DeselectedEnabled item Tooltip", + ImageIndex = 1, + Hint = "DeselectedEnabled item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab DeselectedEnabled item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.DeselectedEnabled + }, + + new CommandManagerSeparator(), + + new CommandManagerItem + { + Name = "SelectedDisabled item", + Tooltip = "SelectedDisabled item Tooltip", + ImageIndex = 2, + Hint = "SelectedDisabled item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab SelectedDisabled item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.SelectedDisabled + }, + new CommandManagerItem + { + Name = "SelectedEnabled item", + Tooltip = "SelectedEnabled item Tooltip", + ImageIndex = 0, + Hint = "SelectedEnabled item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab SelectedEnabled item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.SelectedEnabled + }, + + new CommandManagerSeparator(), + + new CommandManagerItem + { + Name = "Hidden item", + Tooltip = "Hidden item Tooltip", + ImageIndex = 1, + Hint = "Hidden item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => System.Windows.MessageBox.Show("CreateCommandTab Hidden item clicked!"), + OnStateCheck = (args) => args.Result = CommandManagerItemState.Hidden + }, + new CommandManagerItem + { + Name = "Toggle item", + Tooltip = "Toggle item Tooltip", + ImageIndex = 2, + Hint = "Toggle item Hint", + VisibleForDrawings = true, + VisibleForAssemblies = true, + VisibleForParts = true, + OnClick = () => mToggle = !mToggle, + OnStateCheck = (args) => + args.Result = mToggle ? CommandManagerItemState.SelectedEnabled : CommandManagerItemState.DeselectedEnabled + } + }; + + #endregion +} diff --git a/Tutorials/09-CommandItems/SolidDna.CommandItems/CommandItems.sln b/Tutorials/09-CommandItems/SolidDna.CommandItems/CommandItems.sln new file mode 100644 index 00000000..6bf41b94 --- /dev/null +++ b/Tutorials/09-CommandItems/SolidDna.CommandItems/CommandItems.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34309.116 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SolidDna.CommandItems", "SolidDna.CommandItems.csproj", "{73B94EE6-BC16-49B2-824D-4C4E114859AC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CADBooster.SolidDna", "..\..\..\SolidDna\CADBooster.SolidDna\CADBooster.SolidDna.csproj", "{055D1F28-DD27-4824-9740-2D0324905F74}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {73B94EE6-BC16-49B2-824D-4C4E114859AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73B94EE6-BC16-49B2-824D-4C4E114859AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73B94EE6-BC16-49B2-824D-4C4E114859AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73B94EE6-BC16-49B2-824D-4C4E114859AC}.Release|Any CPU.Build.0 = Release|Any CPU + {055D1F28-DD27-4824-9740-2D0324905F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {055D1F28-DD27-4824-9740-2D0324905F74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {055D1F28-DD27-4824-9740-2D0324905F74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {055D1F28-DD27-4824-9740-2D0324905F74}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E4046566-F8A2-46D7-9554-3AA5AEEE2F9C} + EndGlobalSection +EndGlobal diff --git a/Tutorials/09-CommandItems/SolidDna.CommandItems/SolidDna.CommandItems.csproj b/Tutorials/09-CommandItems/SolidDna.CommandItems/SolidDna.CommandItems.csproj new file mode 100644 index 00000000..cfd89fb5 --- /dev/null +++ b/Tutorials/09-CommandItems/SolidDna.CommandItems/SolidDna.CommandItems.csproj @@ -0,0 +1,18 @@ + + + net48-windows + Library + false + latest + true + + + + + + + ..\..\..\SolidDna\CADBooster.SolidDna\bin\Debug\net48\CADBooster.SolidDna.dll + + + + \ No newline at end of file From f4fbd73519b1a012905e290ed516bfe8a891cf18 Mon Sep 17 00:00:00 2001 From: GeKtvi <61162497+GeKtvi@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:11:20 +0300 Subject: [PATCH 02/11] Added detailed comments for context menu --- .../Item/CommandManagerItemState.cs | 12 +-- .../ContextMenuItems/CommandContextBase.cs | 16 ++-- .../CommandContextCreatedBase.cs | 36 +------- .../ContextMenuItems/CommandContextGroup.cs | 6 +- .../CommandContextGroupCreated.cs | 15 +-- .../ContextMenuItems/CommandContextIcon.cs | 13 ++- .../CommandContextIconCreated.cs | 43 ++++++++- .../ContextMenuItems/CommandContextItem.cs | 9 +- .../CommandContextItemCreated.cs | 92 ++++++++++++++++++- 9 files changed, 166 insertions(+), 76 deletions(-) diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/CommandManagerItemState.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/CommandManagerItemState.cs index dca32b4a..4c04f886 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/CommandManagerItemState.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/CommandManagerItemState.cs @@ -1,4 +1,4 @@ -namespace CADBooster.SolidDna; +namespace CADBooster.SolidDna; /// /// States for command manager items, flyout items and flyout groups. @@ -10,13 +10,13 @@ public enum CommandManagerItemState { /// /// Deselect and disable the item. - /// Valid for items, flyout items and flyout groups. + /// Valid for items, flyout items, flyout groups, context icons, and context items. /// DeselectedDisabled = 0, /// /// Deselect and enable the item. This is the default state if no update function is specified. - /// Valid for items, flyout items and flyout groups. + /// Valid for items, flyout items, flyout groups, context icons, and context items. /// DeselectedEnabled = 1, @@ -24,7 +24,7 @@ public enum CommandManagerItemState /// Select and disable the item. /// The default behavior of SolidWorks is to select a (flyout) item if the property manager page for that item is active, like while creating a sketch or drawing a rectangle. /// Also used in the Tools menu to show a selected option with a check mark. - /// Valid for items and flyout items. + /// Valid for items, flyout items, and context items. /// SelectedDisabled = 2, @@ -32,12 +32,12 @@ public enum CommandManagerItemState /// Select and enable the item. /// The default behavior of SolidWorks is to select a (flyout) item if the property manager page for that item is active, like while creating a sketch or drawing a rectangle. /// Also used in the Tools menu to show a selected option with a check mark. - /// Valid for items and flyout items. + /// Valid for items, flyout items, and context items. /// SelectedEnabled = 3, /// - /// Hide the item. Valid for flyout items. + /// Hide the item. Valid for flyout items, context icons, and context items. /// Hidden = 4, } \ No newline at end of file diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextBase.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextBase.cs index 8dd7f40b..79479b92 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextBase.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; @@ -13,11 +13,6 @@ public abstract class CommandContextBase #region Public Properties - /// - /// The help text for this item. - /// - public string Hint { get; set; } - /// /// True to show this item in the context menu when an assembly is open. /// @@ -44,17 +39,18 @@ public abstract class CommandContextBase public Action OnStateCheck { get; set; } /// - /// The selection type that determines where the context menu will be shown + /// The selection type that determines with which selection context the item will be shown /// public SelectionType SelectionType { get; set; } = SelectionType.Everything; #endregion /// - /// Creates the command context item for the specified document types + /// Creates the command context item for the specified document types in derived classes. + /// The base class only implements restriction of multiple creation; it does not create anything. /// - /// The path to use for hierarchical naming. If empty, the item's name is used - /// A list of created command context items + /// The path to use for hierarchical naming. If empty, the item's name is used. Not used in the base class; it's only for the signature. + /// Base class method returns empty enumerable /// Thrown if the item has already been created public virtual IEnumerable Create(string path = "") { diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextCreatedBase.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextCreatedBase.cs index 90916cec..294a39ca 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextCreatedBase.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextCreatedBase.cs @@ -10,15 +10,10 @@ internal abstract class CommandContextCreatedBase : ICommandCreated, ICommandIte public string CallbackId { get; } = Guid.NewGuid().ToString("N"); /// - /// Gets the name of this command context item + /// Gets the name for identification of this command context item /// public abstract string Name { get; } - /// - /// Gets the hint text for this command context item - /// - public string Hint { get; } - /// /// Gets the selection type that determines where this item is shown /// @@ -47,7 +42,6 @@ internal abstract class CommandContextCreatedBase : ICommandCreated, ICommandIte /// The document type (Assembly, Part, or Drawing) for which this item is created public CommandContextCreatedBase(CommandContextBase commandContextBase, DocumentType documentType) { - Hint = commandContextBase.Hint; OnClick = commandContextBase.OnClick; OnStateCheck = commandContextBase.OnStateCheck; SelectionType = commandContextBase.SelectionType; @@ -58,8 +52,6 @@ public CommandContextCreatedBase(CommandContextBase commandContextBase, Document // Listen out for EnableMethod PlugInIntegration.ItemStateCheckFired += PlugInIntegration_EnableMethodFired; - - Logger.LogDebugSource("Context menu item created"); } /// @@ -91,32 +83,8 @@ private void PlugInIntegration_EnableMethodFired(CommandManagerItemStateCheckArg /// /// Disposing /// - public void Dispose() + public virtual void Dispose() { - /// I can't find the way to remove the item - - /// It always returns false, and the item isn't removed. - //var removeMenuPopupItemResult = AddInIntegration.SolidWorks.UnsafeObject.RemoveMenuPopupItem2( - // (int)DocumentType, - // SolidWorksEnvironment.Application.SolidWorksCookie, - // (int)SelectionType, - // FullName, - // $"{nameof(SolidAddIn.Callback)}({CallbackId})", - // $"{nameof(SolidAddIn.ItemStateCheck)}({CallbackId})", - // Hint, - // string.Empty - //); - - /// It always returns true, but the item isn't removed. - //var removeFromPopupMenuResult = AddInIntegration.SolidWorks.UnsafeObject.RemoveFromPopupMenu( - // CommandId, - // (int)DocumentType, - // (int)SelectionType, - // true - //); - - /// Besides, the user can hide it using . - // Stop listening out for callbacks PlugInIntegration.CallbackFired -= PlugInIntegration_CallbackFired; PlugInIntegration.ItemStateCheckFired -= PlugInIntegration_EnableMethodFired; diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroup.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroup.cs index 7b7f5c64..e517f081 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroup.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroup.cs @@ -13,14 +13,14 @@ public class CommandContextGroup : ICommandCreatable #region Public Properties /// - /// The name of this command group + /// The name of this command group that is displayed in the context menu /// public string Name { get; set; } /// /// Context menu items in this group /// - public List Items { get; set; } + public IEnumerable Items { get; set; } #endregion @@ -42,5 +42,5 @@ public IEnumerable Create(string path) return Enumerable.Repeat(new CommandContextGroupCreated(Name, path, Items), 1); } - public override string ToString() => $"ContextGroup with name: {Name}. Count of sub items: {Items.Count}"; + public override string ToString() => $"ContextGroup with name: {Name}. Count of sub items: {Items.Count()}"; } diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroupCreated.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroupCreated.cs index 1c4f1aa7..d67280f3 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroupCreated.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextGroupCreated.cs @@ -10,12 +10,7 @@ namespace CADBooster.SolidDna; internal class CommandContextGroupCreated : ICommandCreated { /// - /// Gets the unique callback ID for this command context group - /// - public string CallbackId => string.Empty; - - /// - /// Gets the name of this command context group + /// The name of this command context group that is displayed in the context menu /// public string Name { get; } @@ -30,7 +25,7 @@ internal class CommandContextGroupCreated : ICommandCreated /// The name of the group /// The hierarchical path for the group /// The list of command items to include in the group - public CommandContextGroupCreated(string name, string path, List items) + public CommandContextGroupCreated(string name, string path, IEnumerable items) { Name = name; @@ -46,9 +41,5 @@ public CommandContextGroupCreated(string name, string path, List /// Disposing /// - public void Dispose() - { - // Dispose all child items - _createdItems.ForEach(x => x.Dispose()); - } + public void Dispose() => _createdItems.ForEach(x => x.Dispose()); } diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIcon.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIcon.cs index 546a54a8..8e584f07 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIcon.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIcon.cs @@ -15,22 +15,29 @@ public class CommandContextIcon : CommandContextBase, ICommandCreatable public string IconPathFormat { get; set; } /// - /// The text that displays on mouse hover. In other words, it is the tooltip for the icon button + /// Text displayed in the SolidWorks status bar and as a tooltip when the user moves the pointer over the icon + /// + public string Hint { get; set; } + + /// + /// The name to identify the command. For icons, Hint is used as the name since it's the only text that the icon has. /// string ICommandCreatable.Name => Hint; /// - /// Creates the command context icon for the specified document types + /// Creates the command context icon /// /// Not used for icon /// A list of created command context icons /// Thrown if the item has already been created public sealed override IEnumerable Create(string path = "") { + // Call base class method to ensure that object isnt created and move object to created state if not _ = base.Create(); - var created = new List(); + var created = new List(3); + // Create commands for each SW document type if (VisibleForAssemblies) created.Add(new CommandContextIconCreated(this, DocumentType.Assembly)); if (VisibleForDrawings) diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs index 0a25ce93..2d716479 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs @@ -1,4 +1,4 @@ -using SolidWorks.Interop.sldworks; +using SolidWorks.Interop.sldworks; using System.Runtime.InteropServices; namespace CADBooster.SolidDna; @@ -9,7 +9,12 @@ namespace CADBooster.SolidDna; internal class CommandContextIconCreated : CommandContextCreatedBase { /// - /// Gets the name of this command context item + /// Text displayed in the SolidWorks status bar and as a tooltip when the user moves the pointer over the icon + /// + public string Hint { get; } + + /// + /// The name for identification of this created context icon. This is the Hint text displayed as a tooltip on mouse hover. /// public sealed override string Name => Hint; @@ -21,22 +26,52 @@ internal class CommandContextIconCreated : CommandContextCreatedBase public CommandContextIconCreated(CommandContextIcon commandContextIcon, DocumentType documentType) : base(commandContextIcon, documentType) { + Hint = commandContextIcon.Hint; + // The list of icons. There should be a one multi sized icon. var icons = Icons.GetArrayFromDictionary(Icons.GetFormattedPathDictionary(commandContextIcon.IconPathFormat)); // Get the SolidWorks frame and add the menu icon var frame = (IFrame)AddInIntegration.SolidWorks.UnsafeObject.Frame(); - _ = frame.AddMenuPopupIcon3( + // AddMenuPopupIcon3 documentation: + // https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.ISldWorks~AddItemToThirdPartyPopupMenu2.html + + // Adds an icon to a context-sensitive menu of a SOLIDWORKS UI. + // Returns: True if the context-sensitive menu icon is added, false if not + var iconAdded = frame.AddMenuPopupIcon3( + // Document type whose context-sensitive menus display the icon (int)DocumentType, + // Selection type whose context-sensitive menus display the icon SelectionType, + // Text displayed in the SOLIDWORKS status bar and as a tooltip when the user moves the pointer over the icon Hint, + // ID of the add-in; value of the Cookie argument passed by ISwAddin::ConnectToSW SolidWorksEnvironment.Application.SolidWorksCookie, + // Function called when user clicks the context-sensitive menu icon. + // See Add-in Callback and Enable Methods to learn how to specify CallbackFunction and CallbackUpdateFunction. + // When the icon is clicked, the function specified in CallbackFunction can perform actions such as displaying a third-party pop-up menu*. + // *Third-party pop-up: https://help.solidworks.com/2025/english/api/sldworksapi/SOLIDWORKS.Interop.sldworks~SOLIDWORKS.Interop.sldworks.ISldWorks~ShowThirdPartyPopupMenu.html $"{nameof(SolidAddIn.Callback)}({CallbackId})", + // Optional function that controls the state of the icon; if specified, then SOLIDWORKS calls this function before displaying the icon. + // If CallbackUpdateFunction returns: + // 0 - (CommandManagerItemState.DeselectedDisabled): Deselects and disables the item. + // 1 - (CommandManagerItemState.DeselectedEnabled): Deselects and enables the item; this is the default state if no update function is specified. + // 4 - (CommandManagerItemState.Hidden): Hides the item. + // Other values (CommandManagerItemState.SelectedDisabled and CommandManagerItemState.SelectedEnabled) are not supported. $"{nameof(SolidAddIn.ItemStateCheck)}({CallbackId})", + // Names of custom feature types. + // CustomNames is a semi-colon separated list of the names of the custom feature types. + // This argument is applicable only if SelectType is a custom feature type (like swSelATTRIBUTES); + // in the case of swSelATTRIBUTES, set this field to the name of the attribute definition SelectionType.GetCustomFeatureNames(), + // Array of strings for the paths for the image files for the context-sensitive menu icon. + // ImageList images can be: 20 x 20 pixels, 32 x 32 pixels, 40 x 40 pixels, 64 x 64 pixels, 96 x 96 pixels, 128 x128 pixels. + // Images should use 256-color palette icons); - _ = Marshal.ReleaseComObject(frame); + Marshal.ReleaseComObject(frame); + + Logger.LogDebugSource($"Context menu icon created Name: {Name} CallbackId: {CallbackId})"); } } diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItem.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItem.cs index 0517a090..fb8959bb 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItem.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItem.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace CADBooster.SolidDna; @@ -8,7 +8,12 @@ namespace CADBooster.SolidDna; public class CommandContextItem : CommandContextBase, ICommandCreatable { /// - /// The name of this command + /// Text displayed in the SolidWorks status bar when the user moves the pointer over the item + /// + public string Hint { get; set; } + + /// + /// The name of this command that is displayed in the context menu /// public string Name { get; set; } diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItemCreated.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItemCreated.cs index 585e7361..1041b676 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItemCreated.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextItemCreated.cs @@ -1,4 +1,4 @@ -namespace CADBooster.SolidDna; +namespace CADBooster.SolidDna; /// /// A command context menu item @@ -6,7 +6,12 @@ internal class CommandContextItemCreated : CommandContextCreatedBase { /// - /// Gets the name of this command context item + /// Text displayed in the SolidWorks status bar when the user moves the pointer over the item + /// + public string Hint { get; } + + /// + /// The name of this command that is displayed in the context menu /// public sealed override string Name => _name; @@ -27,18 +32,101 @@ public CommandContextItemCreated(CommandContextItem commandContextItem, string fullName, DocumentType documentType) : base(commandContextItem, documentType) { + Hint = commandContextItem.Hint; + _name = commandContextItem.Name; /// We have , but it adds a group, and there's no possibility to add a root item. + /// But it can still be implemented as an alternative. Just specific handling for this CommandContextItem type should be added. + + /// Also we have AddMenuPopupItem4 (AddMenuPopupItem3 still not marked obsolete). + /// Documentation says that it is related to features selection and SelectionType is string. + /// AddMenuPopupItem3 looks more understandable so this one is preferred. + /// https://help.solidworks.com/2025/english/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.ISldWorks~AddMenuPopupItem4.html + + + // AddMenuPopupItem3 documentation: + // https://help.solidworks.com/2023/English/api/sldworksapi/SOLIDWORKS.Interop.sldworks~SOLIDWORKS.Interop.sldworks.ISldWorks~AddMenuPopupItem3.html + + // Adds a menu item and zero or more submenus to shortcut menus of entities of the specified type in documents of the specified type. + // This method should be called for each unique combination of DocumentType and SelectType in whose menus you want this menu item to display. + // Returns: SOLIDWORKS runtime command ID or -1 if the method fails CommandId = AddInIntegration.SolidWorks.UnsafeObject.AddMenuPopupItem3( + // Document type as defined by swDocumentTypes_e (int)DocumentType, + // ID of the add-in; value of the Cookie argument passed by SolidWorksEnvironment.Application.SolidWorksCookie, + // Selection type whose context-sensitive menus display the item SelectionType, + // Description displayed on the shortcut menu. + // This parameter should use the at-sign (@) to create submenus. + // + // Actually this does not work on SW 22 and 24; it seems like an API bug: + // To add a separator bar after a menu item, append an at-sign to PopupItemName and enclose PopupItemName in double quotes (""). + // For example: "Edge" adds menu item Edge, "Edge@Color" adds Edge with submenu Color, "Edge@" adds Edge with separator bar after it + // It looks like SW still splits the string in ("") with '@' and uses the first part as menu item name and the second (empty) part as submenu name. fullName, + // Function called when user clicks the context-sensitive menu item. $"{nameof(SolidAddIn.Callback)}({CallbackId})", + // Optional function that controls the state of the menu item. + // If specified, SOLIDWORKS calls this function before displaying the menu. + // Display of the menu item is controlled by the return value of MenuEnableMethod. + // If MenuEnableMethod returns: + // 0 - (CommandManagerItemState.DeselectedDisabled): Deselects and disables the menu item. + // 1 - (CommandManagerItemState.DeselectedEnabled): Deselects and enables the menu item (this is the default menu state if no update function is specified). + // 2 - (CommandManagerItemState.SelectedDisabled): Selects and disables the menu item. + // 3 - (CommandManagerItemState.SelectedEnabled): Selects and enables the menu item. + // 4 - (CommandManagerItemState.Hidden): Hides the menu item. $"{nameof(SolidAddIn.ItemStateCheck)}({CallbackId})", + // Text displayed in the SOLIDWORKS status bar when the user moves the pointer over the menu item. + // If you specify a HintString, it must be preceded by a comma Hint, + // Semi-colon separated list of the names of the custom feature types. + // This argument is applicable only if SelectType is a custom feature type (like swSelATTRIBUTES); + // in the case of swSelATTRIBUTES, set this field to the name of the attribute definition SelectionType.GetCustomFeatureNames() ); + + Logger.LogDebugSource($"Context menu item created Name: {Name} CallbackId: {CallbackId})"); + } + + /// + /// Disposing + /// + public override void Dispose() + { + base.Dispose(); + + /// There is no way to remove the item + + /// It always returns false, and the item isn't removed. + //var removeMenuPopupItemResult = AddInIntegration.SolidWorks.UnsafeObject.RemoveMenuPopupItem2( + // (int)DocumentType, + // SolidWorksEnvironment.Application.SolidWorksCookie, + // (int)SelectionType, + // FullName, + // $"{nameof(SolidAddIn.Callback)}({CallbackId})", + // $"{nameof(SolidAddIn.ItemStateCheck)}({CallbackId})", + // Hint, + // string.Empty + //); + + /// It always returns true, but the item isn't removed. + //var removeFromPopupMenuResult = AddInIntegration.SolidWorks.UnsafeObject.RemoveFromPopupMenu( + // CommandId, + // (int)DocumentType, + // (int)SelectionType, + // true + //); + + /// This one isn't possible since AddMenuPopupItem2 callback is only for C++ + //var frame = (IFrame) AddInIntegration.SolidWorks.UnsafeObject.Frame(); + //frame.AddMenuPopupItem2(..., fullName, ...); + //frame.RemoveMenu(fullName); + + /// Besides, the user can hide it using . + + /// Tested on SW 2024 + /// It can be fixed in future SolidWorks updates, so comments are added for this reason } } From 3078119981f4db8d4922714fc1583e543c1cde83 Mon Sep 17 00:00:00 2001 From: GeKtvi <61162497+GeKtvi@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:13:51 +0300 Subject: [PATCH 03/11] Added exception throw on creation failure --- .../CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs | 12 +++++++++++- .../ContextMenuItems/CommandContextIconCreated.cs | 10 +++++++--- .../ContextMenuItems/CommandContextItemCreated.cs | 5 +++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/SolidDna/CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs b/SolidDna/CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs index ce3f11db..c5ea9ea4 100644 --- a/SolidDna/CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs +++ b/SolidDna/CADBooster.SolidDna/Errors/SolidDnaErrorCode.cs @@ -1,4 +1,4 @@ -namespace CADBooster.SolidDna; +namespace CADBooster.SolidDna; /// /// A list of all known types of error codes in SolidDNA @@ -243,6 +243,16 @@ public enum SolidDnaErrorCode /// SolidWorksCommandContextMenuItemReActivateError = 12011, + /// + /// There was an unknown error while trying to add a context menu icon. + /// + SolidWorksCommandCreateContextIconError = 12012, + + /// + /// There was an unknown error while trying to add a context menu item. + /// + SolidWorksCommandCreateContextItemError = 12013, + #endregion #region Export Data (13,000) diff --git a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs index 2d716479..0bb7ceaf 100644 --- a/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs +++ b/SolidDna/CADBooster.SolidDna/SolidWorks/CommandManager/Item/ContextMenuItems/CommandContextIconCreated.cs @@ -32,14 +32,14 @@ public CommandContextIconCreated(CommandContextIcon commandContextIcon, var icons = Icons.GetArrayFromDictionary(Icons.GetFormattedPathDictionary(commandContextIcon.IconPathFormat)); // Get the SolidWorks frame and add the menu icon - var frame = (IFrame)AddInIntegration.SolidWorks.UnsafeObject.Frame(); + using var frame = new SolidDnaObject