Table of Contents
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.
To use the application described in this article, you can download the following 100% free development tools directly from Microsoft:
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;
}
}
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:
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 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:
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:
<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:
<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"/>
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:
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;
}
You can check/uncheck menu items, if you have configured them as "checkable":
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>
Silverlight Menu supports "Deep Menus", that is, it can handle as many menu levels as you want (and your screen permits):
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);
}
.
.
.
}
.
.
.
}
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.
- 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)