使用后台工作者和大量动画维护响应式用户界面

本文关键字:维护 动画 响应 用户界面 后台 工作者 | 更新日期: 2023-09-27 18:37:19

我有一个C#应用程序(winforms和wpf,但对于这个问题,我专注于winforms应用程序),其中使用backgroundWorker 来处理数据集,以及对ProgressChanged的调用,然后调用表单Refresh方法来强制重绘。然后,这会根据数据集的当前帧绘制一堆椭圆。

给定的帧可能涉及绘制零到几百个椭圆之间的任何位置。

此外,我还有一个滑块控件,允许用户调整播放速率(基本上是循环中的 thread.sleep 值)。

当用户将睡眠值设置得太低时,有时重绘方法会排队,并且 UI 变得无响应。(这取决于帧中省略号的数量和计算机的速度。在UI上重新绘制延迟是100%,而不是任何其他处理,这基本上只是增加计数器并设置标签文本。

我希望能够检测到排队并自动调整速度滑块以适应更大的数据集和/或较慢的计算机。如何判断 UI 线程是否通过多次调用Map_Paint进行备份?

当前代码(释义):

public Map()
{
  InitializeComponent();
  _worker = new BackgroundWorker();
  _worker.DoWork += _worker_DoWork;
  _worker.ProgressChanged += _worker_ProgressChanged;
  _worker.WorkerReportsProgress = true;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
    _frameCount = _frames.FrameCount();
    // For this specific example, _frameCount may be around 30000-40000
    for (var i = 0; i < _frameCount; i++)
    {
      var f = _frames.Frame(i + 1);
      _worker.ReportProgress(i, f);
      Thread.Sleep(_tickCount);
      _suspend.WaitOne(); // Used to Pause the playback
    }
}
void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
  // set some variables according to state and progresspercentage snipped here
  // ...
  // I would like to detect at this point whether the screen repainting is backed up
  // and if so, adjust the value of _tickCount to slow down the program.
  this.Refresh();
}
private void Map_Paint(object sender, PaintEventArgs e)
{
    // Lots of ellipsis drawing stuff here
    // Maybe 0-1000 ellipses drawn per cycle.
}
private void tbSpeed_Scroll(object sender, EventArgs e)
{
  // This is the Scroll event for the slider.
  // Value range is 10-300
  // The slider becomes unresponsive when the UI thread backs up.
  // I'd like to detect the back up and override the value of _tickCount
  _tickCount = tbSpeed.Value;
}

使用后台工作者和大量动画维护响应式用户界面

private static object _lock = new object();
private static int _queuedCount = 0;
public Map()
{
  InitializeComponent();
  _worker = new BackgroundWorker();
  _worker.DoWork += _worker_DoWork;
  _worker.ProgressChanged += _worker_ProgressChanged;
  _worker.WorkerReportsProgress = true;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
    _frameCount = _frames.FrameCount();
    // For this specific example, _frameCount may be around 30000-40000
    for (var i = 0; i < _frameCount; i++)
    {
      var f = _frames.Frame(i + 1);
      lock(_lock)
      {
          _queuedCount++;
      }
      _worker.ReportProgress(i, f);
      Thread.Sleep(_tickCount);
      _suspend.WaitOne(); // Used to Pause the playback
    }
}
void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
  if (_queuedCount > 1)
      //now queue is building up
  this.Refresh();
  lock(_lock)
  {
      _queuedCount--;
  }
}