桌面屏幕叠加 - 新形式的闪烁问题

时间:2015-12-22 16:44:28

标签: c# winforms graphics screenshot gdi+

作为我的开源DeskPins clone的一部分(两个单独的链接,一个用于原始链接,一个用于GitHub上的克隆),我需要允许用户与桌面交互。我认为最简单的方法是打开一个新表单并在其上绘制桌面内容。然后,我应该能够轻松设置鼠标光标,并为用户提供有关当前焦点窗口的视觉提示。

更复杂的替代方法是使用p / invoke到SetSystemCursor并在其他窗口的WM_PAINT事件队列中注入自定义代码(以及可能的其他WinApi相关工作,例如,光标清理将是一个问题,如果我的程序异常终止)。我不喜欢这样。

我下面的代码正在运行,唯一的问题是屏幕闪烁。我设置DoubleBuffered = true后(而不是屏幕闪烁它变成父窗体闪烁)后变得更好,但仍然明显。所以现在我的表单每次打开叠加表单时都会闪烁。

我可以做些什么来使其成为平滑过渡,即好像新窗口没有打开?有一个"冻结" effect =任何动画都将被暂停。

public sealed partial class DesktopOverlayForm : Form
{
  public DesktopOverlayForm()
  {
    InitializeComponent();

    //make full screen
    //http://stackoverflow.com/a/2176683/897326
    Rectangle bounds = Screen.AllScreens
                      .Select(x => x.Bounds)
                      .Aggregate(Rectangle.Union);
    this.Bounds = bounds;

    //set desktop overlay image
    this.BackgroundImage = MakeScreenshot();
  }

  /// <remarks>
  ///  Based on this answer on StackOverflow:
  ///  http://stackoverflow.com/questions/1163761/capture-screenshot-of-active-window
  /// </remarks>
  private static Bitmap MakeScreenshot()
  {
    Rectangle bounds = Screen.GetBounds(Point.Empty);
    Bitmap image = new Bitmap(bounds.Width, bounds.Height);

    using (Graphics g = Graphics.FromImage(image))
    {
      g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
    }

    return image;
  }

  private void DesktopOverlayForm_KeyDown(object sender, KeyEventArgs e)
  {
    if (e.KeyCode == Keys.Escape)
    {
      this.Close();
    }
  }
}

2 个答案:

答案 0 :(得分:2)

过去,我对我闪烁的Form这样做了。对于我的情况,双缓冲区并没有真正起作用。

//this.DoubleBuffered = true; //doesn't work
protected override CreateParams CreateParams { //Very important to cancel flickering effect!!
  get {
    CreateParams cp = base.CreateParams;
    cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    //cp.Style &= ~0x02000000;  // Turn off WS_CLIPCHILDREN not a good idea when combined with above. Not tested alone
    return cp;
  }
}

想法是替换CreateParams参数。另见:

答案 1 :(得分:2)

肯定需要

DoubleBuffered模式。 父窗体闪烁的原因是因为在绘制叠加窗体之前激活叠加窗体(因此父窗体被停用并需要在视觉上指示)。

为了解决这个问题,需要在不激活的情况下显示覆盖形式,然后在第一次绘制后立即激活覆盖形式。第一个是通过覆盖一个鲜为人知的虚拟保护属性Form.ShowWithoutActivation来实现的,第二个是通过挂钩OnPaint方法和一个表单级别标志来实现的。像这样的东西

public sealed partial class DesktopOverlayForm : Form
{
    public DesktopOverlayForm()
    {
        // ...
        this.DoubleBuffered = true;
    }

    protected override bool ShowWithoutActivation { get { return true; } }

    bool activated;

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        if (!activated)
        {
            activated = true;
            BeginInvoke(new Action(Activate));
        }
    }
}