WPF ICommand 相当于 Click -= Button_Click
WPF ICommand equivalent of Click -= Button_Click
对于 WPF ICommand,事件处理程序 -= 的等价物是什么?
我们有一个用户控制按钮,用于管理提交购买请求的功能,但需要用户先在工作站上注册。
WinForms 的用法是:
this.Click -= this.btnRegister_Click;
this.Click -= this.btnSubmit_Click;
WPF 中的现代化版本具有单个 ICommand 属性 和典型的声明。
public ICommand ClickCommand { get; set; }
....
ClickCommand = new DelegateCommand(RegisterClicked);
一旦用户注册,用户控件将重新初始化到提交过程。存在一个简单的触发器来检查按钮的状态。从视觉上看,触发器工作正常,这表明程序正在处理初始化代码块。
<Label.Style>
<Style TargetType="Label" BasedOn="{StaticResource ButtonLabel}">
<Setter Property="Content" Value="Approvals"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ButtonState}" Value="{x:Static locdata:WorkflowButtonState.Register}">
<Setter Property="Content" Value="Register"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=ButtonState}" Value="{x:Static locdata:WorkflowButtonState.Submit}">
<Setter Property="Content" Value="Submit"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
初始化代码块
private void InitRegister()
{
ButtonState = WorkflowButtonState.Register;
ClickCommand = new DelegateCommand(RegisterClicked);
}
private void InitSubmit()
{
ButtonState = WorkflowButtonState.Submit;
ClickCommand = new DelegateCommand(SubmitClicked);
}
private void Reset()
{
ClickCommand = null;
ButtonState = WorkflowButtonState.Default;
}
调试模式清楚地表明 ClickCommand 执行方法是 SubmitClicked,但是 RegisterClicked 事件仍然会触发。这仅在注册过程首先发生时发生,因为用户之前未登录工作站。
复制 EventHandler -= 行为需要什么,因为设置 ClickCommand = null 没有帮助?
更新
到目前为止,我的应用程序中的 ICommand 用法是单次使用。正如 Andy 的回答所述,它应该被视为 属性 并引发 属性 changed 事件。
更新后的 ICommand 声明,正如我为属性所做的那样,所有需要更改的地方。
private ICommand _clickCommand;
public ICommand ClickCommand { get => _clickCommand; set { _clickCommand = value; OnPropertyChanged(); } }
据我了解,您有一个按钮,其内容和功能会根据某些条件发生变化。您还为每个按钮的功能实现了 2 个命令;然而,其中一个命令在它不应该采取行动时仍然采取行动,对吗?
在这种情况下,我认为您可以修改 DelegteCommand(它继承了 IComamnd 接口)以向其添加 CanExecute() 委托。稍后,当您为命令创建新实例时,只需添加一个委托来确定该命令是否可执行。像这样:
public class DelegateCommand: ICommand
{
private Action _execute;
private Func<bool> _canExecute;
public DelegateCommand(Action execute, Func<bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute()
{
if (_canExecute != null && _execute)
return _canExecute.Invoke();
}
public void Execute()
{
if (CanExecute())
_execute.Invoke();
}
}
public ICommand RegisterClicked { get; set; }
RegisterClicked = new DelegateCommand(RegisterInvoke, RegisterCanExecute);
private void RegisterInvoke()
{
}
private bool RegisterCanExecute()
{
// determine when the execution of Register and be perform here
}
这里要考虑两件事:
按钮上的命令不是事件,它是依赖项 属性。
https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.buttonbase.command?view=netcore-3.1
你如何告诉视图中的依赖项 属性 它的值应该从视图模型中再次读取?
你通知属性改变了。
一些示例标记:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
<Button Command="{Binding TestCommand}" Content="Test"/>
<Button Command="{Binding ChangeCommand}" Content="Change Test Command"/>
</StackPanel>
</Window>
有一个测试命令将作为一个命令开始,然后当您单击“更改测试命令”按钮时,它将更改为另一个命令。
我的视图模型:
public class MainWindowViewModel : BindableBase
{
private DelegateCommand testCommand;
public DelegateCommand TestCommand
{
get => testCommand;
set { testCommand = value; RaisePropertyChanged(); }
}
public DelegateCommand ChangeCommand { get; set; }
public MainWindowViewModel()
{
TestCommand = new DelegateCommand(() => { MessageBox.Show("Original Command"); });
ChangeCommand = new DelegateCommand(() =>
{
TestCommand = new DelegateCommand(() => { MessageBox.Show("Replacement is used"); });
});
}
}
调用 changecommand 时,testcommand 的 delegatecommand 将替换为新命令。
和.
属性 更改被调用。
这告诉 UI 去获取新命令。
如果我然后删除 raise 属性 从 TestCommand 更改:
public DelegateCommand TestCommand
{
get => testCommand;
set { testCommand = value; }
}
单击更改按钮,然后单击我的测试按钮,我得到了原始命令运行。这是你看到的问题。因为你的命令看起来像:
public ICommand ClickCommand { get; set; }
没有加注 属性 已更改。
注:
如果我的更改命令是:
ChangeCommand = new DelegateCommand(() =>
{
TestCommand = null;
});
然后点击更改按钮,点击测试按钮,没有任何反应。它的命令现在为空。
对于 WPF ICommand,事件处理程序 -= 的等价物是什么?
我们有一个用户控制按钮,用于管理提交购买请求的功能,但需要用户先在工作站上注册。
WinForms 的用法是:
this.Click -= this.btnRegister_Click;
this.Click -= this.btnSubmit_Click;
WPF 中的现代化版本具有单个 ICommand 属性 和典型的声明。
public ICommand ClickCommand { get; set; }
....
ClickCommand = new DelegateCommand(RegisterClicked);
一旦用户注册,用户控件将重新初始化到提交过程。存在一个简单的触发器来检查按钮的状态。从视觉上看,触发器工作正常,这表明程序正在处理初始化代码块。
<Label.Style>
<Style TargetType="Label" BasedOn="{StaticResource ButtonLabel}">
<Setter Property="Content" Value="Approvals"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ButtonState}" Value="{x:Static locdata:WorkflowButtonState.Register}">
<Setter Property="Content" Value="Register"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=ButtonState}" Value="{x:Static locdata:WorkflowButtonState.Submit}">
<Setter Property="Content" Value="Submit"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
初始化代码块
private void InitRegister()
{
ButtonState = WorkflowButtonState.Register;
ClickCommand = new DelegateCommand(RegisterClicked);
}
private void InitSubmit()
{
ButtonState = WorkflowButtonState.Submit;
ClickCommand = new DelegateCommand(SubmitClicked);
}
private void Reset()
{
ClickCommand = null;
ButtonState = WorkflowButtonState.Default;
}
调试模式清楚地表明 ClickCommand 执行方法是 SubmitClicked,但是 RegisterClicked 事件仍然会触发。这仅在注册过程首先发生时发生,因为用户之前未登录工作站。
复制 EventHandler -= 行为需要什么,因为设置 ClickCommand = null 没有帮助?
更新
到目前为止,我的应用程序中的 ICommand 用法是单次使用。正如 Andy 的回答所述,它应该被视为 属性 并引发 属性 changed 事件。
更新后的 ICommand 声明,正如我为属性所做的那样,所有需要更改的地方。
private ICommand _clickCommand;
public ICommand ClickCommand { get => _clickCommand; set { _clickCommand = value; OnPropertyChanged(); } }
据我了解,您有一个按钮,其内容和功能会根据某些条件发生变化。您还为每个按钮的功能实现了 2 个命令;然而,其中一个命令在它不应该采取行动时仍然采取行动,对吗?
在这种情况下,我认为您可以修改 DelegteCommand(它继承了 IComamnd 接口)以向其添加 CanExecute() 委托。稍后,当您为命令创建新实例时,只需添加一个委托来确定该命令是否可执行。像这样:
public class DelegateCommand: ICommand { private Action _execute; private Func<bool> _canExecute; public DelegateCommand(Action execute, Func<bool> canExecute) { _execute = execute; _canExecute = canExecute; } public bool CanExecute() { if (_canExecute != null && _execute) return _canExecute.Invoke(); } public void Execute() { if (CanExecute()) _execute.Invoke(); } }
public ICommand RegisterClicked { get; set; } RegisterClicked = new DelegateCommand(RegisterInvoke, RegisterCanExecute); private void RegisterInvoke() { } private bool RegisterCanExecute() { // determine when the execution of Register and be perform here }
这里要考虑两件事:
按钮上的命令不是事件,它是依赖项 属性。 https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.buttonbase.command?view=netcore-3.1
你如何告诉视图中的依赖项 属性 它的值应该从视图模型中再次读取?
你通知属性改变了。
一些示例标记:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
<Button Command="{Binding TestCommand}" Content="Test"/>
<Button Command="{Binding ChangeCommand}" Content="Change Test Command"/>
</StackPanel>
</Window>
有一个测试命令将作为一个命令开始,然后当您单击“更改测试命令”按钮时,它将更改为另一个命令。
我的视图模型:
public class MainWindowViewModel : BindableBase
{
private DelegateCommand testCommand;
public DelegateCommand TestCommand
{
get => testCommand;
set { testCommand = value; RaisePropertyChanged(); }
}
public DelegateCommand ChangeCommand { get; set; }
public MainWindowViewModel()
{
TestCommand = new DelegateCommand(() => { MessageBox.Show("Original Command"); });
ChangeCommand = new DelegateCommand(() =>
{
TestCommand = new DelegateCommand(() => { MessageBox.Show("Replacement is used"); });
});
}
}
调用 changecommand 时,testcommand 的 delegatecommand 将替换为新命令。
和.
属性 更改被调用。
这告诉 UI 去获取新命令。
如果我然后删除 raise 属性 从 TestCommand 更改:
public DelegateCommand TestCommand
{
get => testCommand;
set { testCommand = value; }
}
单击更改按钮,然后单击我的测试按钮,我得到了原始命令运行。这是你看到的问题。因为你的命令看起来像:
public ICommand ClickCommand { get; set; }
没有加注 属性 已更改。
注:
如果我的更改命令是:
ChangeCommand = new DelegateCommand(() =>
{
TestCommand = null;
});
然后点击更改按钮,点击测试按钮,没有任何反应。它的命令现在为空。