Logo
HOWTO: Get an inverted bitmap to use with the Dark theme of Visual Studio 2012 from an add-in.

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio 2012
Date: October 2012    
       
Introduction

When using the Dark theme, Visual Studio 2012 automatically inverts the colors of bitmaps of commands on toolbars, commandbars, etc. However, an add-in may require to invert the colors of some bitmap on some other user interface element.

More information

The following sample shows an add-in that inverts the bitmap shown on a form, using the ThemeDIBits method of the Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 interface. To get that interface from an add-in, see the article HOWTO: Get the Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 interface to theme a Visual Studio 2012 add-in. The sample uses the LockBits method of the Bitmap class, as explained in this sample from the MSDN documentation.

To show the sample:

  • Select the Dark theme in Visual Studio 2012.
  • Create a 24-bit bitmap using the value RGB=0,254,0 as transparent color. The sample uses this color, but you can change it to other color.
  • Create an add-in for Visual Studio 2012 with a form Form1 with two PictureBox controls on it (PictureBox1 and PictureBox2).
  • Add references to the Microsoft.VisualStudio.OLE.Interop.dll, Microsoft.VisualStudio.Shell.Interop.dll and Microsoft.VisualStudio.Shell.Interop.11.0 assemblies as explained in the article HOWTO: Get the Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 interface to theme a Visual Studio 2012 add-in.
  • Set the image of the PictureBox1 control to the bitmap created before.
  • Use this code in the Connect class, that shows the form modally with the inverted bitmap in the PictureBox2 control:
Language: VB.NET   Copy Code Copy Code (IE only)
Imports System
Imports Microsoft.VisualStudio.CommandBars
Imports Extensibility
Imports EnvDTE
Imports EnvDTE80
Imports System.Runtime.InteropServices
Imports System.Drawing

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

            InitializeAddIn()

         Case ext_ConnectMode.ext_cm_Startup
            ' OnStartupComplete will be called

      End Select

   End Sub

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

      InitializeAddIn()

   End Sub

   Private Function GetIVsUIShell5() As Microsoft.VisualStudio.Shell.Interop.IVsUIShell5

      Dim sp As Microsoft.VisualStudio.OLE.Interop.IServiceProvider
      Dim SVsUIShellType As Type
      Dim hr As Integer
      Dim serviceIntPtr As IntPtr
      Dim shell5 As Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 = Nothing
      Dim serviceObject As Object

      sp = DirectCast(_applicationObject, Microsoft.VisualStudio.OLE.Interop.IServiceProvider)

      SVsUIShellType = GetType(Microsoft.VisualStudio.Shell.Interop.SVsUIShell)

      hr = sp.QueryService(SVsUIShellType.GUID, SVsUIShellType.GUID, serviceIntPtr)

      If hr <> 0 Then

         System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr)

      ElseIf Not serviceIntPtr.Equals(IntPtr.Zero) Then

         serviceObject = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(serviceIntPtr)

         shell5 = CType(serviceObject, Microsoft.VisualStudio.Shell.Interop.IVsUIShell5)

         System.Runtime.InteropServices.Marshal.Release(serviceIntPtr)

      End If

      Return shell5

   End Function

   Private Function GetInvertedBitmap(ByVal shell5 As Microsoft.VisualStudio.Shell.Interop.IVsUIShell5, _
      ByVal inputBitmap As Bitmap, ByVal transparentColor As Color, ByVal backgroundColor As UInteger) As Bitmap

      Dim outputBitmap As Bitmap = Nothing
      Dim outputBytes() As Byte
      Dim rect As Rectangle
      Dim bitmapData As Imaging.BitmapData
      Dim sourcePointer As IntPtr
      Dim length As Integer

      Try

         outputBitmap = New Bitmap(inputBitmap)

         outputBitmap.MakeTransparent(transparentColor)

         rect = New Rectangle(0, 0, outputBitmap.Width, outputBitmap.Height)

         bitmapData = outputBitmap.LockBits(rect, Imaging.ImageLockMode.ReadWrite, outputBitmap.PixelFormat)

         sourcePointer = bitmapData.Scan0

         length = (Math.Abs(bitmapData.Stride) * outputBitmap.Height)

         outputBytes = New Byte(length - 1) {}

         Marshal.Copy(sourcePointer, outputBytes, 0, length)

         shell5.ThemeDIBits(CType(outputBytes.Length, UInt32), outputBytes, CType(outputBitmap.Width, UInt32), _
                            CType(outputBitmap.Height, UInt32), True, backgroundColor)

         Marshal.Copy(outputBytes, 0, sourcePointer, length)

         outputBitmap.UnlockBits(bitmapData)

      Catch ex As Exception

         MessageBox.Show(ex.ToString)

      End Try

      Return outputBitmap

   End Function

   Private Sub InitializeAddIn()

      Const COLOR_NAME As String = "ToolWindowBackground"

      Const GUID_COLOR_TABLE_ENVIRONMENT_CATEGORY As String = "624ed9c3-bdfd-41fa-96c3-7c824ea32e3d"

      Dim backgroundColor As UInteger
      Dim frm1 As Form1
      Dim inputBitmap As Bitmap
      Dim outputBitmap As Bitmap
      Dim almostGreenColor As Color
      Dim shell5 As Microsoft.VisualStudio.Shell.Interop.IVsUIShell5

      Try

         shell5 = GetIVsUIShell5()

         ' Get the background color of toolwindows
         backgroundColor = shell5.GetThemedColor(New System.Guid(GUID_COLOR_TABLE_ENVIRONMENT_CATEGORY), COLOR_NAME, 0)

         frm1 = New Form1()

         ' Apply the background color to the form
         frm1.BackColor = ColorTranslator.FromWin32(CType(backgroundColor And &HFFFFFF, Integer))

         ' Get the unthemed input bitmap
         inputBitmap = New Bitmap(frm1.PictureBox1.Image)

         ' Assume that the transparent color is almost green. It can be any color
         almostGreenColor = Color.FromArgb(0, 254, 0)

         ' Get the themed output bitmap
         outputBitmap = GetInvertedBitmap(shell5, inputBitmap, almostGreenColor, backgroundColor)

         inputBitmap.Dispose()

         frm1.PictureBox2.Image = outputBitmap

         frm1.ShowDialog()

         frm1.Dispose()

      Catch ex As Exception

         MessageBox.Show(ex.ToString)

      End Try

   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

End Class
Language: C#   Copy Code Copy Code (IE only)
using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;

namespace MyAddIn
{
   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:

               InitializeAddIn();
               break;

            case ext_ConnectMode.ext_cm_Startup:
               // OnStartupComplete will be called
               break;
         }
			
      }

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

      private Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 GetIVsUIShell5()
      {

         Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp = null;
         Type SVsUIShellType = null;
         int hr = 0;
         IntPtr serviceIntPtr;
         Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 shell5 = null;
         object serviceObject = null;

         sp = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)_applicationObject;

         SVsUIShellType = typeof(Microsoft.VisualStudio.Shell.Interop.SVsUIShell);

         hr = sp.QueryService(SVsUIShellType.GUID,  SVsUIShellType.GUID, out serviceIntPtr);

         if (hr != 0)
         {
            System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr);
         }
         else if (!serviceIntPtr.Equals(IntPtr.Zero))
         {
            serviceObject = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(serviceIntPtr);

            shell5 = (Microsoft.VisualStudio.Shell.Interop.IVsUIShell5)serviceObject;

            System.Runtime.InteropServices.Marshal.Release(serviceIntPtr);
         }
         return shell5;
      }

      private Bitmap GetInvertedBitmap(Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 shell5, Bitmap inputBitmap, 
         Color transparentColor, uint backgroundColor)
      {
         Bitmap outputBitmap = null;
         byte[] outputBytes = null;
         Rectangle rect;
         System.Drawing.Imaging.BitmapData bitmapData = null;
         IntPtr sourcePointer;
         int length = 0;

         try
         {
            outputBitmap = new Bitmap(inputBitmap);

            outputBitmap.MakeTransparent(transparentColor);

            rect = new Rectangle(0, 0, outputBitmap.Width, outputBitmap.Height);

            bitmapData = outputBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, outputBitmap.PixelFormat);

            sourcePointer = bitmapData.Scan0;

            length = (Math.Abs(bitmapData.Stride) * outputBitmap.Height);

            outputBytes = new byte[length];

            Marshal.Copy(sourcePointer, outputBytes, 0, length);

            shell5.ThemeDIBits((UInt32)outputBytes.Length, outputBytes, (UInt32)outputBitmap.Width, 
               (UInt32)outputBitmap.Height, true, backgroundColor);

            Marshal.Copy(outputBytes, 0, sourcePointer, length);

            outputBitmap.UnlockBits(bitmapData);

         }
         catch (Exception ex)
         {
            MessageBox.Show(ex.ToString());
         }

         return outputBitmap;
      }

      private void InitializeAddIn()
      {
         const string COLOR_NAME = "ToolWindowBackground";

         const string GUID_COLOR_TABLE_ENVIRONMENT_CATEGORY = "624ed9c3-bdfd-41fa-96c3-7c824ea32e3d";

         uint backgroundColor = 0;
         Form1 frm1 = null;
         Bitmap inputBitmap = null;
         Bitmap outputBitmap = null;
         Color almostGreenColor;
         Microsoft.VisualStudio.Shell.Interop.IVsUIShell5 shell5 = null;

         try
         {
            shell5 = GetIVsUIShell5();

            // Get the background color of toolwindows
            backgroundColor = shell5.GetThemedColor(new System.Guid(GUID_COLOR_TABLE_ENVIRONMENT_CATEGORY), COLOR_NAME, 0);

            frm1 = new Form1();

            // Apply the background color to the form
            frm1.BackColor = ColorTranslator.FromWin32(Convert.ToInt32(backgroundColor & 0xffffff));

            // Get the unthemed input bitmap.
            // Note: Change the modifer from private to public
            inputBitmap = new Bitmap(frm1.pictureBox1.Image);

            // Assume that the transparent color is almost green. It can be any color
            almostGreenColor = Color.FromArgb(0, 254, 0);

            // Get the themed output bitmap
            outputBitmap = GetInvertedBitmap(shell5, inputBitmap, almostGreenColor, backgroundColor);

            inputBitmap.Dispose();

            // Note: Change the modifer from private to public
            frm1.pictureBox2.Image = outputBitmap;

            frm1.ShowDialog();

            frm1.Dispose();

         }
         catch (Exception ex)
         {
            MessageBox.Show(ex.ToString());
         }
      }

      public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
      {
      }

      public void OnAddInsUpdate(ref Array custom)
      {
      }

      public void OnBeginShutdown(ref Array custom)
      {
      }

   }
}

Related articles


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


Top