[英]How to prevent a borderless Windows Form from flickering when resizing (C#)?
[C# .NET 4.0]
我正在學習 C# 並且我正在嘗試使用 C# 構建一個 Windows 窗體,它具有FormBorderStyle = FormBorderStyle.None
並且可以使用 Windows API 移動/調整大小。 例如,我使用用於 Google Chrome 和 Norton 360 的圓角或自定義(可移動/可調整大小)邊框設計作為表單的基礎。
到目前為止,我已經取得了很大的進步,並且一切正常,除了當我調整表單大小時,當您快速調整表單大小時,沿着右邊框和下邊框的長度會出現黑色/白色閃爍。
我試過在構造函數中添加this.DoubleBuffer = true
並且也試過this.SetStyles(ControlStyles.AllPaintInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
.
因為我是圖形方面的傻瓜,我喜歡控制表單的完整設計,所以我可以看到這是永遠困擾我的東西......所以如果有人可以幫助我解決這個問題,那么閃爍不再發生,這對我的學習過程非常有用。
我還應該提到我使用的是 Windows XP,所以我不確定這篇文章是否會對我有所幫助,因為它似乎專注於 Vista/7(使用 DWM)......並不是說我還不夠先進了解該帖子中的所有內容。
與 API 一起使用的代碼的兩部分如下。 我有一個 Windows API 的 WM_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);
}
WndProc 覆蓋方法:
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);
}
這是完整的代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace PracticeForm
{
public partial class Form2 : Form
{
[DllImport("user32.dll")]
private static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int cx, int cy);
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
private static extern bool DeleteObject(System.IntPtr hObject);
private const int cGrip = 20;
private const int cCaption = 35;
private const int cBorder = 7;
private Point mouseOffset;
public Form2()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.MaximumSize = new Size(670, 440);
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.ResizeRedraw |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
}
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);
}
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);
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private void button2_MouseClick(object sender, MouseEventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
mouseOffset = new Point(-e.X, -e.Y);
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Point p = Control.MousePosition;
p.Offset(mouseOffset.X, mouseOffset.Y);
Location = p;
}
}
private void label1_MouseDown(object sender, MouseEventArgs e)
{
mouseOffset = new Point(-e.X, -e.Y);
}
private void label1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Point p = Control.MousePosition;
p.Offset(mouseOffset.X, mouseOffset.Y);
Location = p;
}
}
}
}
謝謝您的幫助。
閃爍的發生是因為您的顯示器區域正在迅速改變顏色,而這又是因為您過度繪制 - 在同一像素上繪制了不止一件東西。
發生這種情況是因為:
要解決這些問題,您需要綜合考慮(越多越好)
雖然這是一個相當老的線程,並且 OP 可能已經找到了解決他的問題的方法並繼續前進,但我想添加一些額外的要點,以防它們被證明對正在解決類似問題的 .NET 開發人員有益.
首先,我要感謝您嘗試在 Windows XP 上解決這個問題。 我去過那里,在那里度過了很多小時,並因此吸取了所有艱難的教訓。 不幸的是,由於 Windows XP 缺少我們大多數人已經習慣的 DWM,因此沒有簡單的解決方案。
正確設置 ControlStyles 絕對重要——我還將包括:
SetStyle(ControlStyles.Opaque, True)
對您打算繪制的控件進行雙緩沖很重要,因為閃爍主要是由在監視器垂直回掃中間重繪的控件引起的。 僅僅因為您調用了 Invalidate(),並不一定意味着該控件會在您想要的時候重新繪制——您受 Windows 的支配,操作系統會在它准備好時執行它。 您可以在 Windows XP 上解決這個問題(就像我所做的那樣),方法是利用 DirecDraw 7 中的 WaitForVerticalBlank 之類的功能(Windows XP 上對該 API 有很多支持),還可以使用 GetVerticalBlankStatus 和 GetScanLine 相應地為渲染和演示計時。
僅在 Form 實際更改SIZE時才設置 Region ,而不是每次都在 Paint() 事件中:
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
System.IntPtr ptrBorder = CreateRoundRectRgn(0, 0,
this.ClientSize.Width, this.ClientSize.Height, 15, 15);
SetWindowRgn(this.Handle, ptrBorder, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip,
this.ClientSize.Height - cGrip, cGrip, cGrip);
ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
}
試試這個:
我的無邊框表單上有面板作為標題欄,標題欄面板上出現閃爍問題,並在表單加載中添加了此代碼,閃爍消失了。
var prop = TitleBar_panel.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
prop.SetValue(TitleBar_panel, true, null);
TitleBar_panel
是閃爍的控件。
編輯:現在只有當我從表單的左側調整它的大小時它才會閃爍。 所以它不是 100% 由這段代碼解決的
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.