My goal is to create a Navigation menu in WPF.
The navigation items are built in the View Model using Nodes.
Selected SubItem
of the menu will display the content of the selected Node.
This is what I have built so far:
Now I need to present the selected MenuItem
to ContentPresenter
- and this is where I have the problem with.
This is my current code
XAML
<!--Menu-->
<Menu x:Name="NavigationTreeView" IsMainMenu="True" ItemsSource="{Binding Navigation}" Grid.Row="0">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Path=Title}" />
<Setter Property="Template" Value="{StaticResource VsMenuTop}" />
</Style>
<HierarchicalDataTemplate DataType="{x:Type nav:PageViewModel}" ItemsSource="{Binding Children}" />
</Menu.Resources>
</Menu>
<!--Content-->
<ContentPresenter Grid.Row="1" Content="{Binding ElementName=NavigationTreeView, Path=SelectedItem.Content}" />
This will not work, because I am not able to bind it to Menu using this line here: SelectedItem.Content
- and the Menu does not have a property SelectedItem
.
My alternative option would be to use ListView
, because it contains a property SelectedItem
. But preferably I'd like to use Menu
.
How can I get the selected item from the Hierarchical Data Template?
The Menu
control does not have a notion of a selected item, as its MenuItem
s are essentially buttons that should execute an action when clicked. You can set the IsCheckable
property to true
to be a able to check menu items like a CheckBox
, but I guess that does not fit your use-case here.
Since you probably want to display different content depending on which menu item was clicked, you could use a command, that is executed when you click a MenuItem
and gets the corresponding view model as parameter and sets a property on a view model that can be bound by the ContentPresenter
.
Introduce a Selected
property of type PageViewModel
and a Select
command in your main view model that contains the Navigation
porperty. The command sets the Selected
property.
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
Selected = new RelayCommand<PageViewModel>(pageViewModel => Selected = pageViewModel);
// ...other code.
}
public DelegateCommand<PageViewModel> Select { get; }
private PageViewModel _selected;
public PageViewModel Selected
{
get => _selected;
private set
{
if (_selected == value)
return;
_selected = value;
OnPropertyChanged();
}
}
// ...other code.
}
You can replace the RelayCommand
by the command implementation that you have at your disposal.
Then, you can adapt your style to bind the command on the main view model (either using ElementName
or RelativeSource
to the data context) and to bind the view model of the clicked MenuItem
as command parameter. This way it is passed to the command, which sets it as Selected
.
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Path=Title}" />
<Setter Property="Template" Value="{StaticResource VsMenuTop}" />
<Setter Property="Command" Value="{Binding DataContext.Select, ElementName=NavigationTreeView}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
Bind the Content
of your ContentPresenter
to the Selected
property on the main view model.
<ContentPresenter Grid.Row="1" Content="{Binding ElementName=NavigationTreeView, Path=Selected}" />