Hi, I’m Ryan Molden, a developer on the Shell team. Due to popular feedback from users I have been reassigned to a top-secret UI project. I can’t say much about it, just give you a quick sneak peek.
No wait, that’s not right, I mean I set aside some of my free time and wrote an extension for Visual Studio 2010 that allows users to change the images on the command UI as many customers had complained of the pain of not being able to do this in the RTM version of the product. The extension is freely available on the Visual Studio Gallery and has the following awesome features:
- Allows users to change the image associated with any piece of command UI (toolbar buttons & menu items) in Visual Studio.
- Contains all images that are used in the Ultimate edition of Visual Studio indexed with search text to make locating an icon easier.
- Allows copy/paste (keyboard and context menu) as well as drag & drop within the tool window to change the images on command UI.
- Allows pasting from the Windows Clipboard and automatically performs low color conversion (Near Green and Magenta are mapped to Transparent) and size conversion (to 16×16).
- Allows users to load an image off their disk (supported formats: jpg, png, gif, tiff, bmp) and use it in their Visual Studio command UI.
The interaction model with the extension is slightly different than command image customization in Visual Studio 2008 since I couldn’t change already shipped assemblies in an out of band extension. This article aims to be a walk-through of the extension and how it works.
First off, you can get the extension here.
After installing the extension there is a new item on the Tools menu with the text Customize Command Images (as shown below)
When you invoke the item it brings up a tool window that looks like this
The UI Explained
Since I wrote this extension in my free time, and I am not a UX designer, apologies if you consider the UI to be terrible. All flaming arrows can be directed at me and not my teammates. That apology out of the way, let’s dig into what is here and what you can do with it. To start, let’s think of a scenario where you would WANT to change command UI. The most common scenario would be that you have created a custom toolbar and filled it with macros you wrote and you want to make it go from this
Since I couldn’t change already shipped assemblies I couldn’t support drag & drop onto the existing UI nor could I integrate the image swapping with the existing customization dialog. To that end we have a tool window with two distinct areas. At the top is the hierarchical command view area, and at the bottom is the available images area.
The Hierarchical Command View Area
The area outlined in red in the picture below is the hierarchical view of the command UI in Visual Studio.
This consists of a few main parts; I will tackle each one in turn. First up is the search text box. In this area you can type text, and the tree view will be filtered down to only show nodes which contain the typed text. One caveat to share, the hierarchy is built up by recursively enumerating the DTE models for all menus/toolbars/context menus in Visual Studio. Since there are a very large number of them (there are over 11,000 ‘buttons’ in the Visual Studio command UI) this is done as lazily and asynchronously as possible. This means that before the hierarchy nodes are expanded their children have not been calculated and search can’t really match against anything, since there is nothing to match against. If you type in the textbox before the hierarchy has been built, the UI will show a warning icon in place of the search icon. This will look like the picture below (imagine your cursor was hovering over the warning icon, which is where the tooltip is coming from)
This is just warning you that any filtering that is done may be incorrect since the entire hierarchy has not been constructed. You are of course safe to ignore this if the filtering results in what you want. If it doesn’t there are two approaches. One approach is to click the button highlighted below
which will force an asynchronous build-out of the entire command hierarchy. This can take a while and use up a decent amount of memory as it constructs in memory-models for all the command UI that exists in all of Visual Studio. I wouldn’t recommend doing this, but if you know that some command exists that you want to associate an image with, but you don’t remember the exact location it exists in this can be helpful. The second way you can get the filtering to see the nodes you want to modify is to expand the nodes that lead to the command(s) you are interested in.
You may be asking “why would I need to filter if I already know where the command is and have to expand nodes leading up to it anyways?” Good question, astute reader! One possible scenario is that you want to have a less cluttered area to prepare for the image switching or perhaps you know the command exists in a couple of locations and you want to change the image in each location. An example of the second scenario can be shown below
Another place this is useful is in culling down the list of context menus. Since there are many context menus in Visual Studio expanding that node and finding one of interest among the children can be an adventure. One way to make that a bit easier is to expand the Context Menus node in the tree view then enter text into the search box that would narrow it down to the context menu you are interested in. An example of that is shown below.
Now that I have explained the search textbox and the Force Build Hierarchy button, I guess the next obvious bit is the tree view itself. As one can see the tree view simply shows the hierarchical arrangement of command UI in Visual Studio under some ‘grouping’ nodes (MenuBar/ToolBars/Context Menus). The MenuBar node is the main menu in Visual Studio. The Toolbars node contains all non-tool window toolbars in Visual Studio and the Context Menus node contains all Context Menus in Visual Studio. The nodes in this tree view serve as the ‘targets’ of the image replacement actions. The only nodes that will accept image replacement are leaf nodes (the ones representing individual commands). Each leaf node has a context menu that looks like so
This is a standard Visual Studio context menu and supports the Copy and Paste commands. Copy will be enabled anytime the node in question already has an image associated with it. If invoked it will copy the image into an internal storage location that can be used for subsequent Paste operations. Copy does not place the image in the Windows Clipboard due to concerns about icon licensing, ownership and potential reuse outside Visual Studio. The Paste command will replace the image that is associated with the target node (if any) with the one from the internal storage location. The Paste command will be enabled in the following three scenarios
- You have copied an image from a leaf node in the tree view via the Copy command.
- You have copied an image from the image list in the lower half of the tool window (discussed later) via the Copy command
- You have data in the Windows Clipboard that WPF recognizes as being image data. NOTE: When pasting from external programs we will auto-convert the image size 16×16 and mask Near-Green (RGB: 00FE00) and Magenta (RGB: FF00FF) colors to transparent since the Windows Clipboard has very spotty support for transparency information and previous versions of Visual Studio have treated those colors as being transparent.
The context menu is accessible in the ‘usual ways’ (i.e. right click, context menu key, shift-F10). Also note that the regular keyboard shortcuts for Copy/Paste should work as well, so there is no need to bring up the context menu if you don’t want to.
The only thing about the hierarchy view area that is left to explain is the Refresh Button and the ‘Show only visible items’ checkbox. Since the Refresh Button is only enabled and only makes sense when the ‘Show only visible items is checked’ I will explain it (and why it is needed) after explaining the checkbox.
The checkbox controls whether the tree view will show all items under a specific container (toolbar/menu/context menu) or just the ones currently visible in the UI. By default it is checked meaning we will only show command UI that is currently visible in the Visual Studio UI. One exception is for the Context Menus node. Since it would be impossible for a Visual Studio context menu to actually be visible while you are interacting with the tool window we obviously won’t hide them as they would then never be available for customization. The Refresh button deals with the fact that there is no DTE event (or other type of event) that would alert us when the visibility of a piece of command UI has changed. This means if you say load a project and new menus become visible, or you show a toolbar that wasn’t previously shown, the tree view can’t update automatically as there is no way for it to be notified this has happened. The ‘hacky fix’ is the Refresh button which will cause the tree view to re-process the controls visibility state which will show/hide ones based on the most up to date information.
The Images View Area
The area outlined in red below is the view of images available for use in the command UI in Visual Studio.
This list is pulled in from an on disk cache that ships with the extension. This is done because enumerating 11,000 buttons in Visual Studio using DTE and tracking unique images found can take some time and use some memory. Since this collection of images is fairly static I chose to do it on my development machine and cache the results into an on disk file for quick startup / usability. I will talk more about the caching below.
The area has a search/filter textbox like the hierarchal command view area that is usable right away to filter the image list. You may wonder “Filter on what?” The answer is that we associate the button text of all buttons we find a given image on with that image and use that as searchable text. Since some images are used in multiple places some images have multiple, distinct search strings. The search box will cause the image list to be filtered down to include only images whose search text contains the text from the search box. This can be helpful in finding images that might work well for buttons that you perhaps created yourself (say that are associated with a macro). For instance assume you have a macro that sets your office on fire so you can leave early on a Friday, because after all, who among us hasn’t written such a macro? [Note to management: I haven’t] Well a great icon to help remember what that macro does would be some kind of fire. So you could do something like what is shown below
You’ll notice there is also a button that looks like the ‘Force Build Hierarchy’ button in the hierarchical view area. This is the ‘Rebuild Image Cache’ command, and it is shown below
This will rebuild the on-disk cache using your local install. This can be useful if there are packages or addins you have installed locally which have images you may want to have available for re-use on your own command UI (or just replacing stock Visual Studio images for commands). This is done asynchronously and takes a little while (again, the processing of 11k buttons). Progress will be shown at the bottom of the tool window and you are free to go about doing other things while this is happening. When it is done the result will be saved to disk so on next launch any images not in the original cache will still be present in your image window.
The button next to the ‘Rebuild Image Cache’ button is the ‘Add Image(s) From Disk’ command and is shown below
This command opens a dialog that lets you choose one (or many) images from your local machine to add to the existing image cache. The button launches the standard Open File dialog and when the images are chosen it shows a dialog like this (well, except the images will of course be different)
It shows each image you have selected, resized to 16×16 and also color converted (Near-Green and Magenta –> Transparent, as talked about in the hierarchal command view area section above). The textbox next to each image allows you to associate search text with the image and defaults to the image’s file name without extension.
The ‘Save Added Images in Cache’ checkbox determines if the images you are loading should be saved into the cache (thus available in the image view on future launches of Visual Studio) or if you want them in the image view area just for this session, but not permanently. What you choose here doesn’t affect your ability to use the images on your command UI, it only affects if they will be available in future customization sessions without having to re-load them again via this same dialog.
Clicking OK will take you back out to the main tool window with the first image you added selected and scrolled into view in the image view area. NOTE: If you have filter text in the search text box, and the search text of the added images doesn’t contain the text the nodes will not be visible in the image view area. This is by design. Clicking cancel or the red X to close the dialog window will result in the images not being placed into the image list.
Now that you know how to load your own images and rebuild the cache locally if you so desire we can tackle the question “How the heck do I get these images onto my UI already?!?!” There are again a couple of ways you can do this. First, we assume the node you are targeting is visible in the hierarchical view area of the tool window. Let’s assume I want to change the icon of File->Exit. Initially my tool window looks like this
Now I have two choices. I can copy / paste via the keyboard or context menus (the images in the image view area also have a context menu available, but only support Copy). To do this I invoke the Copy command while the image is selected, I then select the Exit command in the hierarchy view area and choose Paste. Another method is to drag & drop from the image area up to the tree view area. Since print screen doesn’t capture cursors I can’t show this ‘in action’ but the cursor is changed to show the image you are dragging and will have a red X over the top if you try to drop it somewhere that isn’t allowed. If you are over a valid node in the tree view the red X should go away and you are free to drop there. Once I have done this the tool window will look like this.
Success! This change will now be visible on the File menu (as shown below) as well as being persisted and re-applied on subsequent sessions of Visual Studio.
Okay, what’s the catch?
Has this extension fulfilled all of your longings around command image customization? If not please do leave feedback in the comment section. There are a couple of known issues that customizations like this may expose. These bugs have since been fixed but the RTM version of the product does have them
- Bug: If you customize a given menu/toolbar across multiple sessions the customizations made in the last sessions will be the only ones to ‘stick’ on shutdown. This means all will likely look well in the UI of Visual Studio after say customizing the image. If you then restart Visual Studio the customizations made to that menu/toolbar will have reverted, except for the ones made in the last session.
Work Around: The work around is to not modify the same toolbar/menu across multiple sessions, which I admit is kind of a sucky work around, but unfortunately due to the bug there isn’t a lot of other choices until we get the fix out there.
- Bug: If you change the styling of a command in the Tools->Customize dialog on the Commands Page via the Modify Selection drop down (i.e. if you change from say ‘Default Style’ to say ‘Image and Text’) AFTER you have done other customizations (such as rename, or changing the image) the only thing that will be remembered on shutdown is the style change, not the rename or image change.
Work Around: Do the style change FIRST then make other changes. This is obnoxious but hopefully not too much so. The problem is the style flag overwrites a byte we use to track what changes have occurred (as opposed to ORing its value into the existing changes byte).
I think that covers the extension. Hopefully for those of your for whom the pain of the changes around the customization space impacted you greatly this extension will help even if just a little. If there is something I overlooked or something that is unclear or just general feedback/complaints feel free to post in the comment section. I really do read them and take them to heart (perhaps too much so at times).
Ryan Molden – Developer, Visual Studio Platform
Short Bio: Ryan has been at Microsoft since 2005 when he
bought an online diploma graduated from the University Washington with a BSc in Computer Science. He has been working on the Visual Studio Platform team for the last 2 years. His destructive tendencies have been primarily focused on the Visual Studio command system and the conversion of the UI layer of said command system to WPF. When not at work he enjoys long walks on the beach and conversing with extenders of Visual Studio to help them solve their problems and brainstorm future features for Visual Studio.