The official source of product insight from the Visual Studio Engineering Team
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:
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
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 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
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 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 16x16 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.
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 :(
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.
Thank you for writing this and sharing it. This was one of the things in VS2010 which felt unfinished/rushed and it's good to see it addressed.
Thanks Leo. As the person responsible for not being able to get the full customization experience in 2010 I felt responsible to help 'plug the gaps' as much as I could post-release. I honestly didn't think the image swapping was going to be as missed as it was, but then again I don't create custom toolbars with macros (which seemed to be a big usage case for this kind of functionality). Lesson learned and I am hoping we can roll some of this work into the product for the next release so users won't need to download extra bits to do what they want to do.
I really miss the drag and drop customization. It just made things so much easier.
km007: Yes, we have heard this from a few people. It is one of those 'not used all the time but when it is used extremely helpful' type features. Doing something similar is certainly possible in WPF (though the wacky quasi-modal dialog would be far more effort than it is worth, the wackiness that the last UI framework had to do to get the main window to be 'mostly' modal during drag/drop from the customize dialog was insane). I have looked into this but it isn't something that could be done as an extension as it requires some decent sized changes in the official shell assemblies themselves. This extension was able to leverage the fact that the images were exposed via DTE, thus it could offer a way to change the images without needing to change any of the released bits. Drag & Drop onto the real UI wouldn't be able to do that, well short of some horrendous hackery I wouldn't even dream of promoting :)
I've just used this to attempt to add the "solution close" icon to the "unload project" item on every context menu I can find.
Unfortunately, it appears to have applied the icon to a few other context menu items as well. Since I had a filter applied, I don't think I could have done this accidentally.
Can I/How do I reset VS back to the defaults?
...oh, and it's quite slow to do some things (e.g. rebuild the image cache), but it's only using 100% of a single core. Maybe some operations should be done in parallel?
Not bad. Seems you put a lot of work into this, and have thought it through. So, how about this for a new project--FIX CUSTOMIZING! How about a little drag-drop (hell, you can fake it), multiselect and searching when customizing toolbars/menus/context menus? The current version SUCKS. You have to hunt for a particular command, then add them one at a time... Its a nightmare.
Quite frankly, two people will get use out of this extension. However, if it was built to make customization easy, tens of thousands of people would praise you and name their firstborns after you.
Copy/paste your solution, rename it "NonSuckyCustomization" and get on it ASAP. Going to start siring a brood of children who will all be named "Ryan Molden Rox Bawls" if you're successful.
I like this.
Just a comment. "Show only visible items" -> that's nice.... But, if I uncheck it, how will you be able to show invisible items?
"[ ] display hidden items" might be a better option.
Roger: You can do Tools->Import / Export Settings -> Reset Settings which will 'undo' all of the customizations. This version doesn't have any history of the nodes before the changes so it can't be more intelligent in undo. As for the speed, we are fairly tightly constrained by the fact it uses DTE (which is COM based) and thus is thread bound. It tries to do most of the work on a background thread to avoid locking up the UI, however accessing any DTE property causes COM to transition us back to the UI thread, so in a sense we are bottlenecked in terms of absolute speed because so much needs to be done on the UI thread due to COM threading requirements :(
Will: Thanks for the feedback. As I stated in the post there is little I can do to 'fix customizing' since the bits have shipped and I can't change shipping bits in an extension due to servicing constraints. I wrote the extension as a way to provide functionality that a few people (more than two) have asked for specifically. I will make sure to add your vote to the 'SUCKS' column in the tracking I do on overall satisfaction with the customization story in 2010. I will also append 'NIGHTMARE'. As for all the other things you mention (search, drag & drop, multi-select) they have been suggested and are certainly known as possible improvements to us. I didn't have time to bring all of this in 2010 because I had my hands full changing over the entire command UI (which is far more complex than your garden variety app's commanding system, and far more complex than anything WPF supports 'out of the box') and making sure it still worked for the hundreds of partners that rely on it. That and I occasionally have to sleep / spend time with my family. For 2011 I was thinking of taking up a meth habit so I could stay awake days on end improving the customization experience, so stay tuned.
>Just a comment. "Show only visible items" -> that's nice.... But, if I uncheck it, how will you be able to show invisible items?
Because I am magic :) I see your point, technically it means visible/hidden in the real VS UI (not in the toolwindow itself). Therefore I *can* show 'invisible' items, simply by making them visible in the toolwindow even though they aren't visible in the actual VS UI at the moment. I will consider changing the text/meaning of the checkbox if I release a v2, thanks for the feedback.
Ryan, this is great work and I appreciate the ability to swap out command images for something I might find more suitable.
My need/hope is that the default command images in VS should be vector based or of high enough quality that they don't look blurry in high DPI settings. This extension would be even more useful to me if it came packaged with additional icons sets, much in the same way the VS themes extension comes packaged with a few presets (Silver and Autumn are great, I don't even feel the need to customize beyond those). Now that the VS UI scales cleanly with DPI and zoom changes it would be great if the art could behave the same way.
James: Yes, it would be nice if we had vector based images. We have thousands of images in VS and in the near term converting them all to a vector format would be a very arduous task. I personally would love to do it and think we need to sooner or later.
The additional icon sets idea is interesting. I stuck with the ones that come in VS due to concerns about licensing/image rights and the fact I would need full LCA (legal) review to ship any images that Microsoft doesn't own the rights to. Needless to say that whole process is...not something I would look forward too :) Maybe for v2 (if there is desire for one) I could investigate getting some more choices in there.
So clippy isn't making a return? :(
@nigel: He is if I have my way!! (note: I won't have my way :))
· Change icon for the «Cancel» button on the «Build» toolbar.
· Restart VS
· Modify the same toolbar with «Tools\Customize» - set «Begin a Group» for the «Build Solution» button.
· The changed icon has reset to original state.
The same occurs with most buttons/menus (not quite each). In place of «Begin a Group» may be any other modification on the toolbar.
This makes the extension useless!