Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/257.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何防止无边框窗口窗体在调整大小(C#)时闪烁?_C#_.net_Winforms_Winapi_Windows Xp - Fatal编程技术网

如何防止无边框窗口窗体在调整大小(C#)时闪烁?

如何防止无边框窗口窗体在调整大小(C#)时闪烁?,c#,.net,winforms,winapi,windows-xp,C#,.net,Winforms,Winapi,Windows Xp,[C#.NET 4.0] 我正在学习C#,我正在尝试使用C#构建一个Windows窗体,它具有FormBorderStyle=FormBorderStyle.None,并且可以使用Windows API移动/调整大小。例如,我使用谷歌Chrome和诺顿360使用的圆角或自定义(可移动/可调整大小)边框设计作为表单的基础 到目前为止,我已经取得了很大的进步,所有的工作都顺利进行,除了当我调整窗体大小时,当您快速调整窗体大小时,右边框和下边框的长度会出现黑白闪烁 我已经尝试在构造函数中添加this.

[C#.NET 4.0]

我正在学习C#,我正在尝试使用C#构建一个Windows窗体,它具有
FormBorderStyle=FormBorderStyle.None
,并且可以使用Windows API移动/调整大小。例如,我使用谷歌Chrome和诺顿360使用的圆角或自定义(可移动/可调整大小)边框设计作为表单的基础

到目前为止,我已经取得了很大的进步,所有的工作都顺利进行,除了当我调整窗体大小时,当您快速调整窗体大小时,右边框和下边框的长度会出现黑白闪烁

我已经尝试在构造函数中添加
this.DoubleBuffer=true
,也尝试了
this.SetStyles(ControlStyles.AllPaintInWmPaint | ControlStyles.optimizedublebuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint,true)

因为我喜欢图形方面的东西,我喜欢控制整个表单的设计,所以我可以看到这会永远困扰我……所以如果有人能帮我解决这个问题,这样闪烁就不会再发生了,这对我的学习过程会非常有用

我还应该提到,我正在使用Windows XP,所以我不确定是否会对我有所帮助,因为它似乎专注于Vista/7(使用DWM)…并不是说我已经足够先进,能够理解该帖子中的所有内容

下面是使用API的代码的两部分。我有一个针对Windows API的WM\u NCHITTEST的公共枚举…您可以看到值

OnPaint覆盖方法

protected override void OnPaint(PaintEventArgs e)
{
    System.IntPtr ptrBorder = CreateRoundRectRgn(0, 0,
        this.ClientSize.Width, this.ClientSize.Height, 15, 15);

    SetWindowRgn(this.Handle, ptrBorder, true);

    Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip,
        this.ClientSize.Height - cGrip, cGrip, cGrip);
    ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
    rc = new Rectangle(0, 0, this.ClientSize.Width, 32);
    e.Graphics.FillRectangle(Brushes.SlateGray, rc);
}
protected override void WndProc(ref Message m)
{
    if (m.Msg == (int)HitTest.WM_NCHITTEST)
    {
        // Trap WM_NCHITTEST
        Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
        pos = this.PointToClient(pos);

        if (pos.Y < cCaption)
        {
            m.Result = (IntPtr)HitTest.HTCAPTION;
            return;
        }

        if (pos.X <= cGrip && pos.Y >= this.ClientSize.Height - cGrip)
        {
            m.Result = (IntPtr)HitTest.HTBOTTOMLEFT;
            return;
        }

        if (pos.X >= this.ClientSize.Width - cGrip &&
            pos.Y >= this.ClientSize.Height - cGrip)
        {
            m.Result = (IntPtr)HitTest.HTBOTTOMRIGHT;
            return;
        }

        if (pos.X >= this.ClientSize.Width - cBorder)
        {
            m.Result = (IntPtr)HitTest.HTRIGHT;
            return;
        }

        if (pos.Y >= this.ClientSize.Height - cBorder)
        {
            m.Result = (IntPtr)HitTest.HTBOTTOM;
            return;
        }

        if (pos.X <= cBorder)
        {
            m.Result = (IntPtr)HitTest.HTLEFT;
            return;
        }
    }

    base.WndProc(ref m);
}
WndProc覆盖方法

protected override void OnPaint(PaintEventArgs e)
{
    System.IntPtr ptrBorder = CreateRoundRectRgn(0, 0,
        this.ClientSize.Width, this.ClientSize.Height, 15, 15);

    SetWindowRgn(this.Handle, ptrBorder, true);

    Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip,
        this.ClientSize.Height - cGrip, cGrip, cGrip);
    ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
    rc = new Rectangle(0, 0, this.ClientSize.Width, 32);
    e.Graphics.FillRectangle(Brushes.SlateGray, rc);
}
protected override void WndProc(ref Message m)
{
    if (m.Msg == (int)HitTest.WM_NCHITTEST)
    {
        // Trap WM_NCHITTEST
        Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
        pos = this.PointToClient(pos);

        if (pos.Y < cCaption)
        {
            m.Result = (IntPtr)HitTest.HTCAPTION;
            return;
        }

        if (pos.X <= cGrip && pos.Y >= this.ClientSize.Height - cGrip)
        {
            m.Result = (IntPtr)HitTest.HTBOTTOMLEFT;
            return;
        }

        if (pos.X >= this.ClientSize.Width - cGrip &&
            pos.Y >= this.ClientSize.Height - cGrip)
        {
            m.Result = (IntPtr)HitTest.HTBOTTOMRIGHT;
            return;
        }

        if (pos.X >= this.ClientSize.Width - cBorder)
        {
            m.Result = (IntPtr)HitTest.HTRIGHT;
            return;
        }

        if (pos.Y >= this.ClientSize.Height - cBorder)
        {
            m.Result = (IntPtr)HitTest.HTBOTTOM;
            return;
        }

        if (pos.X <= cBorder)
        {
            m.Result = (IntPtr)HitTest.HTLEFT;
            return;
        }
    }

    base.WndProc(ref m);
}
受保护的覆盖无效WndProc(参考消息m)
{
if(m.Msg==(int)HitTest.WM\n chittest)
{
//陷阱WM_NCHITTEST
点位置=新点(m.LParam.ToInt32()&0xffff,m.LParam.ToInt32()>>16);
pos=此点到客户端(pos);
如果(位置Y=this.ClientSize.Width-cGrip&&
位置Y>=this.ClientSize.Height-cGrip)
{
m、 结果=(IntPtr)HitTest.HTBOTTOMRIGHT;
返回;
}
如果(pos.X>=this.ClientSize.Width-cBorder)
{
m、 结果=(IntPtr)HitTest.HTRIGHT;
返回;
}
如果(pos.Y>=this.ClientSize.Height-cBorder)
{
m、 结果=(IntPtr)HitTest.HTBOTTOM;
返回;
}

如果(pos.X仅设置窗体实际更改大小时的区域,而不是每次在Paint()事件中:


闪烁的发生是因为你的显示器区域的颜色变化很快,而这反过来又是因为你在同一个像素上画了不止一件东西

这是因为:

  • 如果重绘速度较慢,则屏幕上正在绘制的内容(例如窗口边框)将在一段时间内可见。例如,用户可能会看到滚动条的两个副本,直到您用表单内容擦除旧的一个
  • windows会自动为您擦除窗口的背景,通常为白色。因此,在您使用正确的图像进行过度绘制之前,图形中任何非白色区域都会闪烁白色片刻
  • 如果你在同一个地方画多个东西,当你在屏幕的那个区域不断改变颜色时,你会看到闪烁
要解决这些问题,您需要综合考虑(越多越好)

  • 禁用擦除背景,或将擦除颜色设置为图像中的主色
  • 优化重绘代码以使其更快,因此闪烁不太明显
  • 优化你的重绘代码以消除过度绘制。例如,在矩形页面周围放置一个边框,可以绘制背景颜色并用页面覆盖它,但这会闪烁。相反,将顶部边框绘制为矩形,然后左下左右,然后在中间绘制页面。闪烁
  • 在控件上启用双缓冲模式。这样,所有绘图实际上都会在内存中生成位图图像,然后将最终图像复制到屏幕上,这样每个像素只显示一次,不会闪烁

虽然这是一个相当古老的线程,OP可能已经找到了解决问题的方法并继续前进,但我想补充几点,以防它们对正在处理类似问题的.NET开发人员有益

首先,我要向你致敬,因为你试图在Windows XP上解决这个问题。我在那里呆了很多小时,并因此学到了所有的惨痛教训。不幸的是,因为Windows XP缺少我们大多数人已经习惯的DWM,所以没有简单的解决方案

正确设置控件样式是绝对重要的--我还将包括:

SetStyle(ControlStyles.Opaque, True)

双缓冲你要画的控件很重要,因为闪烁主要是由控件在监视器垂直回扫中间重绘引起的。,这并不一定意味着控件会在你想要的时候重新绘制——你受Windows的摆布,操作系统会在它准备好的时候重新绘制。你可以在Windows XP上通过利用DirecDraw 7中的WaitForVerticalBlank等函数来解决这个问题(就像我所做的那样)(Windows XP上对该API的大量支持)另外,还可以使用GetVerticalBlankStatus和GetScanLine根据需要对渲染和演示进行计时

var prop = TitleBar_panel.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
            prop.SetValue(TitleBar_panel, true, null);