我有一个进行一些处理的按钮,我需要在过程开始之前将其禁用,并在过程完成之后将其启用。我需要以mvvm模式完成此操作。
MainWindow.xaml
<Window x:Class="ButtonCommandBindingMVVM.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:ButtonCommandBindingMVVM"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:ViewModel x:Key="vm"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="40"/>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Button Grid.Row="1"
x:Name="Button1"
Width="100"
Height="27"
Content="Say Hello"
Command="{Binding Button1_ClickCommand, Source={StaticResource vm}}"
/>
<Button Grid.Row="2"
x:Name="Button2"
Width="100"
Height="27"
Content="Say Welcome"
Command="{Binding Button2_ClickCommand, Source={StaticResource vm}}"
/>
</Grid>
Command.cs 这是中继命令类。
class Command : ICommand
{
Action<object> ExecuteMethod;
Func<object, bool> CanExecuteMethod;
public Command(Action<object> ExecuteMethod, Func<object, bool> CanExecuteMethod)
{
this.ExecuteMethod = ExecuteMethod;
this.CanExecuteMethod = CanExecuteMethod;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
ExecuteMethod(parameter);
}
public event EventHandler CanExecuteChanged;
}
ViewModel.cs
public class ViewModel
{
public ICommand Button1_ClickCommand { get; set; }
public ICommand Button2_ClickCommand { get; set; }
public ViewModel()
{
Button1_ClickCommand = new Command(ExecuteMethodButton1_ClickCommand, CanExecuteMethodButton1_ClickCommand);
Button2_ClickCommand = new Command(ExecuteMethodButton2_ClickCommand, CanExecuteMethodButton2_ClickCommand);
}
private bool CanExecuteMethodButton1_ClickCommand(object parameter)
{
return true;
}
private void ExecuteMethodButton1_ClickCommand(object parameter)
{
await Task.Run(() =>
{
Thread.Sleep(5000);
});
MessageBox.Show("Hello");
}
private bool CanExecuteMethodButton2_ClickCommand(object parameter)
{
return true;
}
private void ExecuteMethodButton2_ClickCommand(object parameter)
{
MessageBox.Show("Welcome");
}
}
禁用Button1并在线程休眠5秒钟后启用它。
答案 0 :(得分:0)
尝试以异步方式运行的ICommand的这种实现。
public class perRelayCommandAsync : ObservableObject, ICommand
{
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
public perRelayCommandAsync(Func<Task> execute) : this(execute, () => true ) { }
public perRelayCommandAsync(Func<Task> execute, Func<bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
private bool _isExecuting;
public bool IsExecuting
{
get => _isExecuting;
set
{
if(Set(nameof(IsExecuting), ref _isExecuting, value))
RaiseCanExecuteChanged();
}
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => !IsExecuting
&& (_canExecute == null || _canExecute());
public async void Execute(object parameter)
{
if (!CanExecute(parameter))
return;
IsExecuting = true;
try
{
await _execute().ConfigureAwait(true);
}
finally
{
IsExecuting = false;
}
}
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
ObservableObject来自MVVMLight,但是您可以将其替换为任何INotifyPropertyChanged实现。 _canExecute标志将导致在执行任务时自动禁用按钮。
您的示例将成为
Button1_ClickCommand = new perRelayCommandAsync(()=>Task.Delay(TimeSpan.FromSeconds(5));
不要在异步代码中使用Thread.Sleep()-您只是在阻塞执行线程。
有关WPF命令的更广泛讨论,请查看我的blog post。
答案 1 :(得分:0)
异步ICommand的一种非常简单的实现-仅在异步执行过程中禁用命令目标-看起来像这样:
public class AsyncCommand : ICommand
{
private readonly Func<object, Task> execute;
private bool canExecute = true;
public event EventHandler CanExecuteChanged;
public AsyncCommand(Func<object, Task> execute)
{
this.execute = execute;
}
public bool CanExecute(object parameter)
{
return canExecute;
}
public async void Execute(object parameter)
{
if (canExecute)
{
canExecute = false;
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
try
{
await execute(parameter);
}
finally
{
canExecute = true;
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
}
您可以使用以下命令对其进行测试:
public ICommand TestCommand { get; }
private Task TestMethod(object p)
{
return Task.Delay(1000);
}
...
TestCommand = new AsyncCommand(TestMethod);
当然也可以将execute方法声明为async
:
private async Task TestMethod(object p)
{
// do something before awaiting a long running task
await Task.Run(() =>
{
// a long running task
});
// do something after awaiting a long running task
}