在WinForms中使用重新绘制优化的控件
本文关键字:绘制 优化 控件 新绘制 WinForms | 更新日期: 2023-09-27 17:51:11
已删除帖子,并有代码:
namespace StackOverflowQuestion
{
class Program
{
static void Main (string[] args)
{
var cw = new ConsoleWriter ();
for (int i = 0; i <= 5000000; i++)
{
cw.Write (i);
}
Console.ReadKey ();
}
}
class ConsoleWriter
{
private Stopwatch sw = new Stopwatch ();
public ConsoleWriter ()
{
sw.Start ();
}
public void Write (int pNumber)
{
if (sw.ElapsedMilliseconds >= 50)
{
Console.WriteLine (pNumber);
sw.Restart ();
}
}
}
}
,输出:305940年
651171年
1002965
1358665
1715740
2069602
2419054
2772833
3127880
3485054
3844335
4204016
4557912
4913494
所以一切都很好。在这个例子中,ConsoleWriter在控制台上显示数字,但它可以显示在控制界面中。就像你看到的,即使我调用5000000次Write方法,它只在至少50毫秒后更新UI。很好,但请注意,在许多情况下,最后一个值5000000将不会显示。如何解决这个问题?我应该使用一个类(线程),它将调用事件每50毫秒,它将检查值写的变化?
可以使用计时器
Timer _timer;
public void StartTimer()
{
_timer = new Timer();
_timer.Interval = 100; // 100 ms = 0.1 s
_timer.Tick += new EventHandler(timer_Tick);
_timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
myControl.Number = i;
}
控件中应该有类似
的内容private int _number;
public int Number
{
get { return _number; }
set
{
if (value != _number) {
_number = value;
Invalidate();
}
}
}
呼叫Invalidate()
将触发Paint
事件。你的绘画逻辑应该在OnPaint
方法:
protected override void OnPaint(PaintEventArgs e)
{
... paint here
}
但是for循环本身会冻结应用程序。您可以使用第二个计时器,它以比显示计数器更快的间隔更新计数器。在UI- thread(如果你喜欢主线程的话)上运行的每个代码都将冻结UI,直到它终止。在单独的线程中在后台做繁重工作的一个简单方法是使用BackgroundWorker
。后台worker会自动在ui线程和worker线程之间切换,并允许你向ui线程报告进程。
你也可以手动启动一个线程来更新计数器。如果这是唯一一个改变这个数字的线程,则不需要同步机制。但是永远不要从UI线程以外的其他线程访问UI(窗体或控件)。
下面是一个完整的非阻塞解决方案,使用另一个线程来计数Timer _timer;
int _counter;
System.Threading.Thread _worker;
public frmTimerCounter()
{
InitializeComponent();
_worker = new System.Threading.Thread(() =>
{
while (_counter < 10000000) {
_counter++;
System.Threading.Thread.Sleep(20);
}
});
_worker.Start();
StartTimer();
}
public void StartTimer()
{
_timer = new System.Windows.Forms.Timer();
_timer.Interval = 100; // 100 ms = 0.1 s
_timer.Tick += new EventHandler(timer_Tick);
_timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
// I used a Label for the test. Replace it by your control.
label1.Text = _counter.ToString();
}
你没有张贴任何代码,但我可以猜到它看起来像什么。你在属性设置器中做了太多的工作。for()循环的时间不应该超过一毫秒,因为太短了,根本注意不到GUI冻结。
您可以通过遵循控件重新绘制自己的标准方式来获得此。也就是。您可以通过调用Invalidate()方法来获得它。这样的:
class MyControl : Control {
private int number;
public int Number {
get { return this.number; }
set {
if (value != this.number) this.Invalidate();
this.number = value;
}
}
protected override void OnPaint(PaintEventArgs e) {
// TODO: paint number
//...
base.OnPaint(e);
}
}
你现在还会发现一些其他的东西,没有必要再使用for()循环了。从一开始就没有,人类不可能看到现代处理器增加数字的惊人速度。所以你现在要把它替换成:
myControl.Number = 50000;
如果你真的想让人的眼睛看到数字的增加,那么你将不得不做得慢得多。不超过每50毫秒一次,大约在变化变得模糊的时候。这需要一个Timer