如何创建一个自定义按钮,将包含一个变化的图像取决于IsExecutingAsyncCommand?它需要在不创建UserControl的情况下实现,仅通过继承XAML中的Button(public class CustomButton : Button)和Style来实现.

public class AsyncCommand : ViewModelBase, ICommand
{
    protected readonly Func<Task> _execute;
    protected readonly Func<bool>? _canExecute;

    private bool _isExecuting;
    public bool IsExecuting
    {
        get => _isExecuting;
        set => RisePropertyChanged(ref _isExecuting, value);
    }

    public AsyncCommand(Func<Task> execute, Func<bool>? canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public event EventHandler? CanExecuteChanged;

    public bool CanExecute(object? parameter) => !_isExecuting && (_canExecute?.Invoke() ?? true);

    public async void Execute(object? parameter)
    {
        if (CanExecute(parameter))
        {
            try
            {
                IsExecuting = true;
                await ExecuteAsync(parameter);
            }
            finally
            {
                IsExecuting = false;
            }
        }

        OnCanExecuteChanged();
    }

    protected virtual async Task ExecuteAsync(object? parameter) => await _execute();

    protected virtual void OnCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

也许像使用一个event EventHandler? CanExecuteChanged,这是在ICommand中使用,并由Button响应,而不是RaiseProperty Changed,但我已经没有 idea 了.

我找了很多地方,try 了很多方法来实施它,但没有成功.

推荐答案

你可以用StyleDataTrigger来实现你的行为.您可以将此Style定义为应用程序资源或自定义Button的默认Style的一部分.

下面的示例显示如何切换Button.Content值.您可以修改触发器来切换任何其他依赖项属性,如图像源.目前还不清楚你的按钮是什么样子的,所以我决定显示针对Content属性的触发器:

<Button Command="{Binding SomeCommand}">
  <Button.Style>
    <Style TargetType="Button">
      <Setter Property="Content"
              Value="Not running" />

      <Style.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Command.IsExecuting}"
                     Value="True">
          <Setter Property="Content"
                  Value="Command is Running..." />
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </Button.Style>
</Button>

关于ICommand实现的注意事项:您的命令既没有将ICommand.CanExecuteChanged事件链接到CommandManager.RequerySuggested事件,也没有使客户端能够手动引发它.事件当前只在命令执行后引发,这是非常没有意义的.

当你不公开它并将它添加到命令API时,它的意义是什么?

如果你想使ExecuteAsync虚拟化,允许继承者重写它,你应该考虑在这个虚拟方法中添加执行逻辑,使继承者能够改变行为,而不是只添加行为.或者,也可以将Icommand.Execute实现定义为虚拟.

通过添加IsExecuting条件在内部修改CanExecute条件也是有问题的,因为它会减少使用类型的一些灵活性.

您的代码包含一个拼写错误:R100sePropertyChanged,而不是R101sePropertyChanged.

建议改进你的AsyncCommand个版本:

public class AsyncCommand : ViewModelBase, ICommand
{
  protected readonly Func<Task> _execute;
  protected readonly Func<bool>? _canExecute;

  private bool _isExecuting;
  public bool IsExecuting
  {
    get => _isExecuting;
    set => RisePropertyChanged(ref _isExecuting, value);
  }

  public bool IsCommandMangerRequerySuggestedEnabled { get; set; }

  public AsyncCommand(Func<Task> execute, Func<bool>? canExecute = null)
  {
    _execute = execute ?? throw new ArgumentNullException(nameof(execute));
    _canExecute = canExecute;

    this.IsCommandMangerRequerySuggestedEnabled = true;
    CommandManager.RequerySuggested += OnCommandManagerRequerySuggested;
  }

  private void OnCommandManagerRequerySuggested(object? sender, EventArgs e)
  {
    if (this.IsCommandMangerRequerySuggestedEnabled)
    {
      this.CanExecuteChanged?.Invoke(sender, e);
    }
  }

  // Manually raise the CanExecuteChanged event
  public void InvalidateCommand()
    => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);

  public event EventHandler? CanExecuteChanged;

  public bool CanExecute(object? parameter) => !_isExecuting && (_canExecute?.Invoke() ?? true);

  public async virtual void Execute(object? parameter)
  {
    if (CanExecute(parameter))
    {
      try
      {
        IsExecuting = true;
        await ExecuteAsync(parameter);
      }
      finally
      {
        IsExecuting = false;
      }
    }
  }

  public virtual async Task ExecuteAsync(object? parameter) => await _execute();

  protected virtual void OnCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

Csharp相关问答推荐

将C#字符串转换为其UTF8编码字符的十六进制表示

如何使用PDFSharp将文本添加到现有PDF

如何从顶部提取发票号作为单词发票后的第一个匹配

向类注入一个工厂来创建一些资源是一个好的实践吗?

. NET WireMock拒绝PostAsJsonAsync序列化

使用C#中的SDK在Microsoft Graph API上使用SubscribedSkus的问题

如何在C#中使用正则表达式抓取用逗号分隔的两个单词?

需要在重新启动ApplicartionPool或IIS后启动/唤醒API的帮助

内部接口和类的DI解析

静态对象构造顺序

如何使用自定义负载均衡器管理Ocelot负载均衡器中的多线程和批读取

我的MRG32k3a算法实现返回的数字超出了预期的[0,1]范围

集合表达式没有目标类型

链接到字典字符串.拆分为(.Key,.Value)

为什么Docker中没有转发该端口?

如何在使用属性 Select 器时判断是否可以为空

C#使用相同内存的多个数组

WPF:如何从DatagridHeader的内容模板绑定到词典项

映射器-如何映射到多个实体

带有类约束的C#泛型