无论何时选择ComboBox的值,如何触发按钮命令
Posted
技术标签:
【中文标题】无论何时选择ComboBox的值,如何触发按钮命令【英文标题】:How to trigger button command whenever my value from combobox is selected 【发布时间】:2021-12-25 22:44:56 【问题描述】:我有一个组合框和一个按钮。我希望每当我从中选择一项时,该按钮都不会被禁用
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
上的断点
【问题讨论】:
【参考方案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();
-
方法
RaiseExecuteChangedCommand
是多余的。您对CanExecuteChanged
事件的ICommand
实现将事件处理程序委托给CommandManager.RequerySuggested
事件(这是正确的)。 CommandManager.RequerySuggested
事件由 WPF 框架自动引发,并且由于事件委托,框架还将引发 ICommand.CanExecuteChanged
(因为您使用 CommandManager.RequerySuggested += value
将所有 ICommand.CanExecuteChanged
处理程序附加到 CommandManager.RequerySuggested
事件)。如果您不希望框架为您引发 CanExecuteChanged 事件,则必须删除 CommandManager.RequerySuggested += value
部分,即不要将客户端处理程序附加到 CommandManager.RequerySuggested
事件。因为现在您将从RaiseExecuteChangedCommand
方法中明确提出它。所以你不能两者兼得。要么明确提出 ICommand.CanExecuteChanged
,要么让 CommandManager
为你做这件事。
【讨论】:
哇,这是一个很好的解释,我会把它当作我的个人笔记。你也对,我在onPropertyChanged
上犯了一个错误,应该是SelectedPort
而不是portSelected
。谢谢那里
关于 2) 的旁注:我建议不要使用 CommandManager.RequerySuggested
,因为它是一个黑盒,它的工作原理。它可能会导致一些意外行为,并且不必要地多次调用命令的 CanExecute 方法(这也可能很昂贵,具体取决于 CanExecute 方法实际执行的操作)。参见例如How does CommandManager.RequerySuggested work?。我个人喜欢确定性的方式,仅在确实需要时才引发 CanExecuteChange 事件。
@Steeeve CommandManager 明显增加了应用程序的便利性。它剥离了处理命令状态的完整逻辑。特别是在 MVVM 场景中,您不希望视图模型参与视图逻辑。 CanExecuteChanged 事件仅用于视图(ICommandSource 实现)以控制其状态。例如,它允许按钮自行禁用。视图模型不关心这种状态。
@Steeeve 将逻辑卸载到 CommandManager 是有意义的,这样视图模型就不能跟踪影响此状态的条件。在当今的机器上,性能影响可以忽略不计。通常您不会在委托中进行昂贵的计算。还有更多关键的 UI 相关优化需要考虑。从来没有发生过我们必须更改指挥基础设施来修复与 UI 相关的性能问题。无需避开 CommandManager 为您处理命令状态。
@BionicCode 我仍然不相信 :) 我不得不调试一些,我们称之为“次优”ViewModel,文本框中的每次击键都会导致延迟和 CPU 使用率偷看,使流畅打字不可能。它是由 CommandManager 调用的大量命令在 CanExecute 中进行一些昂贵的工作引起的。但正如我所写,这只是我的意见,只是在使用 CommandManager 时要牢记的一个提示。【参考方案2】:
您的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"/>
【讨论】:
当你说“rest ommited”时,为什么我需要删除它?我的意思是,我需要调用OpenPortVM.ConnectPort();
方法来执行另一个函数
我已经省略了其余部分以保持代码简短,您当然需要其余部分。
啊,我明白了,我以为你被删除了其余部分。无论如何,我尝试了你的代码,但我仍然对CanExecuteChange
感到困惑,该代码是做什么的?因为它在当前上下文中不存在
打错了,应该是 CanExecuteChanged(事件的名称)。我会在答案中纠正它。
嘿,它仍然没有改变按钮状态。即使我已经从组合框中选择了项目,该按钮仍然禁用以上是关于无论何时选择ComboBox的值,如何触发按钮命令的主要内容,如果未能解决你的问题,请参考以下文章
用于ComboBox项目选择的事件处理程序(选定项目未必更改)