Add Context Menu
In this article you will see how to attach a RadContextMenu to a data-bound RadTreeView and perform actions on the clicked treeview items, depending on the selection in the context menu.
Defining the ViewModels
For the purposes of the article, the ItemsSource of the RadTreeView will contain League objects. Each league will contain division objects, thus achiving a hierarchical structure. With the help of the RadContextMenu we will give the option of adding a new child, adding a new sibling or deleting an item. In order to do that we will implement a base class with three commands: DeleteCommand, NewSiblingCommand and NewChildCommand. The first two commands will raise an event with custom command arguments. The parent item will listen for it and perform the necessary update. The class structure is demonstrated in Example 1.
Example 1: Defining the ViewModels
public enum ActionToTake
{
CreateSibling,
Delete
}
public class UpdateNeededEventArgs : EventArgs
{
public ActionToTake ActionToTake { get; set; }
}
public abstract class BaseModel : ViewModelBase
{
public BaseModel()
{
this.DeleteCommand = new DelegateCommand(OnDeleteCommandExecuted);
this.NewSiblingCommand = new DelegateCommand(OnNewSiblingCommandExecuted);
this.NewChildCommand = new DelegateCommand(OnNewChildExecuted);
}
public ICommand DeleteCommand { get; private set; }
public ICommand NewSiblingCommand { get; private set; }
public ICommand NewChildCommand { get; private set; }
public event EventHandler<UpdateNeededEventArgs> UpdateNeeded;
protected abstract void OnNewChildExecuted(object obj);
protected virtual void OnUpdateNeeded(UpdateNeededEventArgs e)
{
EventHandler<UpdateNeededEventArgs> handler = UpdateNeeded;
handler?.Invoke(this, e);
}
private void OnDeleteCommandExecuted(object obj)
{
this.OnUpdateNeeded(new UpdateNeededEventArgs() { ActionToTake = ActionToTake.Delete });
}
private void OnNewSiblingCommandExecuted(object obj)
{
this.OnUpdateNeeded(new UpdateNeededEventArgs() { ActionToTake = ActionToTake.CreateSibling });
}
}
public class Division : BaseModel
{
public Division(string name)
{
this.Name = name;
}
private string name;
public string Name
{
get
{
return this.name;
}
set
{
if (this.name != value)
{
this.name = value;
this.OnPropertyChanged("Name");
}
}
}
protected override void OnNewChildExecuted(object obj)
{
// Add children here
}
}
public class League : BaseModel
{
private ObservableCollection<Division> divisions = new ObservableCollection<Division>();
public League(string name, ObservableCollection<Division> divisions)
{
this.Name = name;
this.Divisions = divisions;
foreach (Division division in this.Divisions)
{
division.UpdateNeeded += Division_UpdateNeeded;
}
}
public string Name
{
get;
set;
}
public ObservableCollection<Division> Divisions
{
get
{
return this.divisions;
}
set
{
if (this.divisions != value)
{
this.divisions = value;
this.OnPropertyChanged("Divisions");
}
}
}
protected override void OnNewChildExecuted(object obj)
{
this.Divisions.Add(new Division("New Division"));
}
private void Division_UpdateNeeded(object sender, UpdateNeededEventArgs e)
{
switch (e.ActionToTake)
{
case ActionToTake.CreateSibling:
this.Divisions.Add(new Division("New Division"));
break;
case ActionToTake.Delete:
var divisionToDelete = sender as Division;
this.Divisions.Remove(divisionToDelete);
break;
default:
break;
}
}
}
public class ViewModel : ViewModelBase
{
private ObservableCollection<League> leagues = new ObservableCollection<League>();
public ViewModel(ObservableCollection<League> leagues)
{
this.Leagues = leagues;
foreach (League league in leagues)
{
league.UpdateNeeded += League_UpdateNeeded;
}
}
public ICommand DeleteCommand { get; set; }
public ObservableCollection<League> Leagues
{
get
{
return this.leagues;
}
set
{
if (this.leagues != value)
{
this.leagues = value;
this.OnPropertyChanged("Leagues");
}
}
}
private void League_UpdateNeeded(object sender, UpdateNeededEventArgs e)
{
switch (e.ActionToTake)
{
case ActionToTake.CreateSibling:
this.Leagues.Add(new League("New League", new ObservableCollection<Division>()));
break;
case ActionToTake.Delete:
var leagueToRemove = sender as League;
this.Leagues.Remove(leagueToRemove);
break;
default:
break;
}
}
}Defining the view
Next, we will define the RadTreeView in xaml. We will create the DataTemplates which will display our hierarchical data and a RadContextMenu which will invoke the corresponding commands from the model.
Example 2: Defining the view
<Grid>
<Grid.Resources>
<Style TargetType="telerik:RadTreeViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
<telerik:RadContextMenu x:Key="TreeViewContextMenu" >
<telerik:RadMenuItem Header="New Child" Command="{Binding NewChildCommand}"/>
<telerik:RadMenuItem Header="New Sibling" Command="{Binding NewSiblingCommand}" />
<telerik:RadMenuItem Header="Delete" Command="{Binding DeleteCommand}"/>
</telerik:RadContextMenu>
<DataTemplate x:Key="DivisionTemplate">
<TextBlock Text="{Binding Name}" telerik:RadContextMenu.ContextMenu="{StaticResource TreeViewContextMenu}"/>
</DataTemplate>
<HierarchicalDataTemplate x:Key="LeagueTemplate"
ItemTemplate="{StaticResource DivisionTemplate}"
ItemsSource="{Binding Divisions}">
<TextBlock Text="{Binding Name}" telerik:RadContextMenu.ContextMenu="{StaticResource TreeViewContextMenu}" />
</HierarchicalDataTemplate>
</Grid.Resources>
<telerik:RadTreeView x:Name="radTreeView"
ItemTemplate="{StaticResource LeagueTemplate}"
ItemsSource="{Binding Path=Leagues}" />
</Grid>
Creating the ViewModel
Finally all we need to do is create some sample data and set an instance of our ViewModel as the DataContext.
Example 3: Creating the ViewModel
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.DataContext = this.SetUpViewModel();
}
private ViewModel SetUpViewModel()
{
var leagues = new ObservableCollection<League>();
for (int i = 1; i <= 5; i++)
{
var divisions = new ObservableCollection<Division>();
for (int j = 1; j <= 5; j++)
{
var division = new Division("Division " + j);
divisions.Add(division);
}
var league = new League("League " + i , divisions);
leagues.Add(league);
}
return new ViewModel(leagues);
}
}Figure 1: Result from the Example in the Office2016 theme
