I want to bind my ListView's ContextMenu to an ItemSource 'GetContextMenu' specified in the ViewModel which the entire UserControl is using for it's DataContext. I have tried specifying it in multiple different ways, including using the RelativeSource
and currently trying to bind by name to either the ClientsListView
or ClientsTab
. All which I have tried failed. The specific error I am getting is:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=ClientsListView'. BindingExpression:Path=DataContext.GetContextMenu; DataItem=null; target element is 'ContextMenu' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
It's worth mentioning that my binding for the SelectionChanged event that you'll see below in the XAML works just fine, so I really have no clue what's going on.
<UserControl x:Class="ServerUI.Views.MainWindowViews.ClientsTabView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Name="ClientsTab">
<Grid>
<ListView x:Name="ClientsListView" ItemsSource="{Binding ClientViewModels}">
<ListView.ContextMenu>
<ContextMenu ItemsSource="{Binding ElementName=ClientsListView, Path=DataContext.GetContextMenu}"/>
</ListView.ContextMenu>
<ListView.View>
<GridView>
<GridViewColumn x:Name="OnlineStatusHeader" Header="Online Status" DisplayMemberBinding="{Binding Online}" />
<GridViewColumn x:Name="NameHeader" Header="Computer/User" DisplayMemberBinding="{Binding ComputerUser}" />
</GridView>
</ListView.View>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding UpdateSelectedClientsCommand}" CommandParameter="{Binding ElementName=ClientsListView, Path=SelectedItems}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
</Grid>
</UserControl>
namespace ServerUI.ViewModels.MainWindowViewModels
{
public class ClientsTabViewModel : ObservableObject
{
public ObservableCollection<ClientViewModel> ClientViewModels { get; } = new();
public IEnumerable<ClientViewModel> SelectedClients { get; set; }
public ICommand CloseConnectionCommand
=> new RelayCommand(() => ExecuteCloseConnectionCommand());
public ICommand ConfigureClientCommand
=> new RelayCommand(() => ExecuteConfigureClientCommand());
public ICommand UpdateSelectedClientsCommand
=> new RelayCommand<object>(e => ExecuteUpdateSelectedClientsCommand(e));
public ICommand OpenServerModuleCommand
=> new RelayCommand<object>(e => ExecuteOpenServerModuleCommand(e));
public List<FrameworkElement> GetContextMenu
{
get
{
List<FrameworkElement> menuItems = new();
menuItems.Add(new MenuItem() { Header = "Configure Client", Command = ConfigureClientCommand });
menuItems.Add(new MenuItem() { Header = "Close Connection", Command = CloseConnectionCommand });
menuItems.Add(new Separator());
foreach(var client in SelectedClients)
{
foreach(var module in client.Client.LoadedServerModules)
{
menuItems.Add(new MenuItem() { Header = module.ModuleName, Command = OpenServerModuleCommand, CommandParameter = module });
}
}
return menuItems;
}
}
void ExecuteUpdateSelectedClientsCommand(object param)
{
System.Collections.IList items = (System.Collections.IList)param;
SelectedClients = items.Cast<ClientViewModel>();
}
CodePudding user response:
You can try like this:
<ListView Tag="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
<ListView.ContextMenu>
<ContextMenu
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}"
ItemsSource="{Binding GetContextMenu}"
/>
</ListView.ContextMenu>
</ListView>