Reputation: 25
I have a context Menu on my ListView with FilterBy as one of the Menu items in it. This Menu Item is bound to an observable collection of Menuitems(_childFilterMenuitems coming from an object ContextMenuclass). I want to retrieve the selected item from the submenu of the FilterBy MenuItem and bind to a property called SelectedItem in my MainView Model.
Everything works fine with the relay command and populating the items. However I cannot access the selected item in my main view model. I tried binding the SelectedItem Property(From the MainViewModel) to the Tag Property on the Menuitem. I cannot get this one to work. I read through several blogs about visual tree and its datacontext and Placement target.Tag, I cant figure out where to include the tag property. Please guide me so that I can establish proper bindings. I am fairly new and this website has been of great help.
<ListView.ContextMenu >
<ContextMenu Name="menuListContext" >
<MenuItem Header="Reset" Name="menuReset" Command="{Binding ResetCmd}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext}" >
<MenuItem Header="Filter By" ItemsSource="{Binding ChildFilterMenuItems}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" <Setter Property="Header" Value="{Binding Header, Mode=TwoWay}" />
<Setter Property="ItemsSource" Value="{Binding Path= ChildFilterMenuItems}"/>
<Setter Property= "Command" Value= "{Binding DataContext.FilterByCmd, RelativeSource ={RelativeSource AncestorType={x:Type MenuItem}}}"/>
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=DataContext}" />
<Setter Property="Tag" Value= "{Binding DataContext.SelectedItem, RelativeSource ={RelativeSource AncestorType=MenuItem}}"/>
</Style>
</MenuItem.ItemContainerStyle>
</ContextMenu >
</ListView.ContextMenu>
public class MainViewModel : ViewModelBase, INotifyPropertyChanged
{
/// <summary>
/// The menu item selected from FilterBy Menu.
/// </summary>
public string SelectedItem {get;set;}
public ObservableCollection<ContextMenuClass> ChildFilterMenuItems
{
get
{
return _childFilterMenuItems;
}
set
{
_childFilterMenuItems = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("ChildFilterMenuItems");
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{ _childFilterMenuItems = new ObservableCollection<ContextMenuClass>();
//populates the menuitems for FilterBy
PopulateFilterBy
FilterByCmd = new RelayCommand(() => FilterByMenu(), () => true);
}
private void FilterByMenu()
{
try
{
string MenuName = GetExactName(SelectedItem);
}
public class ContextMenuClass : INotifyPropertyChanged
{
#region Fields
private ObservableCollection<ContextMenuClass> _filterItems;
private string _header;
private bool _isEnabled;
#endregion
#region Properties
/// <summary>
/// Gets or sets the header.
/// </summary>
/// <value>The header.</value>
public string Header
{
get
{
return _header;
}
set
{
_header = value; OnPropertyChanged("Header");
}
}
public bool IsEnabled
{
get
{
return _isEnabled;
}
set
{
_isEnabled = value; OnPropertyChanged("IsEnabled");
}
}
public ObservableCollection<ContextMenuClass> ChildFilterMenuItems
{
get
{
return (_filterItems = _filterItems ??
new ObservableCollection<ContextMenuClass>());
}
}
/// <summary>
/// Gets or sets the SelectedItem.
/// </summary>
/// <value>The header.</value>
public object SelectedMenuItem
{
get
{
return _currentItem = Header;
}
set
{
_currentItem = value; OnPropertyChanged("SelectedMenuItem");
}
}
#endregion
#region INotifyPropertyChanged
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private object _currentItem;
/// <summary>
/// Safely raises the PropertyChanged event.
/// </summary>
/// <param name="property">The property name.</param>
protected void OnPropertyChanged(string Property)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(Property));
}
}
#endregion
Upvotes: 1
Views: 5215
Reputation: 390
I hope this can help:
<MenuItem ItemsSource="{Binding MyList}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding RelativeSource={ RelativeSource FindAncestor, AncestorType={ x:Type Window}}, Path= DataContext.MyListItemCommand}"/>
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="IsCheckable" Value="True"/>
<Setter Property="IsChecked" Value="{Binding IsChecked, Mode=OneWay}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
The list is therefore MyList
and the command is MyListItemCommand
with command parameter being an actual item from the MyList
(because Value="{Binding}"
returns the actual list item).
This also means you would need to write MyListItemCommand
(which implements ICommand
interface) class which would manage which list item is currently checked (please note that also multiple items can be checked at the same time in this case).
Upvotes: 0
Reputation: 25623
If you want to know which ContextMenuClass
corresponds to the menu item that was clicked, you should modify your CommandParameter
setter as follows:
<Setter Property="CommandParameter"
Value="{Binding}" />
And then change your FilterByCmd
from a RelayCommand
to a RelayCommand<ContextMenuClass>
:
FilterByCmd = new RelayCommand<ContextMenuClass>(FilterByMenu, _ => true);
And add a ContextMenuClass
parameter to your FilterByMenu
method:
private void FilterByMenu(ContextMenuClass selectedItem)
{
// ...
}
Upvotes: 1