2011-06-06 59 views
5

我目前正在構建一個來自System.Windows.Forms.ContainerControl的控件,該控件具有我需要繪製自己的邊框區域。由於沒有OnPaintNonClientArea重寫,我建立它自己這樣的(處理其他消息像WM_NCCALCSIZEWM_NCHITTEST等,爲簡潔,刪除):爲什麼從WM_NCPAINT中使用DrawImageUnscaled會導致閃爍?

protected override void WndProc(ref Message m) 
{ 
    switch (m.Msg) 
    { 
    case WM_NCPAINT: 
     IntPtr hDC = NativeApi.Methods.GetWindowDC(m.HWnd); 
     if (hDC != IntPtr.Zero) 
     { 
     using (Graphics canvas = Graphics.FromHdc(hDC)) 
     { 
      if (Width > 0 && Height > 0) 
      using (PaintEventArgs e = new PaintEventArgs(canvas, new Rectangle(0, 0, Width, Height))) 
      { 
       OnPaintNonClientArea(e); 
      } 
     } 
     NativeApi.Methods.ReleaseDC(m.HWnd, hDC); 
     } 
     m.Result = IntPtr.Zero; 
     break; 
    } 
    base.WndProc(ref m); 
} 

OnPaintNonClientArea,我所做的:

private void OnPaintNonClientArea(PaintEventArgs e) 
{ 
    if (_ncBuffer == null) 
    { 
    _ncBuffer = new Bitmap(Width, Height); 
    } 

    using (Graphics g = Graphics.FromImage(_ncBuffer)) 
    { 
    // painting occurs here ... 
    } 
    // this causes flickering 
    e.Graphics.DrawImageUnscaled(_ncBuffer, 0, 0, Width, Height); 
} 

離開OnPaintNonClientArea不變,這消除了閃爍:

protected override void WndProc(ref Message m) 
{ 
    switch (m.Msg) 
    { 
    case WM_NCPAINT: 
     using(Bitmap ncBitmap = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) 
     { 
     using(Graphics ncGraphics = Graphics.FromImage(ncBitmap)) 
     { 
      using (PaintEventArgs e = new PaintEventArgs(ncGraphics, new Rectangle(0, 0, Width, Height))) 
      { 
      OnPaintNonClientArea(e); 
      IntPtr hDCWin = NativeApi.Methods.GetWindowDC(m.HWnd); 
      IntPtr hDCImg = ncGraphics.GetHdc(); 
      IntPtr hBmp = ncBitmap.GetHbitmap(); 
      IntPtr hBmpOld = NativeApi.Methods.SelectObject(hDCImg, hBmp); 
      Padding p = GetNonClientArea(); 
      NativeApi.Methods.ExcludeClipRect(hDCWin, p.Left, p.Top,Width- p.Right, Height-p.Bottom); 
      NativeApi.Methods.BitBlt(hDCWin, 0, 0, Width, Height, hDCImg, 0, 0,NativeApi.TernaryRasterOperations.SRCCOPY); 
      NativeApi.Methods.SelectObject(hDCImg, hBmpOld); 
      NativeApi.Methods.DeleteObject(hBmp); 
      ncGraphics.ReleaseHdc(hDCImg); 
      NativeApi.Methods.ReleaseDC(m.HWnd, hDCWin); 
      } 
     } 
     } 
     m.Result = IntPtr.Zero; 
     break; 
    } 
    base.WndProc(ref m); 
} 

那麼,爲什麼DrawImageUnscaled導致這種閃爍?在繪製緩衝區之前,它似乎使用白色筆刷清除它的工作區域。我沒有找到澄清這個問題的文檔中的任何內容。如果它僅僅是控件周圍的一個小邊框就沒有太大關係,但NC區域內會顯示文本,因此該區域清晰可見,因此閃爍真的可見並且令人討厭。

相關問題我做的本地GDI的東西是正確的,還是有潛在的問題,我現在沒有看到?另外,創建ncBitmap時,我使用的是控件的寬度和高度,但GDI +是分辨率無關的,那裏是否有任何問題?

+0

您是否嘗試過在表單中​​使用Double Buffering?雙緩衝應該處理像這樣的圖形問題。另外,是否有SuspendLayout和PerformLayout等價物可用於在加載圖形對象之前停止更新控件? – 2011-06-15 09:39:24

+0

謝謝你。我用這個東西擺弄了很長一段時間,並嘗試了各種各樣的東西,有一次我用雙倍緩衝形式,這沒有什麼不同。我不認爲這適用於此,因爲我已經有效地實現了雙緩衝 - 我使用Graphics對象繪製了所有的東西,然後使用了'DrawImageUnscaled',我嘗試將緩衝區繪製到由' Graphics.FromHdc(HDC)'。它是DrawImageUnscaled,它首先使用白色筆刷擦除要繪製的區域,然後繪製Graphics對象的內容。不知道這是否可以解決。 – takrl 2011-06-15 11:38:52

回答

2

爲了避免在UserControl中閃爍,我對BufferedGraphics類有更好的運氣。

MSDN

這是一個選項嗎?

+1

+1我不知道的方法。但是爲了繪製非客戶區域,這與我嘗試的第一種方法一樣閃爍。 – takrl 2011-06-17 21:19:19