处理 this question ,我正在尝试制作一个带有两个按钮的用户界面。按下一个按钮会禁用自身并启用另一个按钮。我正在使用 MVVM 模式和 DelegateCommand:
主窗口 XAML
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel>
<Rectangle Height="100"></Rectangle>
<Button Command="{Binding StartServer}" >Start Server</Button>
<Button Command="{Binding StopServer}" >Stop Server</Button>
</StackPanel>
</Window>
查看型号
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
PropertyChanged += PropertyChangedHandler;
}
private void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(ServerIsRunning):
StartServer.RaiseCanExecuteChanged();
StopServer.RaiseCanExecuteChanged();
break;
}
}
private bool m_serverIsRunning;
public bool ServerIsRunning
{
get => m_serverIsRunning;
set
{
m_serverIsRunning = value;
RaisePropertyChanged(nameof(ServerIsRunning));
}
}
public DelegateCommand<object> StartServer => new DelegateCommand<object>(
context =>
{
ServerIsRunning = true;
}, e => !ServerIsRunning);
public DelegateCommand<object> StopServer => new DelegateCommand<object>(
context =>
{
ServerIsRunning = false;
}, e => ServerIsRunning);
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
委托(delegate)指挥
public class DelegateCommand<T> : ICommand
{
private readonly Action<object> ExecuteAction;
private readonly Func<object, bool> CanExecuteFunc;
public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecuteFunc)
{
ExecuteAction = executeAction;
CanExecuteFunc = canExecuteFunc;
}
public bool CanExecute(object parameter)
{
return CanExecuteFunc == null || CanExecuteFunc(parameter);
}
public void Execute(object parameter)
{
ExecuteAction(parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
问题是,当我点击
Start Server
按钮,按钮的 IsEnabled 状态在 UI 中不会改变。我是否不正确地使用了 DelegateCommand?
最佳答案
您的代码的问题是您的命令属性使用表达式主体,每次有人获取属性时都会执行它们,这意味着每次您要求 StartServer
命令它会给你一个新的实例。您可以通过在 getter 上放置一个断点来自己检查这一点,您会看到它会被多次命中,并且总是会创建一个新的委托(delegate)命令实例。
为了使您的代码工作,您必须使用每个命令的一个实例,您可以通过使用支持字段或只读属性并将其设置在构造函数中来实现这一点:
public MainWindowViewModel()
{
PropertyChanged += PropertyChangedHandler;
StartServer = new DelegateCommand<object>(
context =>
{
ServerIsRunning = true;
}, e => !ServerIsRunning);
StopServer = new DelegateCommand<object>(
context => {
ServerIsRunning = false;
}, e => ServerIsRunning);
}
public DelegateCommand<object> StartServer
{ get; }
public DelegateCommand<object> StopServer
{ get; }
UI 将只收听
CanExecuteChanged
绑定(bind)到的命令实例引发的事件(通过 Command={Binding ...}
。您的代码也可以简化,因为您可以调用
RaiseCanExecuteChanged
直接而不是提出 PropertyChangedEvent
并自己听: public bool ServerIsRunning
{
get => m_serverIsRunning;
set
{
m_serverIsRunning = value;
StartServer.RaiseCanExecuteChanged();
StopServer.RaiseCanExecuteChanged();
}
}
关于c# - 如何让 DelegateCommand CanExecute 使用 MVVM 更新 WPF UI?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58437185/