Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF listbox with contextmenu canexecute not called until something selected

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!

like image 244
DanW Avatar asked Feb 16 '15 14:02

DanW


2 Answers

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>
like image 145
Vinkal Avatar answered Nov 09 '22 22:11

Vinkal


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!!!

like image 33
Eric Ouellet Avatar answered Nov 10 '22 00:11

Eric Ouellet