Image service and catalog

This cookbook contains guidance and best practices for adopting the Visual Studio Image Service and Image Catalog introduced in Visual Studio 2015.

The image service introduced in Visual Studio 2015 lets developers get the best images for the device and the user's chosen theme to display the image, including correct theming for the context in which they are displayed. Adopting the image service will help eliminate major pain points related to asset maintenance, HDPI scaling, and theming.

Problems today Solutions
Background color blending Built-in alpha blending
Theming (some) images Theme metadata
High Contrast mode Alternate High Contrast resources
Need multiple resources for different DPI modes Selectable resources with vector-based fallback
Duplicate images One identifier per image concept

Why adopt the image service?

  • Always get the latest "pixel-perfect" image from Visual Studio

  • You can submit and use your own images

  • No need to test your images out when Windows adds new DPI scaling

  • Address old architectural hurdles in your implementations

    The Visual Studio shell toolbar before and after using the image service:

    Image Service Before and After

How it works

The image service can supply a bitmapped image suitable for any supported UI framework:

  • WPF: BitmapSource

  • WinForms: System.Drawing.Bitmap

  • Win32: HBITMAP

    Image service flow diagram

    Image Service Flow Diagram

    Image monikers

    An image moniker (or moniker for short) is a GUID/ID pair that uniquely identifies an image asset or image list asset in the image library.

    Known monikers

    The set of image monikers contained in the Visual Studio Image Catalog and publicly consumable by any Visual Studio component or extension.

    Image manifest files

    Image manifest (.imagemanifest) files are XML files that define a set of image assets, the monikers that represent those assets, and the real image or images that represent each asset. Image manifests can define standalone images or image lists for legacy UI support. Additionally, there are attributes that can be set either on the asset or on the individual images behind each asset to change when and how those assets are displayed.

    Image manifest schema

    A complete image manifest looks like this:

<ImageManifest>
      <!-- zero or one Symbols elements -->
      <Symbols>
        <!-- zero or more Import, Guid, ID, or String elements -->
      </Symbols>
      <!-- zero or one Images elements -->
      <Images>
        <!-- zero or more Image elements -->
      </Images>
      <!-- zero or one ImageLists elements -->
      <ImageLists>
        <!-- zero or more ImageList elements -->
      </ImageLists>
</ImageManifest>

Symbols

As a readability and maintenance aid, the image manifest can use symbols for attribute values. Symbols are defined like this:

<Symbols>
      <Import Manifest="manifest" />
      <Guid Name="ShellCommandGuid" Value="8ee4f65d-bab4-4cde-b8e7-ac412abbda8a" />
      <ID Name="cmdidSaveAll" Value="1000" />
      <String Name="AssemblyName" Value="Microsoft.VisualStudio.Shell.UI.Internal" />
</Symbols>
Subelement Definition
Import Imports the symbols of the given manifest file for use in the current manifest
Guid The symbol represents a GUID and must match GUID formatting
ID The symbol represents an ID and must be a nonnegative integer
String The symbol represents an arbitrary string value

Symbols are case-sensitive, and referenced using $(symbol-name) syntax:

<Image Guid="$(ShellCommandGuid)" ID="$(cmdidSaveAll)" >
      <Source Uri="/$(AssemblyName);Component/Resources/image.xaml" />
</Image>

Some symbols are predefined for all manifests. These can be used in the Uri attribute of the <Source> or <Import> element to reference paths on the local machine.

Symbol Description
CommonProgramFiles The value of the %CommonProgramFiles% environment variable
LocalAppData The value of the %LocalAppData% environment variable
ManifestFolder The folder containing the manifest file
MyDocuments The full path of the My Documents folder of the current user
ProgramFiles The value of the %ProgramFiles% environment variable
System The Windows\System32 folder
WinDir The value of the %WinDir% environment variable

Image

The <Image> element defines an image that can be referenced by a moniker. The GUID and ID taken together form the image moniker. The moniker for the image must be unique across the entire image library. If more than one image has a given moniker, the first one encountered while building the library is the one that is retained.

It must contain at least one source. Size-neutral sources will give the best results across a broad range of sizes, but they are not required. If the service is asked for an image of a size not defined in the <Image> element and there is no size-neutral source, the service will choose the best size-specific source and scale it to the requested size.

<Image Guid="guid" ID="int" AllowColorInversion="true/false">
      <Source ... />
      <!-- optional additional Source elements -->
</Image>
Attribute Definition
Guid [Required] The GUID portion of the image moniker
ID [Required] The ID portion of the image moniker
AllowColorInversion [Optional, default true] Indicates whether the image can have its colors programmatically inverted when used on a dark background.

Source

The <Source> element defines a single image source asset (XAML and PNG).

<Source Uri="uri" Background="background">
      <!-- optional NativeResource element -->
 </Source>
Attribute Definition
Uri [Required] A URI that defines where the image can be loaded from. It can be one of the following:

- A Pack URI using the application:/// authority
- An absolute component resource reference
- A path to a file containing a native resource
Background [Optional] Indicates what on kind of background the source is intended to be used.

It can be one of the following:

Light: The source can be used on a light background.

Dark: The source can be used on a dark background.

HighContrast: The source can be used on any background in High Contrast mode.

HighContrastLight: The source can be used on a light background in High Contrast mode.

HighContrastDark: The source can be used on a dark background in High Contrast mode.

If the Background attribute is omitted, the source can be used on any background.

If Background is Light, Dark, HighContrastLight, or HighContrastDark, the source's colors are never inverted. If Background is omitted or set to HighContrast, the inversion of the source's colors is controlled by the image's AllowColorInversion attribute.

A <Source> element can have exactly one of the following optional subelements:

Element Attributes (all required) Definition
<Size> Value The source will be used for images of the given size (in device units). The image will be square.
<SizeRange> MinSize, MaxSize The source will be used for images from MinSize to MaxSize (in device units) inclusively. The image will be square.
<Dimensions> Width, Height The source will be used for images of the given width and height (in device units).
<DimensionRange> MinWidth, MinHeight,

MaxWidth, MaxHeight
The source will be used for images from the minimum width/height to the maximum width/height (in device units) inclusively.

A <Source> element can also have an optional <NativeResource> subelement, which defines a <Source> that is loaded from a native assembly rather than a managed assembly.

<NativeResource Type="type" ID="int" />
Attribute Definition
Type [Required] The type of the native resource, either XAML or PNG
ID [Required] The integer ID portion of the native resource

ImageList

The <ImageList> element defines a collection of images that can be returned in a single strip. The strip is built on demand, as needed.

<ImageList>
      <ContainedImage Guid="guid" ID="int" External="true/false" />
      <!-- optional additional ContainedImage elements -->
 </ImageList>
Attribute Definition
Guid [Required] The GUID portion of the image moniker
ID [Required] The ID portion of the image moniker
External [Optional, default false] Indicates whether the image moniker references an image in the current manifest.

The moniker for the contained image does not have to reference an image defined in the current manifest. If the contained image cannot be found in the image library, a blank placeholder image will be used in its place.

Using the image service

First steps (managed)

To use the image service, you need to add references to some or all of the following assemblies to your project:

  • Microsoft.VisualStudio.ImageCatalog.dll

    • Required if you use the built-in image catalog KnownMonikers.
  • Microsoft.VisualStudio.Imaging.dll

    • Required if you use CrispImage and ImageThemingUtilities in your WPF UI.
  • Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll

    • Required if you use the ImageMoniker and ImageAttributes types.

    • EmbedInteropTypes should be set to true.

  • Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime

    • Required if you use the IVsImageService2 type.

    • EmbedInteropTypes should be set to true.

  • Microsoft.VisualStudio.Utilities.dll

    • Required if you use the BrushToColorConverter for the ImageThemingUtilities.ImageBackgroundColor in your WPF UI.
  • Microsoft.VisualStudio.Shell.<VSVersion>.0

    • Required if you use the IVsUIObject type.
  • Microsoft.VisualStudio.Shell.Interop.10.0.dll

    • Required if you use the WinForms-related UI helpers.

    • EmbedInteropTypes should be set to true

First steps (native)

To use the image service, you need to include some or all of the following headers to your project:

  • KnownImageIds.h

    • Required if you use the built-in image catalog KnownMonikers, but cannot use the ImageMoniker type, such as when returning values from IVsHierarchy GetGuidProperty or GetProperty calls.
  • KnownMonikers.h

    • Required if you use the built-in image catalog KnownMonikers.
  • ImageParameters140.h

    • Required if you use the ImageMoniker and ImageAttributes types.
  • VSShell140.h

    • Required if you use the IVsImageService2 type.
  • ImageThemingUtilities.h

    • Required if you are unable to let the image service handle theming for you.

    • Do not use this header if the image service can handle your image theming.

  • VSUIDPIHelper.h

    • Required if you use the DPI helpers to get the current DPI.
  • VsDpiAwareness.h

    • Required if you use the DPI awareness helpers to get the current DPI.

How do I write new WPF UI?

  1. Start by adding the assembly references required in the above first steps section to your project. You don't need to add all of them, so add just the references you need. (Note: if you are using or have access to Colors instead of Brushes, then you can skip the reference to Utilities, since you won't need the converter.)

  2. Select the desired image and get its moniker. Use a KnownMoniker, or use your own if you have your own custom images and monikers.

  3. Add CrispImages to your XAML. (See below example.)

  4. Set the ImageThemingUtilities.ImageBackgroundColor property in your UI hierarchy. (This should be set at the location where the background color is known, not necessarily on the CrispImage.) (See below example.)

<Window
  x:Class="WpfApplication.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:imaging="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.Imaging"
  xmlns:theming="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Imaging"
  xmlns:utilities="clr-namespace:Microsoft.Internal.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.Imaging"
  xmlns:catalog="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.ImageCatalog"
  Title="MainWindow" Height="350" Width="525" UseLayoutRounding="True">
  <Window.Resources>
    <utilities:BrushToColorConverter x:Key="BrushToColorConverter"/>
  </Window.Resources>
  <StackPanel Background="White" VerticalAlignment="Center"
    theming:ImageThemingUtilities.ImageBackgroundColor="{Binding Background, RelativeSource={RelativeSource Self}, Converter={StaticResource BrushToColorConverter}}">
    <imaging:CrispImage Width="16" Height="16" Moniker="{x:Static catalog:KnownMonikers.MoveUp}" />
  </StackPanel>
</Window>

How do I update existing WPF UI?

Updating existing WPF UI is a relatively simple process that consists of three basic steps:

  1. Replace all <Image> elements in your UI with <CrispImage> elements.

  2. Change all the Source attributes to Moniker attributes.

    • If the image never changes and you are using KnownMonikers, then statically bind that property to the KnownMoniker. (See the above example.)

    • If the image never changes and you are using your own custom image, then statically bind to your own moniker.

    • If the image can change, bind the Moniker attribute to a code property that notifies on property changes.

  3. Somewhere in the UI hierarchy, set ImageThemingUtilities.ImageBackgroundColor to make sure color inversion works correctly.

    • This might require the use of the BrushToColorConverter class. (See the above example.)

How do I update Win32 UI?

Add the following to your code wherever appropriate to replace the raw loading of images. Switch values for returning HBITMAPs versus HICONs versus HIMAGELIST as needed.

Get the image service

CComPtr<IVsImageService2> spImgSvc;
CGlobalServiceProvider::HrQueryService(SID_SVsImageService, &spImgSvc);

Requesting the image

ImageAttributes attr = { 0 };
attr.StructSize      = sizeof(attributes);
attr.Format          = DF_Win32;
// IT_Bitmap for HBITMAP, IT_Icon for HICON, IT_ImageList for HIMAGELIST
attr.ImageType       = IT_Bitmap;
attr.LogicalWidth    = 16;
attr.LogicalHeight   = 16;
attr.Dpi             = VsUI::DpiHelper::GetDeviceDpiX();
// Desired RGBA color, if you don't use this, don't set IAF_Background below
attr.Background      = 0xFFFFFFFF;
attr.Flags           = IAF_RequiredFlags | IAF_Background;

CComPtr<IVsUIObject> spImg;
// Replace this KnownMoniker with your desired ImageMoniker
spImgSvc->GetImage(KnownMonikers::Blank, attributes, &spImg);
UINT dpiX, dpiY;
HWND hwnd = // get the HWND where the image will be displayed
VsUI::CDpiAwareness::GetDpiForWindow(hwnd, &dpiX, &dpiY);

ImageAttributes attr = { 0 };
attr.StructSize      = sizeof(attributes);
attr.Format          = DF_Win32;
// IT_Bitmap for HBITMAP, IT_Icon for HICON, IT_ImageList for HIMAGELIST
attr.ImageType       = IT_Bitmap;
attr.LogicalWidth    = 16;
attr.LogicalHeight   = 16;
attr.Dpi             = dpiX;
// Desired RGBA color, if you don't use this, don't set IAF_Background below
attr.Background      = 0xFFFFFFFF;
attr.Flags           = IAF_RequiredFlags | IAF_Background;

CComPtr<IVsUIObject> spImg;
// Replace this KnownMoniker with your desired ImageMoniker
spImgSvc->GetImage(KnownMonikers::Blank, attributes, &spImg);

How do I update WinForms UI?

Add the following to your code wherever appropriate to replace the raw loading of images. Switch values for returning Bitmaps versus Icons as needed.

Helpful using statement

using GelUtilities = Microsoft.Internal.VisualStudio.PlatformUI.Utilities;

Get the image service

// This or your preferred way of querying for Visual Studio services
IVsImageService2 imageService = (IVsImageService2)Package.GetGlobalService(typeof(SVsImageService));

Request the image

ImageAttributes attributes = new ImageAttributes
{
    StructSize    = Marshal.SizeOf(typeof(ImageAttributes)),
    // IT_Bitmap for Bitmap, IT_Icon for Icon, IT_ImageList for ImageList
    ImageType     = (uint)_UIImageType.IT_Bitmap,
    Format        = (uint)_UIDataFormat.DF_WinForms,
    LogicalWidth  = 16,
    LogicalHeight = 16,
    Dpi           = (int)DpiHelper.DeviceDpiX;
    // Desired RGBA color, if you don't use this, don't set IAF_Background below
    Background    = 0xFFFFFFFF,
    Flags         = unchecked((uint)_ImageAttributesFlags.IAF_RequiredFlags | _ImageAttributesFlags.IAF_Background),
};

// Replace this KnownMoniker with your desired ImageMoniker
IVsUIObject uIObj = imageService.GetImage(KnownMonikers.Blank, attributes);

Bitmap bitmap = (Bitmap)GelUtilities.GetObjectData(uiObj); // Use this if you need a bitmap
// Icon icon = (Icon)GelUtilities.GetObjectData(uiObj);    // Use this if you need an icon
Control control = // get the control where the image will be displayed

ImageAttributes attributes = new ImageAttributes
{
    StructSize    = Marshal.SizeOf(typeof(ImageAttributes)),
    // IT_Bitmap for Bitmap, IT_Icon for Icon, IT_ImageList for ImageList
    ImageType     = (uint)_UIImageType.IT_Bitmap,
    Format        = (uint)_UIDataFormat.DF_WinForms,
    LogicalWidth  = 16,
    LogicalHeight = 16,
    Dpi           = (int)DpiAwareness.GetWindowDpi(control.Handle);
    // Desired RGBA color, if you don't use this, don't set IAF_Background below
    Background    = 0xFFFFFFFF,
    Flags         = unchecked((uint)_ImageAttributesFlags.IAF_RequiredFlags | _ImageAttributesFlags.IAF_Background),
};

// Replace this KnownMoniker with your desired ImageMoniker
IVsUIObject uIObj = imageService.GetImage(KnownMonikers.Blank, attributes);

Bitmap bitmap = (Bitmap)GelUtilities.GetObjectData(uiObj); // Use this if you need a bitmap
// Icon icon = (Icon)GelUtilities.GetObjectData(uiObj);    // Use this if you need an icon

How do I use image monikers in a new tool window?

The VSIX package project template was updated for Visual Studio 2015. To create a new tool window, right-click on the VSIX project and select Add > New Item (Ctrl+Shift+A). Under the Extensibility node for the project language, select Custom Tool Window, give the tool window a name, and press the Add button.

These are the key places to use monikers in a tool window. Follow the instructions for each:

  1. The tool window tab when the tabs get small enough (also used in the Ctrl+Tab window switcher).

    Add this line to the constructor for the class that derives from the ToolWindowPane type:

    // Replace this KnownMoniker with your desired ImageMoniker
    this.BitmapImageMoniker = KnownMonikers.Blank;
    
  2. The command to open the tool window.

    In the .vsct file for the package, edit the tool window's command button:

    <Button guid="guidPackageCmdSet" id="CommandId" priority="0x0100" type="Button">
      <Parent guid="guidSHLMainMenu" id="IDG_VS_WNDO_OTRWNDWS1"/>
      <!-- Replace this KnownMoniker with your desired ImageMoniker -->
      <Icon guid="ImageCatalogGuid" id="Blank" />
      <!-- Add this -->
      <CommandFlag>IconIsMoniker</CommandFlag>
      <Strings>
        <ButtonText>MyToolWindow</ButtonText>
      </Strings>
    </Button>
    

    How do I use image monikers in an existing tool window?

    Updating an existing tool window to use image monikers is similar to the steps for creating a new tool window.

    These are the key places to use monikers in a tool window. Follow the instructions for each:

  3. The tool window tab when the tabs get small enough (also used in the Ctrl+Tab window switcher).

    1. Remove these lines (if they exist) in the constructor for the class that derives from the ToolWindowPane type:

      this.BitmapResourceID = <Value>;
      this.BitmapIndex = <Value>;
      
    2. See step #1 of the "How do I use image monikers in a new tool window?" section above.

  4. The command to open the tool window.

    • See step #2 of the "How do I use image monikers in a new tool window?" section above.

How do I use image monikers in a .vsct file?

Update your .vsct file as indicated by the commented lines below:

<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <!--  Include the definitions for images included in the VS image catalog -->
  <Include href="KnownImageIds.vsct"/>
  <Commands package="guidMyPackage">
    <Buttons>
      <Button guid="guidMyCommandSet" id="cmdidMyCommand" priority="0x0000" type="Button">
        <!-- Add an Icon element, changing the attributes to match the image moniker you want to use.
             In this case, we're using the Guid for the VS image catalog.
             Change the id attribute to be the ID of the desired image moniker. -->
        <Icon guid="ImageCatalogGuid" id="OpenFolder" />
        <CommandFlag>DynamicVisibility</CommandFlag>
        <CommandFlag>DefaultInvisible</CommandFlag>
        <CommandFlag>DefaultDisabled</CommandFlag>
        <CommandFlag>CommandWellOnly</CommandFlag>
        <CommandFlag>IconAndText</CommandFlag>
        <!-- Add the IconIsMoniker CommandFlag -->
        <CommandFlag>IconIsMoniker</CommandFlag>
        <Strings>
          <ButtonText>Quick Fixes...</ButtonText>
          <CommandName>Show Quick Fixes</CommandName>
          <CanonicalName>ShowQuickFixes</CanonicalName>
          <LocCanonicalName>ShowQuickFixes</LocCanonicalName>
        </Strings>
      </Button>
    </Buttons>
  </Commands>
  <!-- It is recommended that you remove <Bitmap> elements that are no longer used in the vsct file -->
  <Symbols>
    <GuidSymbol name="guidMyPackage"    value="{1491e936-6ffe-474e-8371-30e5920d8fdd}" />
    <GuidSymbol name="guidMyCommandSet" value="{10347de4-69a9-47f4-a950-d3301f6d2bc7}">
      <IDSymbol name="cmdidMyCommand" value="0x9437" />
    </GuidSymbol>
  </Symbols>
</CommandTable>

What if my .vsct file also needs to be read by older versions of Visual Studio?

Older versions of Visual Studio do not recognize the IconIsMoniker command flag. You can use images from the image service on versions of Visual Studio that support it, but continue to use old-style images on older versions of Visual Studio. To do this, you'd leave the .vsct file unchanged (and therefore compatible with older versions of Visual Studio), and create a CSV (comma-separated values) file that maps from GUID/ID pairs defined in a .vsct file's <Bitmaps> element to image moniker GUID/ID pairs.

The format of the mapping CSV file is:

Icon guid, Icon id, Moniker guid, Moniker id
b714fcf7-855e-4e4c-802a-1fd87144ccad,1,fda30684-682d-421c-8be4-650a2967058e,100
b714fcf7-855e-4e4c-802a-1fd87144ccad,2,fda30684-682d-421c-8be4-650a2967058e,200

The CSV file is deployed with the package and its location is specified by the IconMappingFilename property of the ProvideMenuResource package attribute:

[ProvideMenuResource("MyPackage.ctmenu", 1, IconMappingFilename="IconMappings.csv")]

The IconMappingFilename is either a relative path implicitly rooted at $PackageFolder$ (as in the example above), or an absolute path explicitly rooted at a directory defined by an environment variable, such as @"%UserProfile%\dir1\dir2\MyMappingFile.csv".

How do I port a project system?

How to supply ImageMonikers for a project

  1. Implement VSHPROPID_SupportsIconMonikers on the project's IVsHierarchy, and return true.

  2. Implement either VSHPROPID_IconMonikerImageList (if the original project used VSHPROPID_IconImgList) or VSHPROPID_IconMonikerGuid, VSHPROPID_IconMonikerId, VSHPROPID_OpenFolderIconMonikerGuid, VSHPROPID_OpenFolderIconMonikerId (if the original project used VSHPROPID_IconHandle and VSHPROPID_OpenFolderIconHandle).

  3. Change the implementation of the original VSHPROPIDs for icons to create "legacy" versions of the icons if extension points request them. IVsImageService2 provides functionality necessary to get those icons

    Extra requirements for VB/C# project flavors

    Only implement VSHPROPID_SupportsIconMonikers if you detect that your project is the outermost flavor. Otherwise, the actual outermost flavor may not support image monikers in reality, and your base flavor might effectively "hide" customized images.

    How do I use image monikers in CPS?

    Setting custom images in CPS (Common Project System) can be done manually or via an item template that comes with the Project System Extensibility SDK.

    Using the Project System Extensibility SDK

    Follow the instructions at Provide custom icons for the Project Type/Item type to customize your CPS images. More information about CPS can be found at Visual Studio Project System extensibility documentation

    Manually use ImageMonikers

  4. Implement and export the IProjectTreeModifier interface in your project system.

  5. Determine which KnownMoniker or custom image moniker you want to use.

  6. In the ApplyModifications method, do the following somewhere in the method before returning the new tree, similar to the below example:

    // Replace this KnownMoniker with your desired ImageMoniker
    tree = tree.SetIcon(KnownMonikers.Blank.ToProjectSystemType());
    
  7. If you are creating a new tree, you can set the custom images by passing in the desired monikers into the NewTree method, similar to the below example:

    // Replace this KnownMoniker with your desired ImageMoniker
    ProjectImageMoniker icon         = KnownMonikers.FolderClosed.ToProjectSystemType();
    ProjectImageMoniker expandedIcon = KnownMonikers.FolderOpened.ToProjectSystemType();
    
    return this.ProjectTreeFactory.Value.NewTree(/*caption*/<value>,
                                                 /*filePath*/<value>,
                                                 /*browseObjectProperties*/<value>,
                                                 icon,
                                                 expandedIcon);
    

How do I convert from a real image strip to a moniker-based image strip?

I need to support HIMAGELISTs

If there is an already existing image strip for your code that you want to update to use the image service, but you are constrained by APIs that require passing around image lists, you can still get the benefits of the image service. To create a moniker-based image strip, follow the steps below to create a manifest from existing monikers.

  1. Run the ManifestFromResources tool, passing it the image strip. This will generate a manifest for the strip.

    • Recommended: provide a non default name for the manifest to suit its usage.
  2. If you are using only KnownMonikers, then do the following:

    • Replace the <Images> section of the manifest with <Images/>.

    • Remove all the subimage IDs (anything with <imagestrip name>_##).

    • Recommended: rename the AssetsGuid symbol and image strip symbol to suit its usage.

    • Replace each ContainedImage's GUID with $(ImageCatalogGuid), replace each ContainedImage's ID with $(<moniker>), and add the External="true" attribute to each ContainedImage

      • <moniker> should be replaced with the KnownMoniker that matches the image but with the "KnownMonikers." removed from the name.
    • Add <Import Manifest="$(ManifestFolder)\<Relative install dir path to *>\Microsoft.VisualStudio.ImageCatalog.imagemanifest" /*> to the top of the <Symbols> section.

      • The relative path is determined by the deployment location defined in the setup authoring for the manifest.
  3. Run the ManifestToCode tool to generate wrappers so that the existing code has a moniker it can use to query the image service for the image strip.

    • Recommended: provide nondefault names for the wrappers and namespaces to suit their usage.
  4. Do all the adds, setup authoring/deployment, and other code changes to work with the image service and the new files.

    Sample manifest including both internal and external images to see what it should look like:

<?xml version="1.0"?>
<ImageManifest
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://schemas.microsoft.com/VisualStudio/ImageManifestSchema/2014">

  <Symbols>
    <!-- This needs to be the relative path from your manifest to the ImageCatalog's manifest
         where $(ManifestFolder) is the deployed location of this manifest. -->
    <Import Manifest="$(ManifestFolder)\<RelPath>\Microsoft.VisualStudio.ImageCatalog.imagemanifest" />

    <String Name="Resources" Value="/My.Assembly.Name;Component/Resources/ImageStrip" />
    <Guid Name="ImageGuid" Value="{fb41b7ef-6587-480c-aa27-5b559d42cfc9}" />
    <Guid Name="ImageStripGuid" Value="{9c84a570-d9a7-4052-a340-188fb276f973}" />
    <ID Name="MyImage_0" Value="100" />
    <ID Name="MyImage_1" Value="101" />
    <ID Name="InternalList" Value="1001" />
    <ID Name="ExternalList" Value="1002" />
  </Symbols>

  <Images>
    <Image Guid="$(ImageGuid)" ID="$(MyImage_0)">
      <Source Uri="$(Resources)/MyImage_0.png">
        <Size Value="16" />
      </Source>
    </Image>
    <Image Guid="$(ImageGuid)" ID="$(MyImage_1)">
      <Source Uri="$(Resources)/MyImage_1.png">
        <Size Value="16" />
      </Source>
    </Image>
  </Images>

  <ImageLists>
    <ImageList Guid="$(ImageStripGuid)" ID="$(InternalList)">
      <ContainedImage Guid="$(ImageGuid)" ID="$(MyImage_0)" />
      <ContainedImage Guid="$(ImageGuid)" ID="$(MyImage_1)" />
    </ImageList>
    <ImageList Guid="$(ImageStripGuid)" ID="$(ExternalList)">
      <ContainedImage Guid="$(ImageCatalogGuid)" ID="$(StatusError)" External="true" />
      <ContainedImage Guid="$(ImageCatalogGuid)" ID="$(StatusWarning)" External="true" />
      <ContainedImage Guid="$(ImageCatalogGuid)" ID="$(StatusInformation)" External="true" />
    </ImageList>
  </ImageLists>

</ImageManifest>

I don't need to support HIMAGELISTs

  1. Determine the set of KnownMonikers that match the images in your image strip, or create your own monikers for the images in your image strip.

  2. Update whatever mapping you used to get the image at the required index in the image strip to use the monikers instead.

  3. Update your code to use the image service to request monikers via the updated mapping. (This might mean updating to CrispImages for managed code, or requesting HBITMAPs or HICONs from the image service and passing them around for native code.)

Testing your images

You can use the Image Library Viewer tool to test your image manifests to make sure everything is authored correctly. You can find the tool in the Visual Studio 2015 SDK. Documentation for this tool and others can be found here.

Additional resources

Samples

Several of the Visual Studio samples on GitHub have been updated to show how to use the image service as part of various Visual Studio extensibility points.

Check http://github.com/Microsoft/VSSDK-Extensibility-Samples for the latest samples.

Tooling

A set of support tools for the Image Service was created to aid in creating/updating UI that works with the Image Service. For more information about each tool, check the documentation that comes with the tools. The tools are included as part of the Visual Studio 2015 SDK.

ManifestFromResources

The Manifest from Resources tool takes a list of image resources (PNG or XAML) and generates an image manifest file for using those images with the image service.

ManifestToCode

The Manifest to Code tool takes an image manifest file and generates a wrapper file for referencing the manifest values in code (C++, C#, or VB) or .vsct files.

ImageLibraryViewer

The Image Library Viewer tool can load image manifests and allows the user to manipulate them in the same way Visual Studio would to make sure the manifest is authored correctly. The user can alter background, sizes, DPI setting, High Contrast, and other settings. It also displays loading information to find errors in the manifests and displays source information for each image in the manifest.

FAQ

  • Are there any dependencies that you must include when loading <Reference Include="Microsoft.VisualStudio.*.Interop.14.0.DesignTime" />?

    • Set EmbedInteropTypes="true" on all interop DLLs.
  • How do I deploy an image manifest with my extension?

    • Add the .imagemanifest file to your project.

    • Set "Include in VSIX" to True.

  • I am updating my CPS Project System. What happened to ImageName and StockIconService?

    • These were removed when CPS was updated to use monikers. You no longer need to call the StockIconService, just pass the desired KnownMoniker to the method or property using the ToProjectSystemType() extension method in the CPS utilities. You can find a mapping from ImageName to KnownMonikers below:

      ImageName KnownMoniker
      ImageName.OfflineWebApp KnownImageIds.Web
      ImageName.WebReferencesFolder KnownImageIds.Web
      ImageName.OpenReferenceFolder KnownImageIds.FolderOpened
      ImageName.ReferenceFolder KnownImageIds.Reference
      ImageName.Reference KnownImageIds.Reference
      ImageName.SdlWebReference KnownImageIds.WebReferenceFolder
      ImageName.DiscoWebReference KnownImageIds.DynamicDiscoveryDocument
      ImageName.Folder KnownImageIds.FolderClosed
      ImageName.OpenFolder KnownImageIds.FolderOpened
      ImageName.ExcludedFolder KnownImageIds.HiddenFolderClosed
      ImageName.OpenExcludedFolder KnownImageIds.HiddenFolderOpened
      ImageName.ExcludedFile KnownImageIds.HiddenFile
      ImageName.DependentFile KnownImageIds.GenerateFile
      ImageName.MissingFile KnownImageIds.DocumentWarning
      ImageName.WindowsForm KnownImageIds.WindowsForm
      ImageName.WindowsUserControl KnownImageIds.UserControl
      ImageName.WindowsComponent KnownImageIds.ComponentFile
      ImageName.XmlSchema KnownImageIds.XMLSchema
      ImageName.XmlFile KnownImageIds.XMLFile
      ImageName.WebForm KnownImageIds.Web
      ImageName.WebService KnownImageIds.WebService
      ImageName.WebUserControl KnownImageIds.WebUserControl
      ImageName.WebCustomUserControl KnownImageIds.WebCustomControl
      ImageName.AspPage KnownImageIds.ASPFile
      ImageName.GlobalApplicationClass KnownImageIds.SettingsFile
      ImageName.WebConfig KnownImageIds.ConfigurationFile
      ImageName.HtmlPage KnownImageIds.HTMLFile
      ImageName.StyleSheet KnownImageIds.StyleSheet
      ImageName.ScriptFile KnownImageIds.JSScript
      ImageName.TextFile KnownImageIds.Document
      ImageName.SettingsFile KnownImageIds.Settings
      ImageName.Resources KnownImageIds.DocumentGroup
      ImageName.Bitmap KnownImageIds.Image
      ImageName.Icon KnownImageIds.IconFile
      ImageName.Image KnownImageIds.Image
      ImageName.ImageMap KnownImageIds.ImageMapFile
      ImageName.XWorld KnownImageIds.XWorldFile
      ImageName.Audio KnownImageIds.Sound
      ImageName.Video KnownImageIds.Media
      ImageName.Cab KnownImageIds.CABProject
      ImageName.Jar KnownImageIds.JARFile
      ImageName.DataEnvironment KnownImageIds.DataTable
      ImageName.PreviewFile KnownImageIds.Report
      ImageName.DanglingReference KnownImageIds.ReferenceWarning
      ImageName.XsltFile KnownImageIds.XSLTransform
      ImageName.Cursor KnownImageIds.CursorFile
      ImageName.AppDesignerFolder KnownImageIds.Property
      ImageName.Data KnownImageIds.Database
      ImageName.Application KnownImageIds.Application
      ImageName.DataSet KnownImageIds.DatabaseGroup
      ImageName.Pfx KnownImageIds.Certificate
      ImageName.Snk KnownImageIds.Rule
      ImageName.VisualBasicProject KnownImageIds.VBProjectNode
      ImageName.CSharpProject KnownImageIds.CSProjectNode
      ImageName.Empty KnownImageIds.Blank
      ImageName.MissingFolder KnownImageIds.FolderOffline
      ImageName.SharedImportReference KnownImageIds.SharedProject
      ImageName.SharedProjectCs KnownImageIds.CSSharedProject
      ImageName.SharedProjectVc KnownImageIds.CPPSharedProject
      ImageName.SharedProjectJs KnownImageIds.JSSharedProject
      ImageName.CSharpCodeFile KnownImageIds.CSFileNode
      ImageName.VisualBasicCodeFile KnownImageIds.VBFileNode
    • I am updating my completion list provider. What KnownMonikers match to the old StandardGlyphGroup and StandardGlyph values?

      GlyphGroupClass GlyphItemPublic ClassPublic
      GlyphGroupClass GlyphItemInternal ClassInternal
      GlyphGroupClass GlyphItemFriend ClassInternal
      GlyphGroupClass GlyphItemProtected ClassProtected
      GlyphGroupClass GlyphItemPrivate ClassPrivate
      GlyphGroupClass GlyphItemShortcut ClassShortcut
      GlyphGroupConstant GlyphItemPublic ConstantPublic
      GlyphGroupConstant GlyphItemInternal ConstantInternal
      GlyphGroupConstant GlyphItemFriend ConstantInternal
      GlyphGroupConstant GlyphItemProtected ConstantProtected
      GlyphGroupConstant GlyphItemPrivate ConstantPrivate
      GlyphGroupConstant GlyphItemShortcut ConstantShortcut
      GlyphGroupDelegate GlyphItemPublic DelegatePublic
      GlyphGroupDelegate GlyphItemInternal DelegateInternal
      GlyphGroupDelegate GlyphItemFriend DelegateInternal
      GlyphGroupDelegate GlyphItemProtected DelegateProtected
      GlyphGroupDelegate GlyphItemPrivate DelegatePrivate
      GlyphGroupDelegate GlyphItemShortcut DelegateShortcut
      GlyphGroupEnum GlyphItemPublic EnumerationPublic
      GlyphGroupEnum GlyphItemInternal EnumerationInternal
      GlyphGroupEnum GlyphItemFriend EnumerationInternal
      GlyphGroupEnum GlyphItemProtected EnumerationProtected
      GlyphGroupEnum GlyphItemPrivate EnumerationPrivate
      GlyphGroupEnum GlyphItemShortcut EnumerationShortcut
      GlyphGroupEnumMember GlyphItemPublic EnumerationItemPublic
      GlyphGroupEnumMember GlyphItemInternal EnumerationItemInternal
      GlyphGroupEnumMember GlyphItemFriend EnumerationItemInternal
      GlyphGroupEnumMember GlyphItemProtected EnumerationItemProtected
      GlyphGroupEnumMember GlyphItemPrivate EnumerationItemPrivate
      GlyphGroupEnumMember GlyphItemShortcut EnumerationItemShortcut
      GlyphGroupEvent GlyphItemPublic EventPublic
      GlyphGroupEvent GlyphItemInternal EventInternal
      GlyphGroupEvent GlyphItemFriend EventInternal
      GlyphGroupEvent GlyphItemProtected EventProtected
      GlyphGroupEvent GlyphItemPrivate EventPrivate
      GlyphGroupEvent GlyphItemShortcut EventShortcut
      GlyphGroupException GlyphItemPublic ExceptionPublic
      GlyphGroupException GlyphItemInternal ExceptionInternal
      GlyphGroupException GlyphItemFriend ExceptionInternal
      GlyphGroupException GlyphItemProtected ExceptionProtected
      GlyphGroupException GlyphItemPrivate ExceptionPrivate
      GlyphGroupException GlyphItemShortcut ExceptionShortcut
      GlyphGroupField GlyphItemPublic FieldPublic
      GlyphGroupField GlyphItemInternal FieldInternal
      GlyphGroupField GlyphItemFriend FieldInternal
      GlyphGroupField GlyphItemProtected FieldProtected
      GlyphGroupField GlyphItemPrivate FieldPrivate
      GlyphGroupField GlyphItemShortcut FieldShortcut
      GlyphGroupInterface GlyphItemPublic InterfacePublic
      GlyphGroupInterface GlyphItemInternal InterfaceInternal
      GlyphGroupInterface GlyphItemFriend InterfaceInternal
      GlyphGroupInterface GlyphItemProtected InterfaceProtected
      GlyphGroupInterface GlyphItemPrivate InterfacePrivate
      GlyphGroupInterface GlyphItemShortcut InterfaceShortcut
      GlyphGroupMacro GlyphItemPublic MacroPublic
      GlyphGroupMacro GlyphItemInternal MacroInternal
      GlyphGroupMacro GlyphItemFriend MacroInternal
      GlyphGroupMacro GlyphItemProtected MacroProtected
      GlyphGroupMacro GlyphItemPrivate MacroPrivate
      GlyphGroupMacro GlyphItemShortcut MacroShortcut
      GlyphGroupMap GlyphItemPublic MapPublic
      GlyphGroupMap GlyphItemInternal MapInternal
      GlyphGroupMap GlyphItemFriend MapInternal
      GlyphGroupMap GlyphItemProtected MapProtected
      GlyphGroupMap GlyphItemPrivate MapPrivate
      GlyphGroupMap GlyphItemShortcut MapShortcut
      GlyphGroupMapItem GlyphItemPublic MapItemPublic
      GlyphGroupMapItem GlyphItemInternal MapItemInternal
      GlyphGroupMapItem GlyphItemFriend MapItemInternal
      GlyphGroupMapItem GlyphItemProtected MapItemProtected
      GlyphGroupMapItem GlyphItemPrivate MapItemPrivate
      GlyphGroupMapItem GlyphItemShortcut MapItemShortcut
      GlyphGroupMethod GlyphItemPublic MethodPublic
      GlyphGroupMethod GlyphItemInternal MethodInternal
      GlyphGroupMethod GlyphItemFriend MethodInternal
      GlyphGroupMethod GlyphItemProtected MethodProtected
      GlyphGroupMethod GlyphItemPrivate MethodPrivate
      GlyphGroupMethod GlyphItemShortcut MethodShortcut
      GlyphGroupOverload GlyphItemPublic MethodPublic
      GlyphGroupOverload GlyphItemInternal MethodInternal
      GlyphGroupOverload GlyphItemFriend MethodInternal
      GlyphGroupOverload GlyphItemProtected MethodProtected
      GlyphGroupOverload GlyphItemPrivate MethodPrivate
      GlyphGroupOverload GlyphItemShortcut MethodShortcut
      GlyphGroupModule GlyphItemPublic ModulePublic
      GlyphGroupModule GlyphItemInternal ModuleInternal
      GlyphGroupModule GlyphItemFriend ModuleInternal
      GlyphGroupModule GlyphItemProtected ModuleProtected
      GlyphGroupModule GlyphItemPrivate ModulePrivate
      GlyphGroupModule GlyphItemShortcut ModuleShortcut
      GlyphGroupNamespace GlyphItemPublic NamespacePublic
      GlyphGroupNamespace GlyphItemInternal NamespaceInternal
      GlyphGroupNamespace GlyphItemFriend NamespaceInternal
      GlyphGroupNamespace GlyphItemProtected NamespaceProtected
      GlyphGroupNamespace GlyphItemPrivate NamespacePrivate
      GlyphGroupNamespace GlyphItemShortcut NamespaceShortcut
      GlyphGroupOperator GlyphItemPublic OperatorPublic
      GlyphGroupOperator GlyphItemInternal OperatorInternal
      GlyphGroupOperator GlyphItemFriend OperatorInternal
      GlyphGroupOperator GlyphItemProtected OperatorProtected
      GlyphGroupOperator GlyphItemPrivate OperatorPrivate
      GlyphGroupOperator GlyphItemShortcut OperatorShortcut
      GlyphGroupProperty GlyphItemPublic PropertyPublic
      GlyphGroupProperty GlyphItemInternal PropertyInternal
      GlyphGroupProperty GlyphItemFriend PropertyInternal
      GlyphGroupProperty GlyphItemProtected PropertyProtected
      GlyphGroupProperty GlyphItemPrivate PropertyPrivate
      GlyphGroupProperty GlyphItemShortcut PropertyShortcut
      GlyphGroupStruct GlyphItemPublic StructurePublic
      GlyphGroupStruct GlyphItemInternal StructureInternal
      GlyphGroupStruct GlyphItemFriend StructureInternal
      GlyphGroupStruct GlyphItemProtected StructureProtected
      GlyphGroupStruct GlyphItemPrivate StructurePrivate
      GlyphGroupStruct GlyphItemShortcut StructureShortcut
      GlyphGroupTemplate GlyphItemPublic TemplatePublic
      GlyphGroupTemplate GlyphItemInternal TemplateInternal
      GlyphGroupTemplate GlyphItemFriend TemplateInternal
      GlyphGroupTemplate GlyphItemProtected TemplateProtected
      GlyphGroupTemplate GlyphItemPrivate TemplatePrivate
      GlyphGroupTemplate GlyphItemShortcut TemplateShortcut
      GlyphGroupTypedef GlyphItemPublic TypeDefinitionPublic
      GlyphGroupTypedef GlyphItemInternal TypeDefinitionInternal
      GlyphGroupTypedef GlyphItemFriend TypeDefinitionInternal
      GlyphGroupTypedef GlyphItemProtected TypeDefinitionProtected
      GlyphGroupTypedef GlyphItemPrivate TypeDefinitionPrivate
      GlyphGroupTypedef GlyphItemShortcut TypeDefinitionShortcut
      GlyphGroupType GlyphItemPublic TypePublic
      GlyphGroupType GlyphItemInternal TypeInternal
      GlyphGroupType GlyphItemFriend TypeInternal
      GlyphGroupType GlyphItemProtected TypeProtected
      GlyphGroupType GlyphItemPrivate TypePrivate
      GlyphGroupType GlyphItemShortcut TypeShortcut
      GlyphGroupUnion GlyphItemPublic UnionPublic
      GlyphGroupUnion GlyphItemInternal UnionInternal
      GlyphGroupUnion GlyphItemFriend UnionInternal
      GlyphGroupUnion GlyphItemProtected UnionProtected
      GlyphGroupUnion GlyphItemPrivate UnionPrivate
      GlyphGroupUnion GlyphItemShortcut UnionShortcut
      GlyphGroupVariable GlyphItemPublic FieldPublic
      GlyphGroupVariable GlyphItemInternal FieldInternal
      GlyphGroupVariable GlyphItemFriend FieldInternal
      GlyphGroupVariable GlyphItemProtected FieldProtected
      GlyphGroupVariable GlyphItemPrivate FieldPrivate
      GlyphGroupVariable GlyphItemShortcut FieldShortcut
      GlyphGroupValueType GlyphItemPublic ValueTypePublic
      GlyphGroupValueType GlyphItemInternal ValueTypeInternal
      GlyphGroupValueType GlyphItemFriend ValueTypeInternal
      GlyphGroupValueType GlyphItemProtected ValueTypeProtected
      GlyphGroupValueType GlyphItemPrivate ValueTypePrivate
      GlyphGroupValueType GlyphItemShortcut ValueTypeShortcut
      GlyphGroupIntrinsic GlyphItemPublic ObjectPublic
      GlyphGroupIntrinsic GlyphItemInternal ObjectInternal
      GlyphGroupIntrinsic GlyphItemFriend ObjectInternal
      GlyphGroupIntrinsic GlyphItemProtected ObjectProtected
      GlyphGroupIntrinsic GlyphItemPrivate ObjectPrivate
      GlyphGroupIntrinsic GlyphItemShortcut ObjectShortcut
      GlyphGroupJSharpMethod GlyphItemPublic MethodPublic
      GlyphGroupJSharpMethod GlyphItemInternal MethodInternal
      GlyphGroupJSharpMethod GlyphItemFriend MethodInternal
      GlyphGroupJSharpMethod GlyphItemProtected MethodProtected
      GlyphGroupJSharpMethod GlyphItemPrivate MethodPrivate
      GlyphGroupJSharpMethod GlyphItemShortcut MethodShortcut
      GlyphGroupJSharpField GlyphItemPublic FieldPublic
      GlyphGroupJSharpField GlyphItemInternal FieldInternal
      GlyphGroupJSharpField GlyphItemFriend FieldInternal
      GlyphGroupJSharpField GlyphItemProtected FieldProtected
      GlyphGroupJSharpField GlyphItemPrivate FieldPrivate
      GlyphGroupJSharpField GlyphItemShortcut FieldShortcut
      GlyphGroupJSharpClass GlyphItemPublic ClassPublic
      GlyphGroupJSharpClass GlyphItemInternal ClassInternal
      GlyphGroupJSharpClass GlyphItemFriend ClassInternal
      GlyphGroupJSharpClass GlyphItemProtected ClassProtected
      GlyphGroupJSharpClass GlyphItemPrivate ClassPrivate
      GlyphGroupJSharpClass GlyphItemShortcut ClassShortcut
      GlyphGroupJSharpNamespace GlyphItemPublic NamespacePublic
      GlyphGroupJSharpNamespace GlyphItemInternal NamespaceInternal
      GlyphGroupJSharpNamespace GlyphItemFriend NamespaceInternal
      GlyphGroupJSharpNamespace GlyphItemProtected NamespaceProtected
      GlyphGroupJSharpNamespace GlyphItemPrivate NamespacePrivate
      GlyphGroupJSharpNamespace GlyphItemShortcut NamespaceShortcut
      GlyphGroupJSharpInterface GlyphItemPublic InterfacePublic
      GlyphGroupJSharpInterface GlyphItemInternal InterfaceInternal
      GlyphGroupJSharpInterface GlyphItemFriend InterfaceInternal
      GlyphGroupJSharpInterface GlyphItemProtected InterfaceProtected
      GlyphGroupJSharpInterface GlyphItemPrivate InterfacePrivate
      GlyphGroupJSharpInterface GlyphItemShortcut InterfaceShortcut
      GlyphGroupError StatusError
      GlyphBscFile ClassFile
      GlyphAssembly Reference
      GlyphLibrary Library
      GlyphVBProject VBProjectNode
      GlyphCoolProject CSProjectNode
      GlyphCppProject CPPProjectNode
      GlyphDialogId Dialog
      GlyphOpenFolder FolderOpened
      GlyphClosedFolder FolderClosed
      GlyphArrow GoToNext
      GlyphCSharpFile CSFileNode
      GlyphCSharpExpansion Snippet
      GlyphKeyword IntellisenseKeyword
      GlyphInformation StatusInformation
      GlyphReference ClassMethodReference
      GlyphRecursion Recursion
      GlyphXmlItem Tag
      GlyphJSharpProject DocumentCollection
      GlyphJSharpDocument Document
      GlyphForwardType GoToNext
      GlyphCallersGraph CallTo
      GlyphCallGraph CallFrom
      GlyphWarning StatusWarning
      GlyphMaybeReference QuestionMark
      GlyphMaybeCaller CallTo
      GlyphMaybeCall CallFrom
      GlyphExtensionMethod ExtensionMethod
      GlyphExtensionMethodInternal ExtensionMethod
      GlyphExtensionMethodFriend ExtensionMethod
      GlyphExtensionMethodProtected ExtensionMethod
      GlyphExtensionMethodPrivate ExtensionMethod
      GlyphExtensionMethodShortcut ExtensionMethod
      GlyphXmlAttribute XmlAttribute
      GlyphXmlChild XmlElement
      GlyphXmlDescendant XmlDescendant
      GlyphXmlNamespace XmlNamespace
      GlyphXmlAttributeQuestion XmlAttributeLowConfidence
      GlyphXmlAttributeCheck XmlAttributeHighConfidence
      GlyphXmlChildQuestion XmlElementLowConfidence
      GlyphXmlChildCheck XmlElementHighConfidence
      GlyphXmlDescendantQuestion XmlDescendantLowConfidence
      GlyphXmlDescendantCheck XmlDescendantHighConfidence
      GlyphCompletionWarning IntellisenseWarning