WPF listbox with contextmenu canexecute not called until something selected
Asked Answered
B

2

5

I have a ListBox the ItemTemplate bound to an ObservableCollection of my items. At the moment, I'm trying to implement Cut/Copy/Paste/SelectAll (To keep it short, I'll just show selectall here...)

<UserControl.CommandBindings>
    <CommandBinding Command="SelectAll" CanExecute="SelectAll_CanExecute" Executed="SelectAll_Executed"/>
</UserControl.CommandBindings>
<ListBox x:Name="listbox" 
         ItemsSource="{Binding}" 
         Background="Transparent" 
         SelectionMode="Extended"
         ScrollViewer.VerticalScrollBarVisibility="Auto">
    <ListBox.ContextMenu>
        <ContextMenu>
            <MenuItem Command="SelectAll" />
        </ContextMenu>
    </ListBox.ContextMenu>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Background="Transparent">
                <CheckBox Name="cbEnabled" IsChecked="{Binding Enabled, Mode=TwoWay}" Margin="0,2,0,0"/>
                <TextBlock Text="{Binding Name}" Padding="5,0,0,0"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>

this is the codebehind for canexecute:

    private void SelectAll_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = listbox.Items.Count > 0;
        e.Handled = true;
    }

when I first run the program and right-click in the listbox, the "Select All" context menu is always disabled (and SelectAll_CanExecute is never called) until I select something. Is there any way to get this to work like it seems it should? (and without either auto-selecting the first item or making the user have to do it)

Thanks!

Brandon answered 16/2, 2015 at 14:16 Comment(0)
A
8

This is a known bug as mentioned here. If there is no focused element in the window's main focus scope, the CanExecute routing will stop at the ContextMenu, so it will not reach to the CommandBinding on the Window, one workaround is to bind MenuItem's CommandTarget to the main window, as following code demonstrates:

<ListBox.ContextMenu>
    <ContextMenu>
        <MenuItem Command="SelectAll"
                    CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}">
        </MenuItem>
    </ContextMenu>
</ListBox.ContextMenu>

Below is the complete code:

<UserControl x:Class="ListBoxStyle.ListBoxUserControl"
             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" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.CommandBindings>
        <CommandBinding Command="SelectAll" CanExecute="SelectAll_CanExecute" Executed="SelectAll_Executed"/>
    </UserControl.CommandBindings>
    <Grid>
        <ListBox x:Name="listbox" 
             Background="Transparent" 
             SelectionMode="Extended"
             ScrollViewer.VerticalScrollBarVisibility="Auto">
            <ListBox.ContextMenu>
                <ContextMenu>
                    <MenuItem Command="SelectAll"
                    CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}">
                    </MenuItem>
                </ContextMenu>
            </ListBox.ContextMenu>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Background="Transparent">
                        <CheckBox Name="cbEnabled" IsChecked="{Binding Enabled, Mode=TwoWay}" Margin="0,2,0,0"/>
                        <TextBlock Text="{Binding}" Padding="5,0,0,0"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>
Abad answered 16/2, 2015 at 15:28 Comment(0)
A
2

Just as reference... I had the same problem using a UserControl. I had too many MenuItems and I also wanted to make sure to not forget that line on new MenuItem. Then I went for a Style as this:

 <UserControl.Resources>
        <Style TargetType="MenuItem">
            <Setter Property="CommandTarget" Value="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"></Setter>    
        </Style>

...

Please, if you ever want to give me a thumbs up... give it to Vinkal who really identify the problem and found the solution. This guy is a genious!!!

Antitank answered 14/6, 2016 at 15:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.