首页 > 解决方案 > 如何在 ListBox 在其 ItemsSource 集合更改后更新时显示等待光标?

问题描述

我有ListBox一个视图模型属性 ( Photos),它是一个ObservableCollection包含图像文件路径的对象。显示ListBox图像,可能有很多:

看法:

<ListBox ItemsSource="{Binding Path=Photos}"
         SelectionChanged="PhotoBox_SelectionChanged">
    ...
</ListBox>

后面的代码(可以改进为异步运行......):

void RefreshPhotoCollection (string path) {
    Photos.Clear ();
    var info = new DirectoryInfo (path);
    try {
        foreach (var fileInfo in info.EnumerateFiles ()) {
            if (FileFilters.Contains (fileInfo.Extension)) {
                Photos.Add (new Photo (fileInfo.FullName));
            }
        }
    }
    catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) {
        ...
    }
}

RefreshPhotoCollection通过使用这种涉及 IDisposable 的方法,我设法在运行时显示等待光标:

using (this.BusyStackLifeTracker.GetNewLifeTracker())
    {
        // long job
    }

但是当视图被通知集合更改时,光标会在此方法结束时重置为指针。然后ListBox呈现自己,但完成时不显示等待光标。我有问题。

如何ListBox在更新完成之前显示等待光标?

标签: c#wpf

解决方案


这个模式做了这些事情:

  1. 将光标更改逻辑放在标志变量中以供其他用途。
  2. 为其他线程提供一种安全的方式来更改gui 线程上的 GUI 项。
  3. 在允许 UI 响应的单独线程中添加照片。
  4. 为了安全起见,添加到集合中的实际照片是在 Gui 线程上完成的。
  5. 添加照片时,等待光标不会 跳转/更改状态。
  6. 检查最短等待处理时间,以呈现做某事的样子。可以根据需要更改等待时间。如果没有达到最小等待时间,则不做额外的时间。

虚拟机上的通知这允许任何非 gui 线程执行安全的 gui 操作。

public static void SafeOperationToGuiThread(Action operation)
{
    System.Windows.Application.Current?.Dispatcher?.Invoke(operation);
}

状态标志 然后提供一个持续标志,如果需要,可以重新使用视图,这也设置光标:

private bool _IsOperationOnGoing;

public bool IsOperationOnGoing
{
    get { return _IsOperationOnGoing; }
    set {
        if (_IsOperationOnGoing != value)
        {
            _IsOperationOnGoing = value;

            SafeOperationToGuiThread(() => Mouse.OverrideCursor = (_IsOperationOnGoing) ? Cursors.Wait : null  );
            OnPropertyChanged(nameof(IsOperationOnGoing));
        }
    }
}

在单独的线程中添加照片然后在您的照片添加中,执行此模式,其中 gui 线程关闭光标,然后单独的任务/线程加载照片。还监控时间以显示可行的一致等待时间光标:

private Task RunningTask { get; set; }

void RefreshPhotoCollection (string path) 
{
   IsOperationOnGoing = true;
   RunningTask = Task.Run(() =>
     {
         try { 
              TimeIt( () =>
                          {
                          ... // Enumerate photos like before, but add the safe invoke:

                          SafeOperationToGuiThread(() => Photos.Add (new Photo (fileInfo.FullName););
                          },
                         4   // Must run for 4 seconds to show the user.
                    );
              }
         catch() {}
         finally 
             {
               SafeOperationToGuiThread(() => IsOperationOnGoing = false);
             }
          });
     }

}

等待时间检查

如何在视图 ListBox 更新完成之前保持等待光标,以便用户真正知道 UI 何时再次响应?

这是等待操作的方法,如果没有达到最小操作时间,将填写等待时间:

public static void TimeIt(Action work, int secondsToDisplay = 2)
{
    var sw = Stopwatch.StartNew();
    
    work.Invoke();

    sw.Stop();

    if (sw.Elapsed.Seconds < secondsToDisplay)
        Thread.Sleep((secondsToDisplay - sw.Elapsed.Seconds) * 1000);    
}
  

如果需要,可以将其修改为使用异步调用,只需进行最少的更改。


替代配置文件中的@mins 标记行:

 Light a man a fire and he is warm for a day. 
 Set a man a fire and he is warm for the rest of his life.

推荐阅读