Cant embed button Icon on Ribbon | C#

Cant embed button Icon on Ribbon | C#

jzarczynski
Advocate Advocate
1,187 Views
5 Replies
Message 1 of 6

Cant embed button Icon on Ribbon | C#

jzarczynski
Advocate
Advocate

Hello,

 

I am struggeling with embedding icon inside a button added to Ribbon. The code works as inteded until I try to set the button icon. Here's what did to achieve my Goal:

 

1. Created working code, which creates a button in Asseble Tab, in "RF AUTO" Panel.

 

Now I want the button to include the icon inside of it:

 

 

 

1. I've created "Resources folder", then put icons ("icon32.ico" and "icon16.ico" files in 16x16 and 32x32 px resolution) and set the "built action property" as "Embedded Resource" in their properties in Visual Studio.

2. Used the code  from Mode the machine: https://modthemachine.typepad.com/my_weblog/2012/02/bitmaps-without-vb6-icontoipicture.html to create IPictureDisp needed for button images.

3.  Then created IPictureDisp objects using GetIconfromResource method and passed them to "AddButtonDefinition" method:

 

 

 

//Indicating embeded icons
string icon16ResourceName = "InventorAddIn4.Resources.tank16";
string icon32ResourceName = "InventorAddIn4.Resources.tank32";

//Using PictureDisp converter
stdole.IPictureDisp smallic = PictureDispConverter.ToIPictureDisp(GetIconFromResource(icon16ResourceName));
stdole.IPictureDisp bigicon = PictureDispConverter.ToIPictureDisp(GetIconFromResource(icon32ResourceName));

// Create the button definition and include small and big icons
assembleButton = inventorApp.CommandManager.ControlDefinitions.AddButtonDefinition(
"Assemble",
"InventorAddIn.SimpleAssembleAddIn.AssembleButton",
CommandTypesEnum.kFileOperationsCmdType,
"814846a1-b3bd-4636-9196-12c47fb59596", "Assemble components", "Assemble components in assembly",
smallic,
bigicon
);

 

 

 

So the complete Code looks like:

 

 

 

using System;
using System.Runtime.InteropServices;
using Inventor;
using Microsoft.Win32;
using System.IO;
using stdole;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;

namespace InventorAddIn5
{
    /// <summary>
    /// This is the primary AddIn Server class that implements the ApplicationAddInServer interface
    /// that all Inventor AddIns are required to implement. The communication between Inventor and
    /// the AddIn is via the methods on this interface.
    /// </summary>
    [GuidAttribute("814846a1-b3bd-4636-9196-12c47fb59596")]
    public class StandardAddInServer : Inventor.ApplicationAddInServer
    {

        // Inventor application object.
        private Inventor.Application inventorApp;
        private ButtonDefinition assembleButton;
        private UserInterfaceEvents uiEvents;

        public StandardAddInServer()
        {
        }

        #region ApplicationAddInServer Members

        public void Activate(Inventor.ApplicationAddInSite addInSiteObject, bool firstTime)
        {
            inventorApp = addInSiteObject.Application;
            uiEvents = inventorApp.UserInterfaceManager.UserInterfaceEvents;

            //Indicating embeded icons
            string icon16ResourceName = "InventorAddIn4.Resources.tank16";
            string icon32ResourceName = "InventorAddIn4.Resources.tank32";

            //Using PictureDisp converter 
            stdole.IPictureDisp smallic = PictureDispConverter.ToIPictureDisp(GetIconFromResource(icon16ResourceName));
            stdole.IPictureDisp bigicon = PictureDispConverter.ToIPictureDisp(GetIconFromResource(icon32ResourceName));

            // Create the button definition and include small and big icons
            assembleButton = inventorApp.CommandManager.ControlDefinitions.AddButtonDefinition(
            "Assemble",
            "InventorAddIn.SimpleAssembleAddIn.AssembleButton",
            CommandTypesEnum.kFileOperationsCmdType,
            "814846a1-b3bd-4636-9196-12c47fb59596", "Assemble components", "Assemble components in assembly",
            smallic,
            bigicon
            );

            //assembleButton.DisplayName = "Assemble components";
            //assembleButton.Description = "Assemble components in assembly";
            //assembleButton.TooltipText = "Assemble components";

            // Attach the button click event
            //assembleButton.LargeIcon = "xx";
            assembleButton.OnExecute += AssembleButton_OnExecute;

            // Add the button to the ribbon
            if (firstTime)
            {
                RibbonTab assembleTab = inventorApp.UserInterfaceManager.Ribbons["Assembly"].RibbonTabs["id_TabAssemble"];
                RibbonPanel panel = assembleTab.RibbonPanels.Add("RF AUTO", "InventorAddIn.SimpleAssembleAddIn.AssemblePanel", "814846a1-b3bd-4636-9196-12c47fb59596");
                panel.CommandControls.AddButton(assembleButton, true, true);
            }
        }

        public Icon GetIconFromResource(string resourceName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            using (Stream stream = assembly.GetManifestResourceStream(resourceName))
            {
                return new Icon(stream);
            }
        }

        public void Deactivate()
        {
            // This method is called by Inventor when the AddIn is unloaded.
            // The AddIn will be unloaded either manually by the user or
            // when the Inventor session is terminated

            // TODO: Add ApplicationAddInServer.Deactivate implementation

            // Release objects.
            assembleButton.Delete();
            System.Runtime.InteropServices.Marshal.ReleaseComObject(uiEvents);
            uiEvents = null;
            System.Runtime.InteropServices.Marshal.ReleaseComObject(inventorApp);
            inventorApp = null;

            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        private void AssembleButton_OnExecute(NameValueMap context)
        {
            MessageBox.Show("Assemble button clicked! 08_18");
            // Implement your assembling logic here
        }

        public void ExecuteCommand(int commandID)
        {
            // Note:this method is now obsolete, you should use the 
            // ControlDefinition functionality for implementing commands.
        }

        public object Automation
        {
            // This property is provided to allow the AddIn to expose an API 
            // of its own to other programs. Typically, this  would be done by
            // implementing the AddIn's API interface in a class and returning 
            // that class object through this property.

            get
            {
                // TODO: Add ApplicationAddInServer.Automation getter implementation
                return null;
            }
        }

        #endregion

    }

    public sealed class PictureDispConverter

    {

        [DllImport("OleAut32.dll",

            EntryPoint = "OleCreatePictureIndirect",

            ExactSpelling = true,

            PreserveSig = false)]

        private static extern stdole.IPictureDisp

            OleCreatePictureIndirect(

                [MarshalAs(UnmanagedType.AsAny)] object picdesc,

                ref Guid iid,

                [MarshalAs(UnmanagedType.Bool)] bool fOwn);



        static Guid iPictureDispGuid = typeof(stdole.IPictureDisp).GUID;



        private static class PICTDESC

        {

            //Picture Types

            public const short PICTYPE_UNINITIALIZED = -1;

            public const short PICTYPE_NONE = 0;

            public const short PICTYPE_BITMAP = 1;

            public const short PICTYPE_METAFILE = 2;

            public const short PICTYPE_ICON = 3;

            public const short PICTYPE_ENHMETAFILE = 4;



            [StructLayout(LayoutKind.Sequential)]

            public class Icon

            {

                internal int cbSizeOfStruct =

                    Marshal.SizeOf(typeof(PICTDESC.Icon));

                internal int picType = PICTDESC.PICTYPE_ICON;

                internal IntPtr hicon = IntPtr.Zero;

                internal int unused1;

                internal int unused2;



                internal Icon(System.Drawing.Icon icon)

                {

                    this.hicon = icon.ToBitmap().GetHicon();

                }

            }



            [StructLayout(LayoutKind.Sequential)]

            public class Bitmap

            {

                internal int cbSizeOfStruct =

                    Marshal.SizeOf(typeof(PICTDESC.Bitmap));

                internal int picType = PICTDESC.PICTYPE_BITMAP;

                internal IntPtr hbitmap = IntPtr.Zero;

                internal IntPtr hpal = IntPtr.Zero;

                internal int unused;



                internal Bitmap(System.Drawing.Bitmap bitmap)

                {

                    this.hbitmap = bitmap.GetHbitmap();

                }

            }

        }



        public static stdole.IPictureDisp ToIPictureDisp(

            System.Drawing.Icon icon)

        {

            PICTDESC.Icon pictIcon = new PICTDESC.Icon(icon);



            return OleCreatePictureIndirect(

                pictIcon, ref iPictureDispGuid, true);

        }



        public static stdole.IPictureDisp ToIPictureDisp(

            System.Drawing.Bitmap bmp)

        {

            PICTDESC.Bitmap pictBmp = new PICTDESC.Bitmap(bmp);



            return OleCreatePictureIndirect(pictBmp, ref iPictureDispGuid, true);

        }

    }
}

 

 

 

Have no idea where did I made a mistake. I feel helpless. The addin is loaded/deleted after code adaptation for displaying button icon.  Could you please indicate the required changes ?

 

Kind Regards,

Jaromir

 

0 Likes
Accepted solutions (1)
1,188 Views
5 Replies
Replies (5)
Message 2 of 6

jjstr8
Collaborator
Collaborator

The only thing I can see if that you're probably missing the file extensions ".ico".  If that doesn't work, set a breakpoint in your Activate method and step your way through.  I generally use a resource (.resx) file since Visual Studio auto-generates a class to access the resources.

string icon16ResourceName = "InventorAddIn4.Resources.tank16.ico";
string icon32ResourceName = "InventorAddIn4.Resources.tank32.ico";
0 Likes
Message 3 of 6

Michael.Navara
Advisor
Advisor
Accepted solution

Finally I found a time to look at your code.

There are few differences in your an my PictureDispConverter. With your implementation it doesn't work for me. I attach my well tested implementation to the project which works.

Another difference is in reading the icons.

See the attached project for more information.

 

MichaelNavara_0-1680780379969.png

 

 

Message 4 of 6

jjstr8
Collaborator
Collaborator

@jzarczynski:  What was the solution?  With what you posted, the only issue I found was the file extensions.

Message 5 of 6

jzarczynski
Advocate
Advocate

@jjstr8 

 

I did not drill down into the details, however my implementation of PictureDispConverter class differs in a few places from the one used by @Michael.Navara.

 

 

0 Likes
Message 6 of 6

jjstr8
Collaborator
Collaborator

@jzarczynski :  Understood.  I just think it would help others if it's clear what fixed a given problem.  As I indicated in my earlier post, simply adding the file extension fixed what you posted.  However, I did have to make some assumptions of how your VS project was organized.  With regards to icon conversion, there weren't any differences in your implementation of the PictureDispConverter class versus what @Michael.Navara posted in, so I really don't know what the solution was.  

0 Likes