Reusing and customizing a single ContextMenu on a TreeView with RadControls for Silverlight

Posted by: the telerik blogs, on 01 Jun 2009 | View original | Bookmarked: 0 time(s)

This is part two of my Attaching a ContextMenu on TreeView blog post. I will show another approach for adding context menu on a treeview, that uses a single menu that is customized for each treeview item. One context menu per treeview should provide much better performance if the treeview is bound to a large data set, since there are much less visual elements that have to be created. I am also using an updated view model, where each item contains a reference to its parent. This will help me to rely entirely on the model for my application logic, which greatly simplifies the code-behind, because I dont have to care about the visual tree.

The declaration of the TreeView with the ContextMenu is the following:

<telerikNavigation:RadTreeView x:Name="TreeView" ItemsSource="{Binding Items, Source={StaticResource TreeViewModel}}" ItemTemplate="{StaticResource TreeViewItemTemplate}" IsExpandOnSingleClickEnabled="True" HorizontalAlignment="Left"> <telerikNavigation:RadContextMenu.ContextMenu> <telerikNavigation:RadContextMenu x:Name="ContextMenu" 
 ItemClick="ContextMenuClick" Opened="ContextMenuOpened"> <telerikNavigation:RadMenuItem Header="New Child" /> <telerikNavigation:RadMenuItem Header="New Sibling" /> <telerikNavigation:RadMenuItem Header="Delete" /> </telerikNavigation:RadContextMenu> </telerikNavigation:RadContextMenu.ContextMenu> </telerikNavigation:RadTreeView>

Note that you will have to make your Silverlight plug-in windowless to get the right click context menu. Otherwise you will need to set EventName and/or ModifierKey properties to configure the control according your needs.

I want to mention one more thing that we added with Q1 2009 SP2: ItemClicked CLR event in RadMenu and RadContextMenu, raised every time a child item is clicked. It can be attached from XAML, so you dont have to write strange code-behind for that anymore.

Since there is only one context menu for all treeview items, I need to somehow find the clicked treeview item when the menu is opened. The easiest way to do this is to call the VisualTreeHelper.FindElementsInHostCoordinates method with the position of the context menu. The following method searches for an element of the specified type at the given coordinates:

private T FindElementAt<T>(Point coordinates) where T : UIElement
{
    return (T)VisualTreeHelper.FindElementsInHostCoordinates(coordinates, this)
        .FirstOrDefault((element) => element is T);
}

RadContextMenu provides the coordinates of the mouse from the moment when it was opened in its MousePosition property (if you use assemblies from Q1 2009 SP2 or earlier, you may need to update them to the latest internal build to get this functionality):

private RadTreeViewItem ClickedTreeViewItem
{
    get
    {
        return this.FindElementAt<RadTreeViewItem>(this.ContextMenu.MousePosition);
    }
}

 

From now on my job is straightforward. In the context menu Opened event handler I customize the items, depending on the clicked treeview item:

private void ContextMenuOpened(object sender, RoutedEventArgs e)
{
    DataItem dataItem = clickedTreeViewItem.DataContext as DataItem;

    // We will disable the "New Sibling" and "Delete" context menu  
 // items if the clicked treeview item is a root item 
 bool isRootItem = dataItem.Parent == null;

    ((sender as RadContextMenu).Items[1] as RadMenuItem).IsEnabled = !isRootItem;
    ((sender as RadContextMenu).Items[2] as RadMenuItem).IsEnabled = !isRootItem;
}

 

In the ItemClicked event handler I implemented the context menu logic:

private void ContextMenuClick(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
    DataItem item = this.ClickedTreeViewItem.DataContext as DataItem;

    string header = (e.OriginalSource as RadMenuItem).Header as string;
    switch (header)
    {
        case "New Child":
            item.Items.Add(new DataItem() { Text = "New Child" });
            item.IsExpanded = true; // Ensure that the new child is visible break;
        case "New Sibling":
            item.Parent.Items.Add(new DataItem() { Text = "New Sibling" });
            break;
        case "Delete":
            item.Parent.Items.Remove(item);
            break;
    }
}

 

You may notice that there is no Rename option. This is because RadTreeView still does not support editing in data-bound mode out of the box. I hope that this feature will make it for the official Q2 2009 release next month.

 

Here is the source code:

ContextMenuInTreeView2.zip

Advertisement
Free Agile Project Management Tool from Telerik
TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.
Category: C# | Other Posts: View all posts by this blogger | Report as irrelevant | View bloggers stats | Views: 2598 | Hits: 3

Similar Posts

  • Oredev Wrap-Up more
  • Welcome the WebUI Test Studio v2.0! more
  • Introducing Recurring Appointments for Web.UI Scheduler ASP.NET AJAX more
  • A RouteHandler for IHttpHandlers more
  • What's new in RadControls for Silverlight Q3.2009 more
  • Data-binding Telerik CoverFlow for Silverlight + some Routed Commands goodness more
  • Podcast #1 – interview with Ben Scheirman co-author of ASP.NET MVC in Action more
  • Html Encoding Nuggets With ASP.NET MVC 2 more
  • RadScheduler for WinForms data binding and occurrence exceptions more
  • How to Make crossdomain.xml Work with SharePoint more

News Categories

.NET | Agile | Ajax | Architecture | ASP.NET | BizTalk | C# | Certification | Data | DataGrid | DataSet | Debugger | DotNetNuke | Events | GridView | IIS | Indigo | JavaScript | Mobile | Mono | Patterns and Practices | Performance | Podcast | Refactor | Regex | Security | Sharepoint | Silverlight | Smart Client Applications | Software | SQL | VB.NET | Visual Studio | W3 | WCF | WinFx | WPF | WSE | XAML | XLinq | XML | XSD