2013-05-09 48 views
1

[C#.NET 4.0]如何防止調整大小(C#)時無邊界Windows窗體閃爍?

我學習C#,我試圖建立有FormBorderStyle = FormBorderStyle.None和可移動/使用Windows API調整Windows窗體中使用C#。舉個例子,我使用圓角或用於Google Chrome和Norton 360的自定義(可移動/可調整大小)邊框設計作爲我的表單的基礎。

我已經取得了很大的進步,到目前爲止,並得到的一切工作,除了當我調整的形式,有黑/白一起閃爍當調整表格右側和底部邊框的長度快速

我嘗試添加在構造函數中this.DoubleBuffer = true,並且還試圖this.SetStyles(ControlStyles.AllPaintInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);

因爲我是圖形方面的東西,我喜歡控制窗體的完整設計,所以我可以看到這是永遠困擾我的東西... 所以如果有人可以幫助我解決這個問題,所以閃爍不再發生,這將對我的學習過程非常有用。

我還要提到的是,我使用的是Windows XP,所以我不知道this post會幫助我,因爲它似乎集中在Vista/7的(與DWM)......不是我」米先進到足以理解這篇文章中的所有內容。

與該API工作的代碼的兩個部分的下方。我有一個WM_NCHITTEST的Windows API公開枚舉...你可以看到值in this link

的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; 
      } 
     } 
    } 
} 

感謝您的幫助。

回答

0

只設置區域時,形式實際上在paint()事件不會每次都有改變SIZE,:

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); 
    } 
+0

這可能會稍微好一些,但它仍然閃爍非常糟糕。另外,出於某種原因,您的代碼似乎已經覆蓋了我的表單開始位置(CenterScreen)。 – user2063351 2013-05-09 18:00:25

+0

您是否有圖像作爲表單的背景?或者可能是Paint()事件中的一些更復雜的繪畫? – 2013-05-09 18:25:39

+0

不,表單中的所有內容都是在代碼中創建的(除了枚舉類以外,我的所有代碼都列在了我的文章中)。這只是一個矩形區域,覆蓋無邊界形式來創建圓角(據我瞭解)。當然,還有自定義SizeGrip的附加矩形。我認爲創建圓角的矩形是導致問題的原因,因爲當我完全刪除該CreateRoundRectRgn代碼時,它看起來不會閃爍(或閃爍幾乎不如壞)。但圓角對我來說是一個重要的視覺實現。 – user2063351 2013-05-09 19:03:57

1

閃爍是因爲你的顯示器領域正在迅速改變顏色,這反過來又發生因爲你正在透支 - 在同一像素上繪製不止一件東西。

這是因爲:

  • 如果你是重繪緩慢,那麼東西,是在屏幕上(例如窗口邊框),你繪製了會一會兒可見。例如用戶可能會看到滾動條的兩個副本,直到用完表單內容完成擦除舊副本。
  • 窗口會自動爲您擦除窗口的背景,通常爲白色。繪圖中任何不是白色的區域因此在透過正確的圖像之前會瞬間變白。
  • ,如果你在同一個地方畫多的事情,你會看到閃爍,你不斷在屏幕

要解決這些問題的那個區域改變顏色,你需要的東西的結合(更越好)

  • 禁用擦除背景,或將擦除色彩的主色圖像
  • 優化重繪代碼,使其速度更快,所以閃爍不太突出
  • 優化你重繪代碼以消除過度繪製。例如。要在矩形頁面周圍放置邊框,可以繪製背景顏色並將其與頁面重疊繪製,但會閃爍。相反,將頂部邊框繪製爲矩形,然後將底部左側和右側繪製,然後在中間繪製頁面。由於nopixels不止一次被繪製,它不會閃爍
  • 在控件上啓用DoubleBuffered模式。這樣,所有的繪圖實際上都會發生在內存中的位圖圖像中,然後最終圖像被複制到屏幕上,以便每個像素只顯示一次,而且沒有閃爍。
+0

我很感激你花時間來解釋事情......但是,由於我還在學習,很多你寫的東西沒有任何意義(儘管我會查找一些東西)。我想指出,我在我的帖子中指出,我不僅使用您提到的DoubleBuffered模式,而且還使用SetStyles(ControlStyles ...)無濟於事。 – user2063351 2013-05-09 19:06:58

+0

我確實看到我在同一時間在同一個地方繪製了兩件東西,但我還沒有足夠的經驗來實現您的建議,以解決我繪製的矩形之間的像素繪製衝突的形式和矩形我畫的服務作爲SizeGrip。 – user2063351 2013-05-09 19:12:01

+0

你爲「沒有足夠經驗」做了相當先進的事情:-)嘗試重載OnPaintBackground(不做任何事情,避免雙重油漆),優化你的代碼,這樣你就不會在你的OnPaint中做任何你不喜歡的事情不需要 - 例如在OnSizeChanged中創建您的region/rects而不是每個繪製請求。另請注意,更改窗口區域可能會導致重繪,本身可能會導致閃爍。 – 2013-05-09 20:34:09

0

儘管這是一個相當古老的線程,OP很可能找到了解決方案並繼續前進,但我想添加一些附加點以防他們對.NET開發人員有利。正在解決類似的問題。

首先,我的帽子熄滅於你試圖在Windows XP中解決這個問題。我去過那裏,花了很多時間在那裏學習了所有艱難的課程。不幸的是,由於Windows XP缺乏我們大多數人習以爲常的DWM,因此沒有簡單的解決方案。

設置了正確的ControlStyles絕對是至關重要的 - 我還包括:

SetStyle(ControlStyles.Opaque, True) 

雙緩衝您打算繪製控制是重要的,因爲閃爍被控制主要是引起被重新繪製在中間的顯示器垂直回掃。僅僅因爲你調用了Invalidate(),它並不一定意味着控件將在你想要的時候重新繪製 - 你將受到Windows的支配,並且操作系統會在它準備好時執行它。您可以通過從DirecDraw 7借力像WaitForVerticalBlank功能(在Windows XP很多支持該API),並通過使用GetVerticalBlankStatus和GetScanLine相應地計時您的渲染和演示解決這個(像我一樣),在Windows XP上。