Search code examples
c#wpflistboxicommand

CanExecute true iff an item in a listBox is selected


I am relatively new to WPF, so this may be trivial, but I couldn't figure this out. Let's say I have a ListBox and a Button. The button is binded to a command that does something on the selected item of the list.

Example:

<ListBox Name="List" ItemsSource="{Binding Items}" />
<Button Content="Show!" Command="{Binding ShowCommand}" CommandParameter="{Binding ElementName=List, Path=SelectedItem}"/>

I want the button to be disabled if and only if no item is selected, and I ideally want to do it via the ShowCommand.CanExecute function. I tried checking for null parameter and it worked but if only checked once in the beginning. If I select an item the button is still disable.

I tried the suggestion here: WPF CommandParameter binding not updating but it simply didn't work... (same problem) Am I doing something wrong?

How do I make him recall canExecute when I select an item on the list? .


Solution

  • You can achieve that easily using a DataTrigger

    <ListBox Name="List" ItemsSource="{Binding Items}" />
    <Button Content="Show!" Command="{Binding ShowCommand}" CommandParameter="{Binding ElementName=List, Path=SelectedItem}">
            <Button.Style>
                <Style TargetType="Button">
                    <Setter Property="IsEnabled" Value="True" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding SelectedItem,ElementName=List}" Value="{x:Null}">
                            <Setter Property="IsEnabled" Value="False"></Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
    </Button>
    

    Update

    The reason why ou couldn't achieve that using CanExecute is most likely because the CanExecuteChanged event wasn't raised, to fix that you can take advantage of CommandManager.RequerySuggested by linking this event to your command's CanExecuteChanged event[1].

    Here a basic implementation and use of an ICommand in your case:

    public class Command : ICommand
    {
        private readonly Action<object> _action;
        public Command(Action<object> action)
        {
            _action = action;
        }
        public bool CanExecute(object parameter)
        {
            //the selected item of the ListBox is passed as parameter
            return parameter != null;
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        public void Execute(object parameter)
        {
            //the selected item of the ListBox is passed as parameter
            _action(parameter);
        }
    }
    

    The command defined in your VM should look something like that:

     private ICommand _showCommand;
        public ICommand ShowCommand => _showCommand ?? (_showCommand = new Command(ButtonClickAction));
    
        public void ButtonClickAction(object parameter)
        {
           //Your action
        }
    

    You might also want to take a look at Routed Commands to avoid raising the CanExecuteChanged event yourself.