在 ICommand 操作期间更新标签

Updating a Label during an ICommand Action

在 WPF 中,我使用 MVVM 设计创建了一个屏幕,旨在通过单击按钮将大量日志加载到 ListView。 return 后,更新标签以显示 returned 的日志数。这个过程有时需要一段时间。我们的 DA 正在进行优化,但同时我需要进行以下更改以向用户表明搜索是 运行:

  1. 将鼠标显示为 WaitCursor
  2. 更新标签文本以显示“正在搜索...”。

我有一个实现了 ICommand 的 class 并且 WaitCursor 可以正常工作。但是,当搜索为 运行 时,我无法获得更新要显示的标签所需的行为。我当前的代码:

MyScreen.xaml

<Button 
    Name="DisplayButton"
    Content="Display Logs"
    Command="{Binding DisplayLogsCommand}"
    Margin="0,64,10,0"
    VerticalAlignment="Top"
    HorizontalAlignment="Right"
    Width="112"/>
<Label 
    Content="{Binding LogsShowingText}"
    Margin="0,0,127,8"
    Foreground="#FF3C3B3B"
    HorizontalAlignment="Right"
    Width="145" Height="24"
    VerticalAlignment="Bottom"
    HorizontalContentAlignment="Right"
    FontSize="11"/>

MyScreenVM.cs

private Command displayLogsCommand;
private string logShowingText;

public Command DisplayLogsCommand
{
  get
  {
    if (this.displayLogsCommand == null)
    {
      // Attempt 3 made here.

      bool useWaitCursor = true;

      Func<bool> canExecute = () => this.ValidateFields();

      Action execute = () =>
      {
        /*
         *  TODO: Update this.LogsShowingText to read "Searching..."
         */

        // Attempt 1 and 2 made here.
        LogEntry[] entries = this.ClientConnection.GetLogs();

        this.LogsShowingText = string.Format("Showing {0} Logs", entries.Length);
        this.FilteredEntries = new ObservableCollection<LogEntry>(entries);
      };

      this.displayLogsCommand = new Command(useWaitCursor, canExecute, execute);
    }

    return this.displayLogsCommand;
  }
}

public string LogsShowingText
{
  get
  {
    return this.logsShowingText;
  }
  set
  {
    this.logsShowingText= value;
    OnPropertyChanged("LogsShowingText");
  }
}

到目前为止,结果和我相关的失败尝试如下:

  1. 日志 returned 后,标签仅显示为“正在搜索...” .

    Application.Current.Dispatcher.Invoke(new Action(() => this.LogsShowingText = "Searching..."));
    
  2. 日志 returned 后,标签仅显示为“显示 N 日志”。

    this.LogsShowingText = string.Format("Searching...");
    
  3. 之前和搜索期间,标签显示为“正在搜索...",然后 日志被 returned,标签显示为“显示 N 日志”。与 #2 相同的代码,不同的位置。

我知道这可能与 UI 在操作完成之前被阻止有关,这清楚地解释了尝试 1 显示最后排队的标签更新和尝试 2 显示最后的硬编码更新标签。尝试 3 几乎可以工作,但在用户单击按钮执行搜索之前不应更新标签。我该怎么做?

由于这是一个昂贵的命令,意味着 UI 在处理时挂起,您应该将其转换为异步命令。

public Command DisplayLogsCommand
{
  get
  {
    if (this.displayLogsCommand == null)
    {
        bool useWaitCursor = true;
        Func<bool> canExecute = () => this.ValidateFields();

        Action execute = async () =>
        {
           this.LogsShowingText = "Searching";

          // Attempt 1 and 2 made here.
          LogEntry[] entries = await Task<LogEntry[]>.Run(() => this.ClientConnection.GetLogs());

          this.LogsShowingText = string.Format("Showing {0} Logs", entries.Length);
          this.FilteredEntries = new ObservableCollection<LogEntry>(entries);
       };

      this.displayLogsCommand = new Command(useWaitCursor, canExecute, execute);
    }

    return this.displayLogsCommand;
  }
}

通过使 Action 委托异步,您现在可以在 Action 中等待。这使您可以将对 DataLayer 的调用包装在 Task 中并等待它。现在昂贵的操作是 运行 关闭 UI 线程,您的标签将在前后正确更新。无需使用 Dispatcher 编组更改。

这将仅在用户单击按钮执行命令时更新标签。