Logo
HOWTO: Get the bitmap of a component type from Visual Studio add-in

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio 2005
Date: September 2012   Microsoft Visual Studio 2008
      Microsoft Visual Studio 2010
      Microsoft Visual Studio 2012
Introduction

This article explains how to get the bitmap of a component or control type from a Visual Studio add-in.

More information

Types that are components or controls have associated a bitmap that Visual Studio uses in different places (Toolbox, Document Outline, designers, etc.). That bitmap can be established by the creator of the component in two different ways:

  • Including a bitmap resource in the assembly of the component type with the same name than the type and the ".bmp" extension. For example, for the System.Windows.Forms.Button type, the System.Windows.Forms.dll assembly contains a bitmap resource named System.Windows.Forms.Button.bmp.
  • Using the System.Drawing.ToolboxBitmapAttribute applied to the component class. That attribute allows to specify the assembly that contains the bitmap resource (passing a type of that assembly) and the name of image resource (it can be .bmp, .png, etc.).

Both approaches possed a problem in Visual Studio 2012, which uses gray bitmaps for components rather than colored bitmaps. Since Visual Studio is multi-.NET Framework, some assembly (for example System.Windows.Forms version 4.0.0.0) can be used in Visual Studio 2010 and 2012. If the bitmaps of the assembly are colored (the default with assemblies installed by Visual Studio 2010 and .NET Framework 4), they would appear fine in Visual Studio 2010 (which uses colored bitmaps) but not in Visual Studio 2012 (which uses gray bitmaps). And if Visual Studio 2012 would install new versions of assemblies of a previous .NET Framework with gray bitmaps, they would appear fine (gray) in Visual Studio 2012 but also gray in Visual Studio 2010 (this happened during the beta releases of Visual Studio 2012 with the System.Windows.Forms 4.0.0.0 assembly).

To solve that problem, Visual Studio 2012 and the .NET Framework 4.5 introduced a way to allow multiple versions of a bitmap (color, gray) for the same component type of a given assembly, overriding the default bitmap of a component in previous versions of Visual Studio and .NET Framework through the use of a suffix that allows a different name (and even assembly) for the bitmap resource. The approach is the following:

  • The assembly containing the component type must specify with one of the following assembly-level attributes that it opts-in to override the default bitmaps:
    • System.Drawing.BitmapSuffixInSameAssemblyAttribute: with this attribute the assembly containing the component indicates that it provides both the default bitmap resource and a different bitmap resource whose name uses an appended suffix. For example, the assembly can contain the MyNamespace.MyControl.bmp and MyNamespace.MyControl.MySuffix.bmp resource bitmaps.
    • System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute: with this attribute the assembly containing the component indicates that while it provides the default bitmap resource, the different bitmap resource is contained in a (satellite) assembly whose name uses an appended suffix (the name of the bitmap resource in this satellite assembly remains the same than in the original assembly). For example, the assembly MyAssembly.dll can contain the MyNamespace.MyControl.bmp resource and the assembly MyAssembly.MySuffix.dll can contain a different resource with the same MyNamespace.MyControl.bmp name.
  • The suffix is specified through a .config file. The feature that retrieves the resource bitmap must read this suffix if one of the previous attributes is applied to the assembly, and then locate the bitmap resource using it.

For example:

  • The System.Windows.Forms Version 4.0.0.0 assembly installed by the .NET Framework 4.5 specifies the BitmapSuffixInSatelliteAssembly attribute (note that this attribute and the other one were introduced by .NET Framework 4.5, so the System.Windows.Forms Version 4.0.0.0 installed by Visual Studio 2010 and the .NET Framework 4 did not contain it).
  • That assembly contains the default (colored) bitmaps for controls.
  • The devenv.exe.config file of Visual Studio 2012 (in the folder C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE) specifies the following suffix at the end:

    <system.drawing bitmapSuffix=".VisualStudio.11.0" />
  • So, the .NET Framework 4.5 provides a System.Windows.Forms.VisualStudio.11.0.dll (in the folder C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms.VisualStudio.11.0\v4.0_4.0.0.0__b77a5c561934e089) with the gray bitmaps used by Visual Studio 2012 features.

The following add-in shows how to get bitmap of a component with any of these approaches. It gets the bitmap of the System.Windows.Forms.Button type and shows it on a form (Form1). In Visual Studio 2012 it gets the gray bitmap correctly.

Language: VB.NET   Copy Code Copy Code (IE only)
Imports System
Imports Microsoft.VisualStudio.CommandBars
Imports Extensibility
Imports EnvDTE
Imports EnvDTE80

' Reference these assemblies:
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Configuration

Public Class Connect
   Implements IDTExtensibility2

   Private _applicationObject As DTE2
   Private _addInInstance As AddIn

   Public Sub OnConnection(ByVal application As Object, ByVal connectMode As ext_ConnectMode, ByVal addInInst As Object, _
      ByRef custom As Array) Implements IDTExtensibility2.OnConnection

      _applicationObject = CType(application, DTE2)
      _addInInstance = CType(addInInst, AddIn)

      Select Case connectMode

         Case ext_ConnectMode.ext_cm_AfterStartup

            Call Initialize()

         Case ext_ConnectMode.ext_cm_Startup
            ' OnStartupComplete will be called

      End Select

   End Sub

   Public Sub OnStartupComplete(ByRef custom As Array) Implements IDTExtensibility2.OnStartupComplete
      Initialize()
   End Sub

   Public Sub OnDisconnection(ByVal disconnectMode As ext_DisconnectMode, ByRef custom As Array) _
      Implements IDTExtensibility2.OnDisconnection
   End Sub

   Public Sub OnAddInsUpdate(ByRef custom As Array) Implements IDTExtensibility2.OnAddInsUpdate
   End Sub

   Public Sub OnBeginShutdown(ByRef custom As Array) Implements IDTExtensibility2.OnBeginShutdown
   End Sub

   Private Sub Initialize()

      Dim bmp As Bitmap
      Dim frm As Form1

      bmp = GetComponentBitmap(GetType(System.Windows.Forms.Button))
      frm = New Form1()
      frm.BackgroundImageLayout = ImageLayout.None
      frm.BackgroundImage = bmp
      Using (frm)
         frm.ShowDialog()
      End Using

   End Sub

   Private Function GetComponentBitmap(ByVal componentType As Type) As Bitmap

      Dim bmp As Bitmap

      bmp = GetComponentBitmapByEmbeddedResource(componentType)

      If bmp Is Nothing Then
         bmp = GetComponentBitmapByToolboxBitmapAttribute(componentType)
      End If

      If bmp IsNot Nothing Then
         bmp.MakeTransparent(Color.Magenta)
      End If

      Return bmp

   End Function

   Private Function GetComponentBitmapByEmbeddedResource(ByVal componentType As Type) As Bitmap

      Dim bmp As Bitmap = Nothing
      Dim originalAssembly As System.Reflection.Assembly
      Dim suffix As String
      Dim satelliteAssembly As System.Reflection.Assembly
      Dim originalTypeFullName As String
      Dim newTypeFullName As String

      originalAssembly = componentType.Assembly
      originalTypeFullName = componentType.FullName

      If AssemblyHasCustomAttribute(originalAssembly, "System.Drawing.BitmapSuffixInSameAssemblyAttribute") Then

         suffix = GetAssemblySuffix()

         If suffix <> "" Then

            newTypeFullName = AppendSuffix(originalTypeFullName, suffix)

            bmp = GetBitmapFromManifestResource(originalAssembly, newTypeFullName)

         End If

      ElseIf AssemblyHasCustomAttribute(originalAssembly, "System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute") Then

         suffix = GetAssemblySuffix()

         If suffix <> "" Then

            satelliteAssembly = GetSatelliteAssembly(originalAssembly, suffix)

            If satelliteAssembly IsNot Nothing Then
               bmp = GetBitmapFromManifestResource(satelliteAssembly, originalTypeFullName)
            End If

         End If

      End If

      If bmp Is Nothing Then

         bmp = GetBitmapFromManifestResource(componentType.Assembly, originalTypeFullName)

      End If

      Return bmp

   End Function

   Private Function GetComponentBitmapByToolboxBitmapAttribute(ByVal componentType As Type) As Bitmap

      Dim toolboxBitmapAttributeArray() As Object
      Dim toolboxBitmapAttribute As ToolboxBitmapAttribute
      Dim bmp As Bitmap = Nothing
      Dim img As Image

      toolboxBitmapAttributeArray = componentType.GetCustomAttributes(GetType(ToolboxBitmapAttribute), True)

      If toolboxBitmapAttributeArray IsNot Nothing Then

         If toolboxBitmapAttributeArray.Length > 0 Then

            toolboxBitmapAttribute = CType(toolboxBitmapAttributeArray(0), ToolboxBitmapAttribute)

            img = toolboxBitmapAttribute.GetImage(componentType)

            If img IsNot Nothing Then

               bmp = New Bitmap(img)

            End If

         End If

      End If

      Return bmp

   End Function

   Private Function GetAssemblySuffix() As String

      Dim section As Object
      Dim configurationSection As System.Configuration.ConfigurationSection
      Dim result As String = ""

      Try

         ' In VS 2012 this reads the "system.drawing" of the following config file:
         ' C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe.config
         ' which has the value:
         ' <system.drawing bitmapSuffix=".VisualStudio.11.0" />

         section = System.Configuration.ConfigurationManager.GetSection("system.drawing")

         configurationSection = CType(section, System.Configuration.ConfigurationSection)

         If configurationSection IsNot Nothing Then
            result = configurationSection.ElementInformation.Properties.Item("bitmapSuffix").Value.ToString()
         End If

      Catch ex As Exception
      End Try

      Return result

   End Function

   Private Function AppendSuffix(ByVal sOriginalName As String, ByVal sSuffix As String) As String

      Dim result As String = sOriginalName

      Try

         result = IO.Path.ChangeExtension(sOriginalName, sSuffix & IO.Path.GetExtension(sOriginalName))

      Catch ex As Exception
      End Try

      Return result

   End Function

   Private Function GetSatelliteAssembly(ByVal assembly As System.Reflection.Assembly, ByVal suffix As String) _
      As System.Reflection.Assembly

      Dim satelliteAssembly As System.Reflection.Assembly = Nothing
      Dim asmName As System.Reflection.AssemblyName

      asmName = assembly.GetName()

      asmName.Name = asmName.Name & suffix

      Try
         satelliteAssembly = System.Reflection.Assembly.Load(asmName)
      Catch ex As Exception
      End Try

      Return satelliteAssembly

   End Function

   Private Function AssemblyHasCustomAttribute(ByVal assembly As System.Reflection.Assembly, ByVal attributeName As String) As Boolean

      Dim result As Boolean = False
      Dim assemblyAttributeArray() As Object
      Dim attr As Attribute

      Try

         assemblyAttributeArray = assembly.GetCustomAttributes(False)

         If assemblyAttributeArray IsNot Nothing Then

            If assemblyAttributeArray.Length > 0 Then

               For Each attr In assemblyAttributeArray

                  If attr.ToString() = attributeName Then

                     result = True
                     Exit For

                  End If

               Next

            End If

         End If

      Catch ex As Exception
      End Try

      Return result

   End Function

   Private Function GetBitmapFromManifestResource(ByVal assembly As System.Reflection.Assembly, ByVal typeFullName As String) As Bitmap

      Dim bitmapStream As System.IO.Stream
      Dim bmp As Bitmap = Nothing

      If assembly IsNot Nothing Then

         Try

            bitmapStream = assembly.GetManifestResourceStream(typeFullName & ".bmp")

            If bitmapStream IsNot Nothing Then

               bmp = New Bitmap(bitmapStream)

            End If

         Catch ex As Exception
         End Try

      End If

      Return bmp

   End Function

End Class
Language: C#   Copy Code Copy Code (IE only)
using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;

// Reference these assemblies:
using System.Windows.Forms;
using System.Drawing;
using System.Configuration;

namespace MyAddIn1
{
   public class Connect : IDTExtensibility2
   {
      private DTE2 _applicationObject;
      private AddIn _addInInstance;
      
      public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
      {
         _applicationObject = (DTE2)application;
         _addInInstance = (AddIn)addInInst;
      
         switch (connectMode)
         {
            case ext_ConnectMode.ext_cm_AfterStartup:

               Initialize();
               break;

            case ext_ConnectMode.ext_cm_Startup:
               
               // OnStartupComplete will be called
               break;
         }
      }
      public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
      {
      }

      public void OnStartupComplete(ref Array custom)
      {
         Initialize();
      }

      public void OnAddInsUpdate(ref Array custom)
      {
      }
      public void OnBeginShutdown(ref Array custom)
      {
      }

      private void Initialize()
      {
         Bitmap bmp = null;
         Form1 frm = null;

         bmp = GetComponentBitmap(typeof(System.Windows.Forms.Button));
         frm = new Form1();
         frm.BackgroundImageLayout = ImageLayout.None;
         frm.BackgroundImage = bmp;
         using ((frm))
         {
            frm.ShowDialog();
         }
      }

      private Bitmap GetComponentBitmap(Type componentType)
      {
         Bitmap bmp = null;

         bmp = GetComponentBitmapByEmbeddedResource(componentType);

         if (bmp == null)
         {
            bmp = GetComponentBitmapByToolboxBitmapAttribute(componentType);
         }

         if (bmp != null)
         {
            bmp.MakeTransparent(Color.Magenta);
         }

         return bmp;
      }

      private Bitmap GetComponentBitmapByEmbeddedResource(Type componentType)
      {
         Bitmap bmp = null;
         System.Reflection.Assembly originalAssembly = null;
         string suffix = null;
         System.Reflection.Assembly satelliteAssembly = null;
         string originalTypeFullName = null;
         string newTypeFullName = null;

         originalAssembly = componentType.Assembly;
         originalTypeFullName = componentType.FullName;

         if (AssemblyHasCustomAttribute(originalAssembly, "System.Drawing.BitmapSuffixInSameAssemblyAttribute"))
         {
            suffix = GetAssemblySuffix();

            if (!string.IsNullOrEmpty(suffix))
            {
               newTypeFullName = AppendSuffix(originalTypeFullName, suffix);

               bmp = GetBitmapFromManifestResource(originalAssembly, newTypeFullName);

            }

         }
         else if (AssemblyHasCustomAttribute(originalAssembly, "System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute"))
         {
            suffix = GetAssemblySuffix();

            if (!string.IsNullOrEmpty(suffix))
            {
               satelliteAssembly = GetSatelliteAssembly(originalAssembly, suffix);

               if (satelliteAssembly != null)
               {
                  bmp = GetBitmapFromManifestResource(satelliteAssembly, originalTypeFullName);
               }
            }
         }

         if (bmp == null)
         {
            bmp = GetBitmapFromManifestResource(componentType.Assembly, originalTypeFullName);
         }

         return bmp;
      }

      private Bitmap GetComponentBitmapByToolboxBitmapAttribute(Type componentType)
      {
         object[] toolboxBitmapAttributeArray = null;
         ToolboxBitmapAttribute toolboxBitmapAttribute = null;
         Bitmap bmp = null;
         Image img = null;

         toolboxBitmapAttributeArray = componentType.GetCustomAttributes(typeof(ToolboxBitmapAttribute), true);

         if (toolboxBitmapAttributeArray != null)
         {
            if (toolboxBitmapAttributeArray.Length > 0)
            {
               toolboxBitmapAttribute = (ToolboxBitmapAttribute)toolboxBitmapAttributeArray[0];

               img = toolboxBitmapAttribute.GetImage(componentType);

               if (img != null)
               {
                  bmp = new Bitmap(img);
               }
            }
         }
         return bmp;
      }

      private string GetAssemblySuffix()
      {

         object section = null;
         System.Configuration.ConfigurationSection configurationSection = null;
         string result = "";

         try
         {
            // In VS 2012 this reads the "system.drawing" of the following config file:
            // C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe.config
            // which has the value:
            // <system.drawing bitmapSuffix=".VisualStudio.11.0" />

            section = System.Configuration.ConfigurationManager.GetSection("system.drawing");

            configurationSection = (System.Configuration.ConfigurationSection)section;

            if (configurationSection != null)
            {
               result = configurationSection.ElementInformation.Properties["bitmapSuffix"].Value.ToString();
            }
         }
         catch
         {
         }

         return result;
      }

      private string AppendSuffix(string sOriginalName, string sSuffix)
      {
         string result = sOriginalName;

         try
         {
            result = System.IO.Path.ChangeExtension(sOriginalName, sSuffix + System.IO.Path.GetExtension(sOriginalName));
         }
         catch
         {
         }

         return result;
      }

      private System.Reflection.Assembly GetSatelliteAssembly(System.Reflection.Assembly assembly, string suffix)
      {
         System.Reflection.Assembly satelliteAssembly = null;
         System.Reflection.AssemblyName asmName = null;

         asmName = assembly.GetName();

         asmName.Name = asmName.Name + suffix;

         try
         {
            satelliteAssembly = System.Reflection.Assembly.Load(asmName);
         }
         catch
         {
         }

         return satelliteAssembly;
      }

      private bool AssemblyHasCustomAttribute(System.Reflection.Assembly assembly, string attributeName)
      {

         bool result = false;
         object[] assemblyAttributeArray = null;
         Attribute attr = null;

         try
         {
            assemblyAttributeArray = assembly.GetCustomAttributes(false);

            if (assemblyAttributeArray != null)
            {
               if (assemblyAttributeArray.Length > 0)
               {
                  foreach (Attribute attr_loopVariable in assemblyAttributeArray)
                  {
                     attr = attr_loopVariable;

                     if (attr.ToString() == attributeName)
                     {
                        result = true;
                        break; // TODO: might not be correct. Was : Exit For
                     }
                  }
               }
            }
         }
         catch
         {
         }

         return result;
      }

      private Bitmap GetBitmapFromManifestResource(System.Reflection.Assembly assembly, string typeFullName)
      {

         System.IO.Stream bitmapStream = null;
         Bitmap bmp = null;

         if (assembly != null)
         {

            try
            {
               bitmapStream = assembly.GetManifestResourceStream(typeFullName + ".bmp");

               if (bitmapStream != null)
               {
                  bmp = new Bitmap(bitmapStream);
               }
            }
            catch
            {
            }
         }

         return bmp;
      }	
   }
}

Go back to the 'Resources for Visual Studio .NET extensibility' section for more articles like this


Top