I am getting this error/warning:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=DrivesListView' When I press 'Refresh', the command does not fire. I am guessing since it is a ContextMenu, I need to somehow access the parent control in the binding path and then I can use MouseDownCommand.
MouseDownCommand is located in my tab viewmodel, TabItemViewModel. My MainWindowViewModel contains a list of TabItemViewModels, and that is the source of the TabControl's items.
What I've tried:
1)
I've tried setting the ContextMenu Opened event to set the DataContext manually like this to see if it would fix the DataContext somehow:
private void ContextMenu_Opened(object sender, RoutedEventArgs e) { ContextMenu menu = sender as ContextMenu; ListView listView = menu.PlacementTarget as ListView; Grid grid = listView.Parent as Grid; TabControl tabControl = grid.Parent as TabControl; menu.DataContext = (TabItemViewModel)tabControl.SelectedItem; } The problem with this is the fact that I cannot seem to get the tabControl from the grid. Doing .Parent just returns null for some unknown reason.
2)
I also tried setting the Tag of the control, which did not work either:
<ListView Grid.Row="1" Name="DrivesListView" ItemsSource="{Binding Drives}" Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}">> <ListView.ContextMenu> <ContextMenu> <MenuItem Header="Refresh"> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseDown"> <i:InvokeCommandAction Command="{Binding Path=PlacementTarget.Tag.MouseDownCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" CommandParameter="{Binding ElementName=DrivesListView}"/> </i:EventTrigger> </i:Interaction.Triggers> </MenuItem> </ContextMenu> </ListView.ContextMenu> <ListView.Style> <Style TargetType="{x:Type ListView}" BasedOn="{StaticResource MahApps.Styles.ListView}"> <Style.Triggers> <DataTrigger Binding="{Binding DrivesListViewEnabled}" Value="true"> <Setter Property="Visibility" Value="Visible" /> </DataTrigger> <DataTrigger Binding="{Binding DrivesListViewEnabled}" Value="false"> <Setter Property="Visibility" Value="Hidden" /> </DataTrigger> </Style.Triggers> </Style> </ListView.Style> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseDoubleClick"> <i:InvokeCommandAction Command="{Binding Path=MouseDoubleClickCommand}" CommandParameter="{Binding ElementName=DrivesListView}"/> </i:EventTrigger> </i:Interaction.Triggers> <ListView.View> <GridView> <GridViewColumn x:Name="NameHeader" Header="Name" DisplayMemberBinding="{Binding Name}"/> <GridViewColumn x:Name="TypeHeader" Header="Type" DisplayMemberBinding="{Binding Type}"/> <GridViewColumn x:Name="TotalSizeHeader" Header="Total Size" DisplayMemberBinding="{Binding TotalSize}"/> <GridViewColumn x:Name="FreeSpaceHeader" Header="Free Space" DisplayMemberBinding="{Binding FreeSpace}"/> </GridView> </ListView.View> </ListView> Here is my XAML
<UserControl x:Class="FileExplorerModuleServer.Views.FileBrowserTabView" 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/expression/2010/interactivity" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--ItemsSource is bound to the 'Tabs' property on the view- model, while DisplayMemeberPath tells TabControl which property on each tab has the tab's name --> <TabControl Name="SubTabControl" Grid.Row="1" ItemsSource="{Binding Tabs}" DisplayMemberPath="Header" SelectedIndex="{Binding SelectedIndex}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <i:InvokeCommandAction Command="{Binding Path=TabChangedCommand}" CommandParameter="{Binding ElementName=SubTabControl}"/> </i:EventTrigger> </i:Interaction.Triggers> <TabControl.ContentTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBox Grid.Row="0" HorizontalAlignment="Left" Width="300" Text="{Binding Path}"> </TextBox> <mah:ProgressRing Grid.Row="1" Height="1"> <mah:ProgressRing.Style> <Style TargetType="{x:Type mah:ProgressRing}"> <Style.Triggers> <DataTrigger Binding="{Binding IsLoading}" Value="true"> <Setter Property="Visibility" Value="Visible" /> </DataTrigger> <DataTrigger Binding="{Binding IsLoading}" Value="false"> <Setter Property="Visibility" Value="Hidden" /> </DataTrigger> </Style.Triggers> </Style> </mah:ProgressRing.Style> </mah:ProgressRing> <ListView Grid.Row="1" Name="DrivesListView" ItemsSource="{Binding Drives}"> <ListView.ContextMenu> <ContextMenu Opened="ContextMenu_Opened"> <MenuItem Header="Refresh"> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseDown"> <i:InvokeCommandAction Command="{Binding Path=MouseDownCommand}" CommandParameter="{Binding ElementName=DrivesListView}"/> </i:EventTrigger> </i:Interaction.Triggers> </MenuItem> </ContextMenu> </ListView.ContextMenu> <ListView.Style> <Style TargetType="{x:Type ListView}" BasedOn="{StaticResource MahApps.Styles.ListView}"> <Style.Triggers> <DataTrigger Binding="{Binding DrivesListViewEnabled}" Value="true"> <Setter Property="Visibility" Value="Visible" /> </DataTrigger> <DataTrigger Binding="{Binding DrivesListViewEnabled}" Value="false"> <Setter Property="Visibility" Value="Hidden" /> </DataTrigger> </Style.Triggers> </Style> </ListView.Style> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseDoubleClick"> <i:InvokeCommandAction Command="{Binding Path=MouseDoubleClickCommand}" CommandParameter="{Binding ElementName=DrivesListView}"/> </i:EventTrigger> </i:Interaction.Triggers> <ListView.View> <GridView> <GridViewColumn x:Name="NameHeader" Header="Name" DisplayMemberBinding="{Binding Name}"/> <GridViewColumn x:Name="TypeHeader" Header="Type" DisplayMemberBinding="{Binding Type}"/> <GridViewColumn x:Name="TotalSizeHeader" Header="Total Size" DisplayMemberBinding="{Binding TotalSize}"/> <GridViewColumn x:Name="FreeSpaceHeader" Header="Free Space" DisplayMemberBinding="{Binding FreeSpace}"/> </GridView> </ListView.View> </ListView> Here is the ViewModel.cs
public ICommand MouseDownCommand => new RelayCommand<object>(e => MouseDown(e)); private void MouseDown(object commandParameter) { }
DataContextof the ListView is bound toTabItemViewModelandDrivesis one of its properties? Then you want to set that ListView into CommandParameter?SelectionChangedevent is a mess, just use a binding:SelectedItem="{Binding SelectedTab, Mode=OneWayToSource}"; then in the VM, add aSelectedTabproperty of the type of the objects in theTabscollection. Then move the code ofTabChangedCommandto the setter of theSelectedTabproperty.ListView.Styleis also a mess, just use a BooleanToVisibilityConverter:Visibility="{Binding DrivesListViewEnabled, Converter={StaticResource BooleanToVisibilityConverter}}"