使用 backgroundWorker 和大量动画维护响应式用户界面
Maintain a responsive user interface with backgroundWorker and heavy animation
我有一个 C# 应用程序(winforms 和 wpf,但对于这个问题我主要关注 winforms 应用程序),其中 backgroundWorker 用于处理数据集,然后调用 ProgressChanged 然后调用表单刷新方法强制重绘。然后根据数据集的当前帧绘制一堆椭圆。
给定的帧可能涉及绘制零到几百个椭圆之间的任意位置。
此外,我还有一个滑块控件,允许用户调整播放速率(基本上是循环内的 thread.sleep 值。)
当用户将睡眠值设置得太低时,有时重绘方法会排队,并且 UI 变得无响应。 (这取决于帧中椭圆的数量和计算机的速度。在 UI 上重新绘制延迟是 100%,而不是任何其他处理,这基本上只是增加一个计数器和设置标签文本。)
我希望能够检测排队情况并自动调整速度滑块以适应更大的数据集 and/or 较慢的计算机。我如何判断 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--;
}
}
我有一个 C# 应用程序(winforms 和 wpf,但对于这个问题我主要关注 winforms 应用程序),其中 backgroundWorker 用于处理数据集,然后调用 ProgressChanged 然后调用表单刷新方法强制重绘。然后根据数据集的当前帧绘制一堆椭圆。
给定的帧可能涉及绘制零到几百个椭圆之间的任意位置。
此外,我还有一个滑块控件,允许用户调整播放速率(基本上是循环内的 thread.sleep 值。)
当用户将睡眠值设置得太低时,有时重绘方法会排队,并且 UI 变得无响应。 (这取决于帧中椭圆的数量和计算机的速度。在 UI 上重新绘制延迟是 100%,而不是任何其他处理,这基本上只是增加一个计数器和设置标签文本。)
我希望能够检测排队情况并自动调整速度滑块以适应更大的数据集 and/or 较慢的计算机。我如何判断 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--;
}
}