在从另一个问题中阅读this answer时,我100%确定以下代码会锁定用户界面并且不会导致任何移动。
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不应该在表单上调用,也不能在其任何子控件上调用,这是如何工作的?
答案 0 :(得分:10)
Aero做到这一点。 Windows不再直接渲染到视频帧缓冲区,它们渲染到内存中。想想“位图”。然后DWM过程合成这些位图并将它们blits到缓冲区。这解决了许多窗口绘画文物,最臭名昭着的可能就是当你将一个窗口移到另一个窗口时留下的“痕迹”。无论底层窗口拥有什么进程都必须赶上并重新绘制显示的像素。这需要时间和未上漆的像素可见。还允许其他特殊效果,例如当您将鼠标悬停在任务栏按钮上时可以看到的实时缩略图,只需缩小该位图的副本即可。和Aero Peek,只是该位图的投影。和Vista和Win7中的玻璃。以及在Win8中显示商店应用程序的特殊“屏幕”。以及触摸屏幕时看到的可见标记。
并且影响此代码,窗口位置现在只是位图合成的不同位置。原始位图仍然完好无损,不需要重新绘制。
请勿在XP上或关闭Aero时尝试此操作。
答案 1 :(得分:1)
这可能是因为Coroutines和Fibers。
虽然线程是可由操作系统调度程序独立管理的最小程序指令序列,但协同程序可以存在于共享单个线程的应用程序中。这意味着一个例程可以在另一个例程执行时停止在其轨道中,然后在它停止的位置恢复,所有这些都在同一个线程内。
显然,当我们设置那些看似无害的属性this.Left
和this.Right
时,幕后发生的事情远比我们想象的要多。
如果我们在问题的代码示例中添加一点日志记录,我们就可以看到这种情况发生了。另外 - 因为我已经删除了下面示例中的Thread.Sleep(75)
次来电,并且在较早的时候添加了更长时间的睡眠 - 我们可以看到,这不是那些带来收益的睡眠其他功能,但是对this.Left
和this.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;
日志: