为什么我的表格没有锁定?

在从另一个问题中阅读这个答案时 ,我100%确定以下代码会锁定UI并且不会导致任何移动。

private void button1_Click(object sender, EventArgs e) { for (int i = 0; i < 5; i++) { this.Left += 10; System.Threading.Thread.Sleep(75); this.Left -= 10; System.Threading.Thread.Sleep(75); } } 

我和评论中的其他几位人士表示不会有效,但OP坚持认为它确实如此,我们应该尝试。 我最终创建了一个新的Winforms项目,添加了一个按钮,然后将上面的代码放在事件处理程序中进行单击,表单确实发生了变化。

当此方法阻止消息泵时表单如何移动,OnPaint不应该在表单上调用,也不能在其任何子控件上调用,这是如何工作的?

Aero做到了这一点。 Windows不再直接渲染到video帧缓冲区,它们渲染到内存。 想想“位图”。 然后DWM过程合成这些位图并将它们blits到缓冲区。 这解决了许多窗口绘画文物,最臭名昭着的可能就是在将一个窗口移到另一个窗口时留下的“痕迹”。 无论底层窗口拥有什么进程都必须赶上并重新绘制显示的像素。 这需要时间和未上漆的像素可见。 还允许其他特殊效果,例如当您将鼠标hover在任务栏按钮上时可以看到的实时缩略图,只需缩小该位图的副本即可。 而Aero Peek,只是该位图的投影。 和Vista和Win7中的玻璃。 以及在Win8中显示商店应用程序的特殊“屏幕”。 当您触摸屏幕时看到的可见标记。

并且影响此代码,窗口位置现在只是位图合成的不同位置。 原始位图仍然完好无损,不需要重新绘制。

不要在XP上或关闭Aero时尝试此操作。

由于Coroutines和Fibers,这是可能的。

虽然线程是可以由操作系统调度程序独立管理的最小程序指令序列,但协同程序可以存在于共享单个线程的应用程序中。 这意味着一个例程可以在另一个例程执行时停止在其轨道中,然后在它停止的点处恢复,所有这些都在同一个线程内。

显然,当我们设置那些看似无害的属性时,封面下发生的事情远比我们想象的this.Rightthis.Leftthis.Rightthis.Right

如果我们在问题的代码示例中添加一点日志记录,我们就可以看到这种情况发生了。 另外 – 因为我在下面的示例中删除了Thread.Sleep(75)调用,并且在较早的时候添加了更长的睡眠 – 我们可以看到,这不是那些导致其他函数的收益的睡眠,而是调用this.Leftthis.Right (此外,还在没有睡眠的情况下摇动表单):

  protected override void WndProc(ref Message m) { if (_logWndProc) listBox1.Items.Add(string.Format("WndProc on thread {0}", Thread.CurrentThread.ManagedThreadId)); base.WndProc(ref m); } private void button1_Click(object sender, EventArgs e) { listBox1.Items.Clear(); _logWndProc = true; Thread.Sleep(2000); listBox1.Items.Add(string.Format("After sleep on thread {0} (see any WndProc logs before this line?!!!)", Thread.CurrentThread.ManagedThreadId)); for (int i = 0; i < 5; i++) { listBox1.Items.Add(string.Format("Right {0} on thread {1}", i, Thread.CurrentThread.ManagedThreadId)); this.Left += 10; listBox1.Items.Add(string.Format("Left {0} on thread {1}", i, Thread.CurrentThread.ManagedThreadId)); this.Left -= 10; } _logWndProc = false; } private bool _logWndProc = false; 

日志:

在此处输入图像描述