Introduction
I want to present to you a part of one of my projects that I considered interesting to use within WPF. I will describe the creation of a customizable ContextMenu to use in your application, based on an XML data source.
Background
The traditional way of creating ContextMenus (it could also be Menu) involved building an Item collection and specifying the properties from the code. Even if you use VS IDE, it is not so easy... you have to manually add each item and open endless dialogs to set all of the properties for each MenuItem. Then changing an item at a later time... it really takes a lot of time.
Using the Code
WPF and XAML make your life a lot easier because they support Binding. As you know, Binding in WPF is really easy and can be done with little code in most cases. Let's return to our ContextMenu. In WPF, you can declare an ItemsSource property and bind it to a data source.
The data source can be of any supported kind, but I think one of the greatest is the XML data source. Building an XML data source is easy, thanks to the XMLDataProvider class. You only need to name the object and specify the external source, as well as the Xpath for the root element, and you're ready to go.
<XmlDataProvider x:Key="dataProvider" XPath="//Menus/Menu[@id='Menu1']"
Source="file.xml"/>
For the given file.xml:
<Menus xmlns="">
<Menu id="Menu1" Name="Main Menu">
<item>item1</item>
<item>item2</item>
<item>item3</item>
</Menu>
</Menus>
Now let's customize the look of the menu DataTemplate. Basically, now you can create any control you want to hold inside the menu while binding to DataTemplate properties.
<DataTemplate DataType="item">
<MenuItem Header="{Binding XPath=@Name}">
</MenuItem>
</DataTemplate>
Here I've used MenuItem, but you can use ANY Control. As you saw earlier (XPath="//Menus/Menu[@id='Menu1']" and XPath=@Name), we've used XPath (XML Path Language). This is a powerful query language for XML that you can use in Binding. In short terms, XPath=@Name selects the attribute Name from the current node and XPath="//Menus/Menu[@id='Menu1']" selects the node of type Menu, which is the child of a root Menu's node and has the "Menu1" ID attribute.
Of course, you can add multiple attributes and nodes to the XML node and further customize the ContextMenu. For example, given the following file.xml...
<Menus xmlns="">
<Menu id="Menu1" Name="Main Menu">
<item Name="Item1" ImageLocation="c:\image1.jpg"></item>
<item Name="Item2" ImageLocation="c:\image2.jpg"></item>
<item Name="Item3" ImageLocation="c:\image3.jpg"></item>
</Menu>
</Menus>
...you can use:
<DataTemplate DataType="item">
<MenuItem Header="{Binding XPath=@Name}">
<MenuItem.Icon>
<Image Source="{Binding XPath=@ImageLocation}"></Image>
</MenuItem.Icon>
</MenuItem>
</DataTemplate>
So, the menu item has an icon whose DataSource is the file specified in the ImageLocation attribute of the item node. So far so good, but we will need submenus to this menu. For example, for the given file.xml:
="1.0" ="utf-8"
<Menus xmlns="">
<Menu id="Menu1" Name="Main Menu">
<item Name="Item1" ImageLocation="c:\image1.jpg"></item>
<item Name="Item2" ImageLocation="c:\image2.jpg"></item>
<item Name="Item3" ImageLocation="c:\image3.jpg"></item>
<itemlist Name="Submenu">
<item Name="Item5" ImageLocation="c:\image1.jpg"></item>
<item Name="Item6" ImageLocation="c:\image2.jpg"></item>
<item Name="Item7" ImageLocation="c:\image3.jpg"></item>
</itemlist>
</Menu>
<Menu id="Menu2" Name="Intermediate">
<item Name="Item8" ImageLocation="c:\image1.jpg">=</item>
</Menu>
<Menu Name="Advanced">
<item Name="Item9" ImageLocation="c:\image1.jpg"></item>
</Menu>
</Menus>
To enable the itemList to show submenus, you must add these lines of XAML:
<HierarchicalDataTemplate DataType="Menu" ItemsSource="{Binding XPath=*}">
<TextBlock Text="{Binding XPath=@Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="itemlist" ItemsSource="{Binding XPath=*}">
<TextBlock Text="{Binding XPath=@Name}"/>
</HierarchicalDataTemplate>
Again, I've used TextBlock... You can use any Control you want for your project. Now to add the ContextMenu to your Control:
ContextMenu menu = new ContextMenu();
Binding binding = new Binding();
binding.Source=this.Resources["dataProvider"];
binding.XPath="."; menu.SetBinding(Menu.ItemsSourceProperty,binding);
this.ContextMenu = menu;
So now you have a more simplified and ready-to-use ContextMenu that, after being built, is easy to modify. Simply modifying the external XML file will change the ContextMenu.
I hope you found this article useful. I await your questions regarding this article.