| 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 (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 (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
|