WPF MVVM AsyncCommand CanExecute 不工作
WPF MVVM AsyncCommand CanExecute not working
我正在尝试创建一个简单的 WPF 应用程序,其中包含一个填充了服务器名称的组合框和一个用于连接到服务器的按钮。
预期行为:
按钮最初是禁用的,但一旦选择服务器就可用。
我正在使用为异步任务实现 ICommand 方法的 AsyncCommand found this in this blog post。
我的问题是按钮在使用普通 RelayCommand 时工作正常,但在我使用 AsynCommand 时不起作用。我错过了什么吗?
这里是简化代码:
ConnectionWindow.xaml.cs:
<ComboBox Grid.Row="1" Grid.Column="1"
HorizontalAlignment="Left"
x:Name="listSourceServer"
ItemsSource="{Binding ListSourceServer}"
SelectedValue="{Binding SelectedSourceServer}"
VerticalAlignment="Top"
Width="450"
RenderTransformOrigin="0.5,0.5"/>
<Button Grid.Row="1" Grid.Column="2"
Content="Connect"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="100"
Height="25"
FontFamily="Arial"
Foreground="#FFFFFF"
Background="#2e86de"
Command="{Binding ButtonConnectSourceServer}">
</Button>
ConnectionViewModel.cs:
private string _selectedSourceServer;
public string SelectedSourceServer
{
set
{
_selectedSourceServer = value;
OnPropertyChanged(nameof(SelectedSourceServer));
}
get => _selectedSourceServer;
}
private async Task ConnectSourceServerAsync()
{
await ConnectAsync(SelectedSourceServer);
}
private bool CanConnectOnSourceServer()
{
return !string.IsNullOrEmpty(SelectedSourceServer);
}
public ConnectionViewModel() {
ButtonConnectSourceServer = new AsyncCommand(ConnectSourceServerAsync, CanConnectOnSourceServer);
}
AsyncCommand.cs:
public interface IAsyncCommand : ICommand
{
Task ExecuteAsync();
bool CanExecute();
}
public class AsyncCommand : IAsyncCommand
{
public event EventHandler CanExecuteChanged;
private bool _isExecuting;
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
private readonly IErrorHandler _errorHandler;
public AsyncCommand(
Func<Task> execute,
Func<bool> canExecute = null,
IErrorHandler errorHandler = null)
{
_execute = execute;
_canExecute = canExecute;
_errorHandler = errorHandler;
}
public bool CanExecute()
{
return !_isExecuting && (_canExecute?.Invoke() ?? true);
}
public async Task ExecuteAsync()
{
if (CanExecute())
{
try
{
_isExecuting = true;
await _execute();
}
finally
{
_isExecuting = false;
}
}
RaiseCanExecuteChanged();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
#region Explicit implementations
bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}
void ICommand.Execute(object parameter)
{
ExecuteAsync().FireAndForgetSafeAsync(_errorHandler);
}
#endregion
}
SelectedSourceServer
属性变化时需要调用RaiseCanExecuteChanged
方法
public ConnectionViewModel()
{
_buttonConnectSourceServer = new AsyncCommand(ConnectSourceServerAsync, CanConnectOnSourceServer);
}
private readonly AsyncCommand _buttonConnectSourceServer;
public IAsyncCommand ButtonConnectSourceServer => _buttonConnectSourceServer;
private string _selectedSourceServer;
public string SelectedSourceServer
{
get => _selectedSourceServer;
set
{
_selectedSourceServer = value;
OnPropertyChanged(nameof(SelectedSourceServer));
_buttonConnectSourceServer.RaiseCanExecuteChanged();
}
}
我正在尝试创建一个简单的 WPF 应用程序,其中包含一个填充了服务器名称的组合框和一个用于连接到服务器的按钮。
预期行为: 按钮最初是禁用的,但一旦选择服务器就可用。
我正在使用为异步任务实现 ICommand 方法的 AsyncCommand found this in this blog post。
我的问题是按钮在使用普通 RelayCommand 时工作正常,但在我使用 AsynCommand 时不起作用。我错过了什么吗?
这里是简化代码:
ConnectionWindow.xaml.cs:
<ComboBox Grid.Row="1" Grid.Column="1"
HorizontalAlignment="Left"
x:Name="listSourceServer"
ItemsSource="{Binding ListSourceServer}"
SelectedValue="{Binding SelectedSourceServer}"
VerticalAlignment="Top"
Width="450"
RenderTransformOrigin="0.5,0.5"/>
<Button Grid.Row="1" Grid.Column="2"
Content="Connect"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="100"
Height="25"
FontFamily="Arial"
Foreground="#FFFFFF"
Background="#2e86de"
Command="{Binding ButtonConnectSourceServer}">
</Button>
ConnectionViewModel.cs:
private string _selectedSourceServer;
public string SelectedSourceServer
{
set
{
_selectedSourceServer = value;
OnPropertyChanged(nameof(SelectedSourceServer));
}
get => _selectedSourceServer;
}
private async Task ConnectSourceServerAsync()
{
await ConnectAsync(SelectedSourceServer);
}
private bool CanConnectOnSourceServer()
{
return !string.IsNullOrEmpty(SelectedSourceServer);
}
public ConnectionViewModel() {
ButtonConnectSourceServer = new AsyncCommand(ConnectSourceServerAsync, CanConnectOnSourceServer);
}
AsyncCommand.cs:
public interface IAsyncCommand : ICommand
{
Task ExecuteAsync();
bool CanExecute();
}
public class AsyncCommand : IAsyncCommand
{
public event EventHandler CanExecuteChanged;
private bool _isExecuting;
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
private readonly IErrorHandler _errorHandler;
public AsyncCommand(
Func<Task> execute,
Func<bool> canExecute = null,
IErrorHandler errorHandler = null)
{
_execute = execute;
_canExecute = canExecute;
_errorHandler = errorHandler;
}
public bool CanExecute()
{
return !_isExecuting && (_canExecute?.Invoke() ?? true);
}
public async Task ExecuteAsync()
{
if (CanExecute())
{
try
{
_isExecuting = true;
await _execute();
}
finally
{
_isExecuting = false;
}
}
RaiseCanExecuteChanged();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
#region Explicit implementations
bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}
void ICommand.Execute(object parameter)
{
ExecuteAsync().FireAndForgetSafeAsync(_errorHandler);
}
#endregion
}
SelectedSourceServer
属性变化时需要调用RaiseCanExecuteChanged
方法
public ConnectionViewModel()
{
_buttonConnectSourceServer = new AsyncCommand(ConnectSourceServerAsync, CanConnectOnSourceServer);
}
private readonly AsyncCommand _buttonConnectSourceServer;
public IAsyncCommand ButtonConnectSourceServer => _buttonConnectSourceServer;
private string _selectedSourceServer;
public string SelectedSourceServer
{
get => _selectedSourceServer;
set
{
_selectedSourceServer = value;
OnPropertyChanged(nameof(SelectedSourceServer));
_buttonConnectSourceServer.RaiseCanExecuteChanged();
}
}