Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Silverlight Menu

0.00/5 (No votes)
24 Apr 2011 3  
A lightweight, professional-looking, MVVM-enabled dropdown menu for Silverlight
Silverlight Menus

Table of Contents

Introduction 

These days, I took some time to create a Silverlight menu component, which I'll start using in my projects. It's true that there are much better components in the marketplace, but you can use this one for free, even for commercial purposes, and extend it as you wish.

This article deals first with the features of Silverlight Menu, and also with the implementation details. There are some interesting features, such as MVVM support, XAML support, commanding, checkable menu items, deep menus and basic styling configurations. Although I'm far from being an expert, I hope this article to give readers some notions of building useful components with simple pieces in Silverlight.

You can see a live demo here in this page.

System Requirements

To use the application described in this article, you can download the following 100% free development tools directly from Microsoft:

Default Usage

The most simple way of using Silverlight Menu is to declare the Menu element directly in the XAML, assigning the MenuItem to a static resource and binding the MenuItemClicked event to some event handler:

<l:Menu 
    x:Name="mnuTop"  
    MenuItem="{StaticResource mnuRoot}" 
    MenuItemClicked="Menu_MenuItemClicked"/>

This is the simple part. The "hard" part is to declare the MenuItem hierarchy inside Resources section of your page or user control. Notice that the above snippet references the "mnuRoot" MenuItem, so you must start your menu tree with a MenuItem with a x:Key value set to "mnuRoot". As the name says, this is the root for our hierarchy tree, and right below it stand the first level menu items, that fall inside the horizontal top panel. The next level is made up by the children of the first MenuItem level, that will form the vertical menus. The next levels will be all vertical menus, displayed at the right side of their parent MenuItem elements.

Here is the list of MenuItem properties that you can customize:

  • MenuItems: This is the list of submenus inside the menu item.
  • Name: The name that uniquely identify the menu item. Notice that each separator also must receive a unique name.
  • ParentName: [This property is used for internal purposes only.]
  • Text: The display text for the menu item.
  • ImagePath: The relative path for the image source displayed at the left of the text. If the path is not found, no image will be displayed. If no image is provided, the application will look for a file located at the ImagesPath property of Menu element + menu item name + ".png".
  • IsEnabled: Boolean property that enables/disables a specific menu item.
  • IsCheckable: Boolean property that defines whether the menu item can be checked or not.
  • IsChecked: If the menu item is checkable, and if the menu item is checked, a "checked.png" image will be displayed, otherwise no image is displayed.
  • Level: [This property is used for internal purposes only.]
  • MenuGrid: [This property is used for internal purposes only.]

Below there is a MenuItem hierarchy for our example (notice that this example was taken from Visual Studio 2010 menus). At first, this seems to be too much work to do, but since it is XAML, you can make use of Intellisense to discover which properties you can customize for each MenuItem:

<UserControl.Resources>
    <l:MenuItem x:Key="mnuRoot" Name="mnuRoot">
            <l:MenuItem Name="mnuFile" Text="File">
                <l:MenuItem Name="mnuNewProject" Text="New Project..."/>
                <l:MenuItem Name="mnuNewWebSite" Text="New Web Site..."/>
                <l:MenuItem Name="mnuNewFile" Text="New File..."/>
                <l:MenuItem Name="mnuSeparator1" Text="-"/>
                <l:MenuItem Name="mnuOpenProject" Text="Open Project..."/>
                <l:MenuItem Name="mnuOpenWebSite" Text="Open Web Site..."/>
                <l:MenuItem Name="mnuOpenFile" Text="Open File..."/>
                <l:MenuItem Name="mnuSeparator2" Text="-"/>
                <l:MenuItem Name="mnuAdd" Text="Add">
                    <l:MenuItem Name="mnuNewProject2" Text="New Project...">
                        <l:MenuItem Name="mnuNewASPNETProject" 
			Text="New ASP.NET Project"/>
                        <l:MenuItem Name="mnuNewSilverlightProject" 
			Text="New Silverlight Project"/>
                    </l:MenuItem>
                    <l:MenuItem Name="mnuNewWebSite2" Text="New Web Site..."/>
                    <l:MenuItem Name="mnuSeparator11" Text="-"/>
                    <l:MenuItem Name="mnuExistingProject" Text="Existing Project..."/>
                    <l:MenuItem Name="mnuExistingWebSite" Text="Existing Web Site..."/>
                </l:MenuItem>
                <l:MenuItem Name="mnuSeparator3" Text="-"/>
                <l:MenuItem Name="mnuClose" Text="Close"/>
                <l:MenuItem Name="mnuCloseSolution" Text="Close Solution"/>
                <l:MenuItem Name="mnuSeparator4" Text="-"/>
                <l:MenuItem Name="mnuSave" Text="Save" IsEnabled="False"/>
                <l:MenuItem Name="mnuSaveAs" Text="Save As..." IsEnabled="False"/>
                <l:MenuItem Name="mnuSaveAll" Text="Save All"/>
                <l:MenuItem Name="mnuExportTemplate" Text="Export Template..."/>
                <l:MenuItem Name="mnuSeparator5" Text="-"/>
                <l:MenuItem Name="mnuPageSetup" Text="Page Setup..."/>
                <l:MenuItem Name="mnuPrint" Text="Print..."/>
                <l:MenuItem Name="mnuSeparator6" Text="-"/>
                <l:MenuItem Name="mnuRecentFiles" Text="Recent Files"/>
                <l:MenuItem Name="mnuRecentProjectsandSolutions" 
			Text="Recent Projects and Solutions"/>
                <l:MenuItem Name="mnuSeparator7" Text="-"/>
                <l:MenuItem Name="mnuExit" Text="Exit"/>
            </l:MenuItem>
            <l:MenuItem Name="mnuEdit" Text="Edit">
                <l:MenuItem Name="mnuUndo" Text="Undo"/>
                <l:MenuItem Name="mnuRedo" Text="Redo"/>
                <l:MenuItem Name="mnuSeparator8" Text="-"/>
                <l:MenuItem Name="mnuCut" Text="Cut"/>
                <l:MenuItem Name="mnuCopy" Text="Copy"/>
                <l:MenuItem Name="mnuPaste" Text="Paste"/>
                <l:MenuItem Name="mnuDelete" Text="Delete"/>
                <l:MenuItem Name="mnuSeparator9" Text="-"/>
                <l:MenuItem Name="mnuSelectAll" Text="Select All"/>
                <l:MenuItem Name="mnuSeparator10" Text="-"/>
                <l:MenuItem Name="mnuQuickFind" Text="Quick Find"/>
                <l:MenuItem Name="mnuQuickReplace" Text="Quick Replace"/>
            </l:MenuItem>
            <l:MenuItem Name="mnuView" Text="View">
                <l:MenuItem Name="mnuStartPage" Text="Start Page"/>
                <l:MenuItem Name="mnuOtherWindows" Text="Other Windows">
                    <l:MenuItem Name="mnuDatabaseExplorer" Text="Database Explorer"/>
                    <l:MenuItem Name="mnuErrorList" Text="Error List"/>
                    <l:MenuItem Name="mnuPropertiesWindow" Text="Properties Window"/>
                    <l:MenuItem Name="mnuSolutionExplorer" Text="Solution Explorer"/>
                    <l:MenuItem Name="mnuToolbox" Text="Toolbox"/>
                    <l:MenuItem Name="mnuSeparator30" Text="-"/>
                    <l:MenuItem Name="mnuWebBrowser" Text="Web Browser"/>
                    <l:MenuItem Name="mnuSeparator31" Text="-"/>
                    <l:MenuItem Name="mnuFindResults1" Text="Find Results 1"/>
                </l:MenuItem>
                <l:MenuItem Name="mnuSeparator12" Text="-"/>
                <l:MenuItem Name="mnuToolbars" Text="Toolbars">
                    <l:MenuItem Name="mnuBuild" Text="Build" 
			IsCheckable="True" IsChecked="True"/>
                    <l:MenuItem Name="mnuDataDesign" 
			Text="Data Design" IsCheckable="True"/>
                    <l:MenuItem Name="mnuDatabaseDiagram" 
			Text="Database Diagram" IsCheckable="True" IsChecked="True"/>
                    <l:MenuItem Name="mnuDebug2" Text="Debug" 
			IsCheckable="True" IsChecked="True"/>
                    <l:MenuItem Name="mnuFormatting" 
			Text="Formatting" IsCheckable="True"/>
                    <l:MenuItem Name="mnuHTMLSourceEditing" 
			Text="HTML Source Editing" IsCheckable="True"/>
                    <l:MenuItem Name="mnuLayout" Text="Layout" IsCheckable="True"/>
                    <l:MenuItem Name="mnuQueryDesigner" 
			Text="Query Designer" IsCheckable="True"/>
                    <l:MenuItem Name="mnuStandard" 
			Text="Standard" IsCheckable="True" IsChecked="True"/>
                    <l:MenuItem Name="mnuStyleSheet" 
			Text="Style Sheet" IsCheckable="True"/>
                    <l:MenuItem Name="mnuTableDesigner" 
			Text="Table Designer" IsCheckable="True"/>
                    <l:MenuItem Name="mnuTextEditor" 
			Text="Text Editor" IsCheckable="True"/>
                    <l:MenuItem Name="mnuViewDesigner" 
			Text="View Designer" IsCheckable="True"/>
                    <l:MenuItem Name="mnuWebBrowser2" 
			Text="Web Browser" IsCheckable="True"/>
                    <l:MenuItem Name="mnuWebOneClickPublish" 
			Text="Web One Click Publish" IsCheckable="True"/>
                    <l:MenuItem Name="mnuSeparator32" Text="-" IsCheckable="True"/>
                    <l:MenuItem Name="mnuCustomize2" 
			Text="Customize" IsCheckable="True"/>
                </l:MenuItem>
                <l:MenuItem Name="mnuFullScreen" Text="Full Screen"/>
                <l:MenuItem Name="mnuSeparator13" Text="-"/>
                <l:MenuItem Name="mnuPropertyPages" Text="Property Pages"/>
            </l:MenuItem>
            <l:MenuItem Name="mnuProject" Text="Project">
                <l:MenuItem Name="mnuAddClass" Text="Add Class..."/>
                <l:MenuItem Name="mnuAddNewItem" Text="Add New Item..."/>
                <l:MenuItem Name="mnuAddExistingItem" Text="Add Existing Item"/>
                <l:MenuItem Name="mnuSeparator14" Text="-"/>
                <l:MenuItem Name="mnuAddReference" Text="Add Reference"/>
                <l:MenuItem Name="mnuAddServiceReference" 
			Text="Add Service Reference..."/>
                <l:MenuItem Name="mnuSetasStartUpProject" 
			Text="Set as StartUp Project"/>
                <l:MenuItem Name="mnuProjectDependencies" 
			Text="Project Dependencies..."/>
                <l:MenuItem Name="mnuSeparator15" Text="-"/>
                <l:MenuItem Name="mnuSilverlightMenuProperties" 
			Text="SilverlightMenu Properties"/>
            </l:MenuItem>
            <l:MenuItem Name="mnuDebug" Text="Debug">
                <l:MenuItem Name="mnuStartDebugging" Text="Start Debugging"/>
                <l:MenuItem Name="mnuSeparator16" Text="-"/>
                <l:MenuItem Name="mnuBuildSilverlightMenu" Text="Build SilverlightMenu"/>
                <l:MenuItem Name="mnuSeparator17" Text="-"/>
                <l:MenuItem Name="mnuStepInto" Text="Step Into"/>
                <l:MenuItem Name="mnuStepOver" Text="Step Over"/>
                <l:MenuItem Name="mnuSeparator18" Text="-"/>
                <l:MenuItem Name="mnuWindows" Text="Windows">
                    <l:MenuItem Name="mnuImmediate" Text="Immediate"/>
                    <l:MenuItem Name="mnuLocals" Text="Locals"/>
                    <l:MenuItem Name="mnuSeparator33" Text="-"/>
                    <l:MenuItem Name="mnuCallStack" Text="Call Stack"/>
                </l:MenuItem>
                <l:MenuItem Name="mnuSeparator19" Text="-"/>
                <l:MenuItem Name="mnuClearAllDataTips" Text="Clear All DataTips"/>
                <l:MenuItem Name="mnuExportDataTips" Text="Export DataTips..."/>
                <l:MenuItem Name="mnuImportDataTips" Text="Import DataTips..."/>
                <l:MenuItem Name="mnuSeparator20" Text="-"/>
                <l:MenuItem Name="mnuOptionsandSettings" 
			Text="Options and Settings..."/>
            </l:MenuItem>
            <l:MenuItem Name="mnuData" Text="Data">
                <l:MenuItem Name="mnuShowDataSources" Text="Show Data Sources"/>
                <l:MenuItem Name="mnuAddNewDataSource" Text="Add New Data Source..."/>
            </l:MenuItem>
            <l:MenuItem Name="mnuTools" Text="Tools">
                <l:MenuItem Name="mnuConnecttoDatabase" Text="Connect to Database..."/>
                <l:MenuItem Name="mnuSeparator21" Text="-"/>
                <l:MenuItem Name="mnuExtensionManager" Text="Extension Manager..."/>
                <l:MenuItem Name="mnuSeparator22" Text="-"/>
                <l:MenuItem Name="mnuSettings" Text="Settings">
                    <l:MenuItem Name="mnuBasicSettings" Text="Basic Settings"/>
                    <l:MenuItem Name="mnuCodeOnly" Text="Code Only"/>
                    <l:MenuItem Name="mnuExpertSettings" Text="Expert Settings"/>
                    <l:MenuItem Name="mnuSeparator34" Text="-"/>
                    <l:MenuItem Name="mnuReset" Text="Reset..."/>
                    <l:MenuItem Name="mnuSeparator35" Text="-"/>
                    <l:MenuItem Name="mnuImportandExportSettings" 
			Text="Import and Export Settings..."/>
                </l:MenuItem>
                <l:MenuItem Name="mnuCustomize" Text="Customize..."/>
                <l:MenuItem Name="mnuOptions" Text="Options..."/>
            </l:MenuItem>
            <l:MenuItem Name="mnuWindow" Text="Window">
                <l:MenuItem Name="mnuSplit" Text="Split"/>
                <l:MenuItem Name="mnuSeparator27" Text="-"/>
                <l:MenuItem Name="mnuFloat" Text="Float"/>
                <l:MenuItem Name="mnuDock" Text="Dock"/>
                <l:MenuItem Name="mnuDockasTabbedDocument" 
			Text="Dock as Tabbed Document"/>
                <l:MenuItem Name="mnuAutoHide" Text="Auto Hide"/>
                <l:MenuItem Name="mnuHide" Text="Hide"/>
                <l:MenuItem Name="mnuSeparator28" Text="-"/>
                <l:MenuItem Name="mnuAutoHideAll" Text="Auto Hide All"/>
                <l:MenuItem Name="mnuNewHorizontalTabGroup" 
		Text="New Horizontal Tab Group"/>
                <l:MenuItem Name="mnuNewVerticalTabGroup" Text="New Vertical Tab Group"/>
                <l:MenuItem Name="mnuCloseAllDocuments" Text="Close All Documents"/>
                <l:MenuItem Name="mnuResetWindowLayout" Text="Reset Window Layout"/>
                <l:MenuItem Name="mnuSeparator29" Text="-"/>
                <l:MenuItem Name="mnu1MainPagexaml" Text="1 MainPage.xaml"/>
                <l:MenuItem Name="mnu2Menucs" Text="2 Menu.cs"/>
                <l:MenuItem Name="mnu3MenuItemcs" Text="3 MenuItem.cs"/>
                <l:MenuItem Name="mnu4MainPagexamlcs" Text="4 MainPage.xaml.cs"/>
                <l:MenuItem Name="mnu5Appxamlcs" Text="5 App.xaml.cs"/>
                <l:MenuItem Name="mnuWindows2" Text="Windows..."/>
                
            </l:MenuItem>
            <l:MenuItem Name="mnuHelp" Text="Help">
                <l:MenuItem Name="mnuViewHelp" Text="View Help"/>
                <l:MenuItem Name="mnuManageHelpSettings" Text="Manage Help Settings"/>
                <l:MenuItem Name="mnuSeparator23" Text="-"/>
                <l:MenuItem Name="mnuMSDNForums" Text="MSDN Forums"/>
                <l:MenuItem Name="mnuSeparator24" Text="-"/>
                <l:MenuItem Name="mnuSamples" Text="Samples"/>
                <l:MenuItem Name="mnuSeparator25" Text="-"/>
                <l:MenuItem Name="mnuCustomerFeedbackOptions" 
		Text="Customer Feedback Options..."/>
                <l:MenuItem Name="mnuRegisterProduct" Text="Register Product"/>
                <l:MenuItem Name="mnuCheckforUpdates" Text="Check for Updates"/>
                <l:MenuItem Name="mnuTechnicalSupport" Text="Technical Support"/>
                <l:MenuItem Name="mnuOnlinePrivacyStatement" 
		Text="Online Privacy Statement..."/>
                <l:MenuItem Name="mnuSeparator26" Text="-"/>
                <l:MenuItem Name="mnuAboutMicrosoftVisualWebDeveloper2010Express" 
	Text="About Microsoft Visual Web Developer 2010 Express"/>
            </l:MenuItem>
        </l:MenuItem>
</UserControl.Resources>

In the above example, you must implement the MenuItemClicked event, otherwise you would not be able to take action when the user clicks a specific menu item. Below there is an example of implementation of this event, that shows a simple messagebox with the name of the menu item and also informs when a checkable menu has been checked/unchecked.

private void Menu_MenuItemClicked(object sender, EventArgs e)
{
    var clickedItem = (MenuItem)sender;
    
    if (clickedItem.IsCheckable)
        MessageBox.Show(string.Format("{0} is now {1}", clickedItem.Name, 
	clickedItem.IsChecked ? "CHECKED" : "UNCHECKED"));
    else
        MessageBox.Show(string.Format("You clicked: {0}", clickedItem.Name));
        
    switch (clickedItem.Name)
    {
        case "mnuOpenProject":
            var newMenuItem = new MenuItem()
            {
                Name = string.Format("Item{0}", menuIndex),
                Text = string.Format("{0} Item {0}", menuIndex)
            };
            menuIndex++;
            mnuTop.AddMenuItem(clickedItem, newMenuItem);
            break;
    }
}

Support for MVVM Pattern

If you don't know the MVVM (Model View View Model) pattern, here is a very simple explanation: in the above example, we created the menu hierarchy directly in the view (the MainPage.xaml file) and we implemented the MenuItemClicked directly in the code behind of the view (the MainPage.xaml.cs file). In the MVVM pattern, on the other hand, we wouldn't use the view for this. Instead, we would configure the view (the MenuTestView.xaml file) so that the menu properties would be bound to properties from another class (the View Model, represented by the MenuTestViewModel.cs file).

The following diagram shows the MVVM components in a generic scenario:

MVVM

Back to our application: in order to use the MVVM mode, comment the following line and enable the application to use MenuTestView as the startup:

private void Application_Startup(object sender, StartupEventArgs e)
{
    //this.RootVisual = new MainPage();
    this.RootVisual = new MenuTestView();
}

Open the MenuTestView view and notice the differences for the MVVM style: first, we must define the ImagesPath, because the View is located at a "\Views" folder, and this changes the relative path for the images. Then we must configure the MenuItem property to bind to some property on the ViewModel side (the MVVMMenuItem, in our case). And finally there is the Command property, that is bound to the MenuCommand property on the ViewModel side. Notice that the MenuCommand has the same effect as implementing the MenuItemClicked event, with the difference of being implemented in the ViewModel instead of in the code behind:

<l:Menu 
    ImagesPath="{Binding ImagesPath}" 
    MenuItem="{Binding MVVMMenuItem}" 
    Command="{Binding Path=MenuCommand}"/>

The following code snippet shows how we implement the menu item hierarchy programmatically in the view model.

public class MenuTestViewModel : INotifyPropertyChanged
{
    MenuItem mvvmMenuItem;
    string imagesPath = "../Images/";
    public MenuTestViewModel()
    {
        mvvmMenuItem = new MenuItem()
        {
            Name = "Root"
        };
        
        var mnuFile = new MenuItem() { Name = "mnuFile", Text = "File" };
        var mnuEdit = new MenuItem() { Name = "mnuEdit", Text = "Edit" };
        var mnuWindow = new MenuItem() { Name = "mnuWindow", Text = "Window" };
        var mnuHelp = new MenuItem() { Name = "mnuHelp", Text = "Help" };
        
        var mnuNew = new MenuItem() { Name = "mnuNew", Text = "New" };
        var mnuSeparator1 = new MenuItem() { Name = "mnuSeparator1", Text = "-" };
        var mnuOpenFile = new MenuItem() { Name = "mnuOpenFile", Text = "Open File" };
        var mnuSaveFile = new MenuItem() { Name = "mnuSave", 
			Text = "Save File", IsEnabled = false };
        var mnuCloseFile = new MenuItem() { Name = "mnuClose", 
			Text = "Close File", IsEnabled = false };
        var mnuSeparator2 = new MenuItem() { Name = "mnuSeparator2", Text = "-" };
        var mnuExit = new MenuItem() { Name = "mnuExit", Text = "Exit" };
        
        var mnuNewFile = new MenuItem() { Name = "mnuNewFile", Text = "New File" };
        var mnuNewProject = new MenuItem() { Name = "mnuNewProject", 
			Text = "New Project" };
        var mnuNewSolution = new MenuItem() { Name = "mnuNewSolution", 
			Text = "New Solution" };
        
        var mnuCut = new MenuItem() { Name = "mnuCut", Text = "Cut" };
        var mnuCopy = new MenuItem() { Name = "mnuCopy", Text = "Copy" };
        var mnuPaste = new MenuItem() { Name = "mnuPaste", Text = "Paste" };
        var mnuDelete = new MenuItem() { Name = "mnuDelete", Text = "Delete" };
        
        var mnuWindow1 = new MenuItem() { Name = "mnuWindow1", 
		Text = "Window 1", IsChecked = true, IsCheckable = true };
        var mnuWindow2 = new MenuItem() { Name = "mnuWindow2", 
		Text = "Window 2", IsChecked = false, IsCheckable = true };
        var mnuWindow3 = new MenuItem() { Name = "mnuWindow3", 
		Text = "Window 3", IsChecked = false, IsCheckable = true };
        
        var mnuAbout = new MenuItem() { Name = "mnuViewHelp", 
			Text = "About Silverlight Menu" };
        
        mnuNew.Add(mnuNewFile);
        mnuNew.Add(mnuNewProject);
        mnuNew.Add(mnuNewSolution);
        
        mnuFile.Add(mnuNew);
        mnuFile.Add(mnuSeparator1);
        mnuFile.Add(mnuOpenFile);
        mnuFile.Add(mnuSaveFile);
        mnuFile.Add(mnuCloseFile);
        mnuFile.Add(mnuSeparator2);
        mnuFile.Add(mnuExit);
        
        mnuEdit.Add(mnuCut);
        mnuEdit.Add(mnuCopy);
        mnuEdit.Add(mnuPaste);
        mnuEdit.Add(mnuDelete);
        
        mnuWindow.Add(mnuWindow1);
        mnuWindow.Add(mnuWindow2);
        mnuWindow.Add(mnuWindow3);
        
        mnuHelp.Add(mnuAbout);
        
        mvvmMenuItem.Add(mnuFile);
        mvvmMenuItem.Add(mnuEdit);
        mvvmMenuItem.Add(mnuWindow);
        mvvmMenuItem.Add(mnuHelp);
    }
            .
            .
            .

In the setter part of each ViewModel property, we must call the OnPropertyChanged method, so that the View can be notified about that change.

public MenuItem MVVMMenuItem
{
    get
    {
        return mvvmMenuItem;
    }
    set
    {
        mvvmMenuItem = value;
        OnPropertyChanged("MVVMMenuItem");
    }
}

public string ImagesPath
{
    get
    {
        return imagesPath;
    }
    set
    {
        imagesPath = value;
        OnPropertyChanged("ImagesPath");
    }
}

Finally, we have the implementation for the Command property. Notice that in this type of binding, the DoMenuCommand method reference is stored when the Menu element is created (notice that the DoMenuCommand is not fired yet):

public ICommand MenuCommand
{
    get { return new RelayCommand(p => DoMenuCommand(p)); }
}

public void DoMenuCommand(object param)
{
    var menuItem = (MenuItem)param;
    
    if (menuItem.IsCheckable)
        MessageBox.Show(string.Format("{0} is now {1}", 
Item.Name, menuItem.IsChecked ? "CHECKED" : "UNCHECKED"));
    else
        MessageBox.Show(string.Format("You clicked: {0}", menuItem.Name));
}

And when a user clicks a menu item, the Menu class invokes the Execute method, passing the selected menu item as a parameter:

	command.Execute(menuItem);

The following image shows a MVVM version of our Silverlight Menu:

MVVM Silverlight Menu

Example: Configuring for a Black Style and White Style Menu

Depending on the layout styles you are using for your Silverlight application, the default styles of Silverlight Menu can break the visual identity of your web application and produce an undesirable result. You can handle this problem by configuring the Menu to use a different set of brushes and colors, as you want.

The following XAML excerpt shows how to configure Silverlight Menu for a "black" style:

Black Style

<l:Menu 
    x:Name="mnuTop"
    BorderBrush="#FF303030" 
    TopPanelBrush="Black"
    ImageBackgroundBrush="#FF404040"
    FocusBrush="#FF808080"
    FocusBorderBrush="Gray"
    Foreground="Orange"
    Background="Black"
    MenuItem="{StaticResource mnuRoot}" 
    MenuItemClicked="Menu_MenuItemClicked"/>

And below you can see how to configure a "white" style menu:

White Style

<l:Menu 
    x:Name="mnuTop"
    BorderBrush="#FFC0C0C0"
    TopPanelBrush="White"
    ImageBackgroundBrush="#FFC0C0C0"
    FocusBrush="#FFE0E0D0"
    FocusBorderBrush="#FFC0C0C0"
    Foreground="Black"
    Background="#FFF0F0F0"
    MenuItem="{StaticResource mnuRoot}" 
    MenuItemClicked="Menu_MenuItemClicked"/>  

Adding Sub Items to Menu Items

There are some cases when you need to add submenus dynamically. This is the case of "Recent Files" of almost every desktop application that deals with documents. Silverlight Menu is prepared to deal with this scenario:

Adding Items

The only requirement is that you call the AddMenuItem method of the Menu class whenever you want to add an sub item to a menu item (because the AddMenuItem will refresh the menu tree automatically):

switch (clickedItem.Name)
{
    case "mnuOpenProject":
        var newMenuItem = new MenuItem()
        {
            Name = string.Format("Item{0}", menuIndex),
            Text = string.Format("{0} Item {0}", menuIndex)
        };
        menuIndex++;
        mnuTop.AddMenuItem(clickedItem, newMenuItem);
        break;
}

Checkable Items

You can check/uncheck menu items, if you have configured them as "checkable":

CheckableItems

This configuration is simple and straightforward. To do this, just mark the menu item as Checkable="True", as seen below:

<l:MenuItem Name="mnuToolbars" Text="Toolbars">
	<l:MenuItem Name="mnuBuild" Text="Build" IsCheckable="True" IsChecked="True"/>
	<l:MenuItem Name="mnuDataDesign" Text="Data Design" IsCheckable="True"/>
	<l:MenuItem Name="mnuDatabaseDiagram" Text="Database Diagram" 
		IsCheckable="True" IsChecked="True"/>
	<l:MenuItem Name="mnuDebug2" Text="Debug" IsCheckable="True" IsChecked="True"/>
	<l:MenuItem Name="mnuFormatting" Text="Formatting" IsCheckable="True"/>
	<l:MenuItem Name="mnuHTMLSourceEditing" Text="HTML Source Editing" 
		IsCheckable="True"/>
	<l:MenuItem Name="mnuLayout" Text="Layout" IsCheckable="True"/>
	<l:MenuItem Name="mnuQueryDesigner" Text="Query Designer" IsCheckable="True"/>
	<l:MenuItem Name="mnuStandard" Text="Standard" IsCheckable="True" 
		IsChecked="True"/>
	<l:MenuItem Name="mnuStyleSheet" Text="Style Sheet" IsCheckable="True"/>
	<l:MenuItem Name="mnuTableDesigner" Text="Table Designer" IsCheckable="True"/>
	<l:MenuItem Name="mnuTextEditor" Text="Text Editor" IsCheckable="True"/>
	<l:MenuItem Name="mnuViewDesigner" Text="View Designer" IsCheckable="True"/>
	<l:MenuItem Name="mnuWebBrowser2" Text="Web Browser" IsCheckable="True"/>
	<l:MenuItem Name="mnuWebOneClickPublish" Text="Web One Click Publish" 
		IsCheckable="True"/>
	<l:MenuItem Name="mnuSeparator32" Text="-" IsCheckable="True"/>
	<l:MenuItem Name="mnuCustomize2" Text="Customize" IsCheckable="True"/>
</l:MenuItem>

Deep Menus

Silverlight Menu supports "Deep Menus", that is, it can handle as many menu levels as you want (and your screen permits):

DeepMenus

This is possible thanks to the recursive method CreateVerticalGrid in the Menu class:

private Grid CreateVerticalGrid(
Grid parentGrid,
double currentLeftMargin,
double currentTopMargin,
MenuItem parentMenuItem,
TextBlock txt,
Grid grdVertical)
{
    .
    .
    .
    
    int gridRow = 0;
    foreach (var mi3 in parentMenuItem.MenuItems)
    {
        .
        .
        .
        
        if (mi3.MenuItems.Count > 0)
        {
            CreateVerticalGrid(parentGrid, currentLeftMargin + verticalMenuWidth, 
		currentTopMargin, mi3, txt, grdVertical);
        }
        .
        .
        .
        
    }
    
    .
    .
    .
}

Final Considerations

As you have seen, this Silverlight Menu is made of simple Silverlight elements, so forgive me if it doesn't give the functionality you want. I'd love to hear your feedback about it, especially if you use it in your projects. As I said before, this component will be part of my future projects, so I'm interested in enhancing it as much as I can, and that's why your feedback is so important.

History

  • 2010-09-26: Initial version
  • 2011-04-24: Fixed a bug when the user clicks off the menu control (it wasn't closing the menus automatically) 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here