选择组合框中的值时如何触发按钮命令

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

我有一个组合框和一个按钮。我希望每当我从中选择一项时,按钮都不会被禁用

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>

命令:

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();
        }
    }

我已经调试了它并检查了我用于绑定的变量的值 SelectedPort 并且它有一个值但是不知何故,我的按钮的 CommandParameter 未检测到所以 CanExecute 方法是不 运行 正确。我错过了什么吗?

更新

预计:

结果:

更新 2

Setter 上的断点:

CanExecute 上的断点

您的 OpenPortCommand 缺少实际引发 CanExecuteChanged 事件的可能性。我建议如下:

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
}

在您的 ViewModel 中 SelectedPort 属性:

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

在 XAML 中,我简化了 ComboBox.SelectedItem 属性 的绑定,如下所示:

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

您的代码有两个问题:

  1. 最关键的是引发 INotifyPropertyChanged.PropertyChanged 事件的方式,它实际上会阻止您的代码正确执行。您当前没有传递 属性 名称,而是传递支持字段的名称。

而不是

OnPropertyChanged(nameof(portSelected));

一定是

OnPropertyChanged(nameof(SelectedPort));

为了简化事件调用,您的调用者应该使用 CallerMemberName 属性:

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

然后调用不带任何参数的方法OnPropertyChanged,因为属性名称现在会被自动检测到:

OnPropertyChanged();
  1. 方法RaiseExecuteChangedCommand是多余的。 ICommand 事件的 CanExecuteChanged 实现将事件处理程序委托给 CommandManager.RequerySuggested 事件(这是正确的)。 CommandManager.RequerySuggested 事件由 WPF 框架自动引发,并且由于事件委托,框架还将引发 ICommand.CanExecuteChanged(因为您将所有 ICommand.CanExecuteChanged 处理程序附加到 CommandManager.RequerySuggested 事件,使用CommandManager.RequerySuggested += value)。如果您不希望框架为您引发 CanExecuteChanged 事件,您必须删除 CommandManager.RequerySuggested += value 部分,即不要将客户端处理程序附加到 CommandManager.RequerySuggested 事件。因为现在您将从 RaiseExecuteChangedCommand 方法中明确提出它。所以你不能两者兼得。明确地提高 ICommand.CanExecuteChanged 或让 CommandManager 为你做。