【问题标题】:Why does my form NOT lock up?为什么我的表格没有锁定?
【发布时间】:2014-09-20 05:18:21
【问题描述】:

在从另一个问题中阅读this answer 时,我 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);
    }
}

我和 cmets 中的其他几个人说它不起作用,但 OP 坚持认为它起作用了,我们应该尝试。我最终创建了一个新的 Winforms 项目,添加了一个按钮,然后将上述代码放入事件处理程序中进行点击,表单确实发生了抖动。

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

【问题讨论】:

  • 即使不触发Paint,表单也可以移动。这仍然是个坏主意。
  • @SLaks 我并不是说这是一个好主意,我只是想知道到底发生了什么让这项工作发挥作用。
  • 某处可能有DoEvents 或某种道德等价物。
  • 我相信 Windows 会存储您窗口的“屏幕截图”,即使消息泵没有响应,仍然允许移动发生。最终,窗口可能会变灰,并显示应用程序未响应的消息。
  • 这就是我问这个问题的原因,我想到了和以前的 cmets 一样的“也许”和“某事”,我只是不确定答案。我问了这个问题,以便更详细地了解到底发生了什么。

标签: c# .net winforms


【解决方案1】:

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

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

不要在 XP 或关闭 Aero 的情况下尝试此操作。

【讨论】:

  • 当你说And affects this as well时,我想你漏掉了一个词,但我不知道那是什么词。
  • 这也是我的第一个想法,但我清楚地记得在 Windows 2000 和 XP 上运行几乎相同的代码。没有明显的问题:没有重影、没有撕裂等。我确信应用程序变得没有响应,但这并不是真正的问题。没有人会尝试在它来回晃动的时候点击它。不管有没有 Aero,窗口管理器都会绘制它。
【解决方案2】:

这是可能的,因为 CoroutinesFibers

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

显然,当我们设置那些看似无害的属性this.Leftthis.Right 时,幕后发生的事情远比我们想象的要多。

如果我们在问题中的代码示例中添加一点日志记录,我们可以看到这种情况。另外——因为我在下面的示例中删除了Thread.Sleep(75) 调用,并在更早的时候添加了更长的睡眠——我们可以看到不是那些睡眠给另一个函数带来了收益,而是对@ 的调用987654327@ 和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;

日志:

【讨论】:

    猜你喜欢
    • 2014-02-28
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 2021-02-02
    • 1970-01-01
    • 2010-10-14
    相关资源
    最近更新 更多