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:
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 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" />
<!-- If your assembly is strongly named, you'll need the version and public key token as well -->
<!-- <String Name="AssemblyName" Value="Microsoft.VisualStudio.Shell.UI.Internal;v17.0.0.0;b03f5f7f11d50a3a" /> -->
</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.
VsDpiAwareness.h
- Required if you use the DPI awareness helpers to get the current DPI.
How do I write new WPF UI?
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.)
Select the desired image and get its moniker. Use a KnownMoniker, or use your own if you have your own custom images and monikers.
Add CrispImages to your XAML. (See below example.)
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.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Utilities"
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:
Replace all <Image> elements in your UI with <CrispImage> elements.
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.
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
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
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:
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;
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>
Ensure the following also exists at the top of the file, after the
<Extern>
elements:<Include href="KnownImageIds.vsct"/>
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:
The tool window tab when the tabs get small enough (also used in the Ctrl+Tab window switcher).
Remove these lines (if they exist) in the constructor for the class that derives from the ToolWindowPane type:
this.BitmapResourceID = <Value>; this.BitmapIndex = <Value>;
See step #1 of the "How do I use image monikers in a new tool window?" section above.
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
Implement VSHPROPID_SupportsIconMonikers on the project's IVsHierarchy, and return true.
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).
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
Implement and export the IProjectTreeModifier interface in your project system.
Determine which KnownMoniker or custom image moniker you want to use.
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());
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.
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.
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.
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.
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
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.
Update whatever mapping you used to get the image at the required index in the image strip to use the monikers instead.
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.
My images are still not working, how do I figure out what's wrong?
Visual Studio may not be finding your image manifest. For performance reasons Visual Studio limits folder search depth, so it's recommended that the image manifest be kept in the root folder of your extension.
You might be missing assembly information in your image manifest file. Assemblies that are strongly named require additional information in order to be loaded by Visual Studio. In order to load a strongly named assembly, you need to include (in addition to the assembly name) the assembly version and public key token in the resource URIs for the images in your image manifest.
<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> <String Name="Resources" Value="/Microsoft.VisualStudio.Shell.UI.Internal;v17.0.0.0;b03f5f7f11d50a3a;Component/Resources" /> ... </Symbols> ... </ImageManifest>
You may be missing a codebase entry for your image assembly. If your assembly is not yet loaded by the time Visual Studio needs it, it will need to know where to find your assembly in order to load it. To add a codebase for your assembly, you can use the ProvideCodeBaseAttribute to ensure a codebase entry is generated and included in your extension's pkgdef.
[assembly: ProvideCodeBase(AssemblyName = "ClassLibrary1", Version = "1.0.0.0", CodeBase = "$PackageFolder$\\ClassLibrary1.dll")]
If the previous options do no resolve your image load issue, you can enable logging by dropping the following entries into a pkgdef in your extension:
[$RootKey$\ImageLibrary] "TraceLevel"="Verbose" "TraceFilename"="ImageLibrary.log"
This will create a log file called ImageLibrary.log in your %UserProfile% folder. Make sure to run "devenv /updateConfiguration" from a developer command prompt after adding these entries to a pkgdef. This ensures that the logging entries are enabled and that VS refreshes the image manifest cache to help find any errors that may occur when reading your image manifest. If you then run through the scenario where your image is expected to load, your log file will contain both the registration logging and request logging for your image.
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?
Name Name Name 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