Caleb W.
Caleb W.

Reputation: 129

Bind ListViewItem ContextMenu MenuItem Command to ViewModel of the ListView's ItemsSource

I have a ListView of Expanders. Each Expander (representing a database table) will have items under it, in another ListView. I want to Right-Click and have an "Edit" option on the innermost items, which represent records in the corresponding database table.

There is an ICommand named 'Edit' in my MainEditorViewModel. The Datacontext in which this command resides is the same as that of the outermost ListView named "TestGroupsListView"

Here is the XAML markup for the ListView of Expanders. The outermost ListView I've named for referencing in the binding via ElementName for the MenuItem's Binding:

        <ListView Name="TestGroupsListView" ItemsSource="{Binding TestGroups}" Grid.Row="1">
            <ListView.ItemTemplate>
                <DataTemplate>


                    <Expander Style="{StaticResource MaterialDesignExpander}"  >
                        <Expander.Header>
                            <Grid MaxHeight="50">
                                <TextBlock Text="{Binding Name}"/>
                                <Grid.ContextMenu>

                                    <ContextMenu>
                                        <MenuItem Header="Add..." Command="{Binding Add}"/>
                                    </ContextMenu>
                                </Grid.ContextMenu>
                            </Grid>

                        </Expander.Header>

                        <ListView ItemsSource="{Binding Records}" Style="{StaticResource MaterialDesignListView}" Margin="30 0 0 0">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ContextMenu>
                                            <ContextMenu>
                                                <MenuItem Header="Edit" 
                                                          Command="{Binding ElementName=TestGroupsListView, Path=DataContext.Edit}"
                                                          CommandParameter="{Binding }"/>
                                            </ContextMenu>
                                        </Grid.ContextMenu>

                                        <Button Content="{Binding RecordName}" Command="{Binding ElementName=TestGroupsListView, Path=DataContext.Edit}"/>
                                        <!--<TextBlock Text="{Binding RecordName}" AllowDrop="True"/>-->
                                    </Grid>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                    </Expander>
                </DataTemplate>
            </ListView.ItemTemplate>

        </ListView>

I am able to bind a button in the DataTemplate to 'Edit' successfully, but when I attempt to bind the MenuItem's Command to 'Edit', nothing happens. Why might this be that the button command binding works using ElementName but the same binding in the ContextMenu doesn't?

Upvotes: 1

Views: 284

Answers (1)

Maksym
Maksym

Reputation: 76

I think it will be better to use the context menu globally for ListView and globally for each Child ListView. Ok, here is my solution:

<ListBox ItemsSource="{Binding Groups}">
    <ListBox.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Add..." Command="{Binding Add}"/>
        </ContextMenu>
    </ListBox.ContextMenu>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Expander Header="{Binding Name}">
                <ListView ItemsSource="{Binding Records}" SelectedItem="{Binding SelectedRecord}">
                    <ListView.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="Edit" Command="{Binding Edit}" IsEnabled="{Binding CanEdit}"/>
                        </ContextMenu>
                    </ListView.ContextMenu>
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name}"/>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </Expander>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

And for better understanding code behind:

public class GroupsVM : ViewModelBase
{
    public ICommand Add
    {
        get => null; //Command implementation
    }

    public ObservableCollection<GroupVM> Groups { get; set; } = new ObservableCollection<GroupVM>()
    {
        new GroupVM { Name = "First" },
        new GroupVM { Name = "Second" },
        new GroupVM { Name = "Third" }
    };
}

public class GroupVM : ViewModelBase
{
    private string _name;

    public string Name
    {
        get => _name;
        set { _name = value; OnPropertyChanged(); }
    }

    public ICommand Edit
    {
        get => null; //Command implementation
    }

    public bool CanEdit => SelectedRecord != null;

    public ObservableCollection<RecordVM> Records { get; set; } = new ObservableCollection<RecordVM>()
    {
        new RecordVM { Name="Record1" },
        new RecordVM { Name="Record2" },
        new RecordVM { Name="Record3" }
    };

    private RecordVM _selectedRecord = null;

    public RecordVM SelectedRecord
    {
        get => _selectedRecord;
        set
        {
            _selectedRecord = value;
            OnPropertyChanged();
            OnPropertyChanged("CanEdit");
        }
    }
}

public class RecordVM : ViewModelBase
{
    private string _name;

    public string Name
    {
        get => _name;
        set { _name = value; OnPropertyChanged(); }
    }
}

Upvotes: 1

Related Questions