Originally posted at SOAPitStop.com – Sept 2, 2009
Remember that little idea that Office had a while ago that would hide infrequently-used menu items? Wasn’t that a great idea? For me, it was the very first thing I turned off after installing Office. But I do understand what they were going after. When applications do so much, every user is probably just using a subset of the whole application’s features.
The application that I’m writing is kind of getting like that. A few versions ago, I created a toolbar on the side that was planned to be context-sensitive, so it would show actions based on what data was shown and available – kind of how Microsoft is now doing with the task pane. Eventually, I may create or convert the toolbar to a task pane. But as the application was growing, I had the same thought the Office designers had: each user probably only cares about 5 or 6 menu items at a time and those items should be as readily available as possible. So instead of making personalized menus, I decided to create a Favorites toolbar. This is similar to Microsoft programs where you can add toolbars and put menu items on them.
Because the application is in flux and because I am lazy, I didn’t want to go through the effort of creating a “Customize Toolbar” dialog. I also didn’t want to have an extra dialog for “Add To Favorites”. So what I did was allow menu items to be dragged onto the toolbar. The proof-of-concept started as most do, just to see how it would work. I got it going in under 150 lines of code, even less considering whitespace and definitions and all.
To quickly summarize the technique, I started by putting a toolbar container on the form, adding a toolstrip to hold the favorites, and adding a menu to hold the draggable items.
Then I added the code to allow the dragging of the menu items:
Private Sub Menu_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) _ Handles mnuFirst.MouseMove, mnuSecond.MouseMove, mnuThird.MouseMove, mnuFourth.MouseMove, _ mnu2ndLevel1.MouseMove, mnu2ndLevel2.MouseMove, mnu2ndLevel3.MouseMove Dim item As ToolStripMenuItem If e.Button = Windows.Forms.MouseButtons.Left Then item = CType(sender, ToolStripMenuItem) item.DoDragDrop(item, DragDropEffects.Copy) End If End Sub
Then the code to drop the items (the toolstrip needs to have AllowDrop set to True):
Private Sub toolFavorites_DragEnter(ByVal sender As Object, ByVal e As DragEventArgs) _ Handles toolFavorites.DragEnter If e.AllowedEffect = DragDropEffects.Copy AndAlso e.Data.GetDataPresent(GetType(ToolStripItem)) Then e.Effect = DragDropEffects.Copy End If End Sub Private Sub toolFavorites_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs) _ Handles toolFavorites.DragDrop Dim droppedItem As ToolStripItem droppedItem = CType(e.Data.GetData(GetType(ToolStripItem)), ToolStripItem) AddToFavorites(droppedItem) End Sub Private Sub AddToFavorites(ByVal item As ToolStripItem) Dim newItem As ToolStripButton newItem = New ToolStripButton(item.Text, item.Image) newItem.Tag = item AddHandler newItem.MouseDown, AddressOf FavoritesContext AddHandler newItem.Click, AddressOf FavoritesClick AddHandler item.EnabledChanged, AddressOf OnMenuEnabledChanged toolFavorites.Items.Add(newItem) End Sub
Then the code to route the click of the favorites to the real menu item
Private Sub FavoritesClick(ByVal s As Object, ByVal e As EventArgs) Dim item As ToolStripItem item = CType(CType(s, ToolStripItem).Tag, ToolStripMenuItem) item.PerformClick() End Sub
That was really it. Of course, then I had to persist the favorites in My.Settings and provide a way of removing the favorite menu item, resulting in the above-referenced AddHandler statement for FavoritesContext and a couple other methods for running through the menu items on the form load and close. Then we need to disable the favorite button when the linked menu item is disabled, leading to the AddHandler for OnMenuEnabledChanged. It just keeps growing.