c# - 桌面屏幕覆盖 - 新窗体闪烁问题

标签 c# winforms graphics screenshot gdi+

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

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

我下面的代码可以正常工作,唯一的问题是屏幕闪烁。在我设置 DoubleBuffered = true 之后它变得更好了(而不是 screen flicker 它变成了 parent form flicker),但仍然很明显。所以现在每次打开覆盖表单时我的表单都会闪烁。

我能做些什么来使它成为一个平滑的过渡,即好像没有打开新窗口一样?可以有一个“卡住”效果 = 任何动画都会被暂停。

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();
    }
  }
}

最佳答案

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

为了解决这个问题,覆盖表单需要在不被激活的情况下显示,然后在第一次绘制后立即被激活。第一个是通过重写一个鲜为人知的虚拟 protected 属性 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));
        }
    }
}

关于c# - 桌面屏幕覆盖 - 新窗体闪烁问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34420599/

相关文章:

java - 实现这个最有效的方法是什么?

c# - 将 DOM XmlElement 反序列化为 .NET 对象

c# - 如何使用 C#.NET 创建 .odt 文件?

c# - 使用 IndexOf 在字符串之间提取值。怎么了?

c# - 在 C# 中将证书链添加到 PKCS#7/CMS 容器

image - 找到适合图像上绘制的多边形的最大正方形的最佳方法

c# - SplitContainer 被识别为 ActiveControl

c# - 用 C# 创建 Form2 并对其进行编辑

c# - GetChanges() 或 RowState 获取数据表的所有更改?

java - 如何截取 lwuit 应用程序的屏幕截图,但不在模拟器中