Home > database >  How to trigger button command whenever my value from combobox is selected
How to trigger button command whenever my value from combobox is selected

Time:11-15

I have a combo box and a button. I want whenever I choose 1 of the item from it, the button is not disabled

XAML :

<Border Style="{StaticResource borderMain}"
                Grid.Row="7"
                Grid.Column="0">
            <ComboBox   ItemsSource="{Binding Source={StaticResource portNames}}"
                        SelectedItem="{Binding SelectedPort, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                        x:Name="Port_Selector" Grid.Column="0"
                        Text="Port Selector" Background="White"/>
        </Border>

        <Border Style="{StaticResource borderMain}"
                Grid.Column="1"
                Grid.Row="7">
            <Button Content="Connect"
                    Command="{Binding OpenPortCommand}"
                    CommandParameter="{Binding SelectedPort}"
                    Style="{StaticResource buttonMain}"
                    Margin="5"/>
        </Border>

Command :

public class OpenPortCommand : ICommand
    {
        public OpenPortVM OpenPortVM{ get; set; }

        public OpenPortCommand(OpenPortVM OpenPortVM)
        {
            this.OpenPortVM = OpenPortVM;
        }

        public event EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested  = value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public bool CanExecute(object? parameter)
        {
            string? portCom = parameter as string;

            if (!string.IsNullOrEmpty(portCom))
                return true;
            return false;
        }

        public void Execute(object? parameter)
        {
            OpenPortVM.ConnectPort();
        }
    }

I already debug it and check the value from variable that I using for binding SelectedPort and it has a value on it but somehow, the CommandParameter for my button not detected so CanExecute method is not run properly. Am i missing something?

Update

expected : enter image description here

result: enter image description here

Update 2

breakpoint on Setter: enter image description here

breakpoint on CanExecute enter image description here

CodePudding user response:

Your OpenPortCommand is missing the possibility to actually raise the CanExecuteChanged event. I suggest following:

public class OpenPortCommand : ICommand
{
    public OpenPortVM OpenPortVM{ get; set; }

    public OpenPortCommand(OpenPortVM OpenPortVM)
    {
        this.OpenPortVM = OpenPortVM;
    }

    // Leave this event as is, don't do an explicit implementation        
    public event EventHandler? CanExecuteChanged;

    public void RaiseCanExecuteChange() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    
    public bool CanExecute(object? parameter) => !string.IsNullOrEmpty(parameter as string);

    // rest ommitted
}

In your ViewModel's SelectedPort property:

public string SelectedPort
{
    get => _selectedPort;
    set
    {
        _selectedPort = value;
        // call your OnPropertyChanged( ... );
        OpenPortCommand?.RaiseCanExecuteChanged();
    }
}

In the XAML I've simplified the binding of the ComboBox.SelectedItem property like this:

<ComboBox ... SelectedItem="{Binding SelectedPort}"/>

CodePudding user response:

Your code has two issues:

  1. The most critical one, which actually prevents your code from executing properly, is the way you raise the INotifyPropertyChanged.PropertyChanged event. Instead of passing in the property name, you currently pass in the name of the backing field.

Instead of

OnPropertyChanged(nameof(portSelected));

it must be

OnPropertyChanged(nameof(SelectedPort));

To simplify the event invocation your invocator should use the CallerMemberName attribute:

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
  => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName);

Then call the method OnPropertyChanged without any parameter, as the property name is now automatically detected:

OnPropertyChanged();
  1. The method RaiseExecuteChangedCommand is redundant. Your ICommand implementation of the CanExecuteChanged event delegates event handlers to the CommandManager.RequerySuggested event (which is correct). The CommandManager.RequerySuggested event is raised automatically by the WPF framework and because of the event delegation the framework will also raise ICommand.CanExecuteChanged (because you attach all ICommand.CanExecuteChanged handlers to the CommandManager.RequerySuggested event using CommandManager.RequerySuggested = value). If you don't want the framework to raise the CanExecuteChanged event for you you must remove the CommandManager.RequerySuggested = value part i.e. don't attach client handlers to the CommandManager.RequerySuggested event. Because now you will raise it explcitly from your RaiseExecuteChangedCommand method. So you can't have both. Either raise ICommand.CanExecuteChanged explicitly or let the CommandManager do it for you.
  • Related