簡體   English   中英

玻璃上的渲染控件:找到解決方案,需要雙緩沖/完善

[英]Rendering controls on glass: Solution found, needs double-buffering/perfecting

我(終於。)找到了一種在玻璃上呈現 Windows.Forms 控件的方法,該控件似乎沒有任何重大缺陷,也沒有任何大的實施時間。 它的靈感來自 Coded 的 這篇文章,它基本上解釋了如何在本地覆蓋控件的繪制以在它們之上繪制。

我使用這種方法將控件渲染到 bitmap 並在 NativeWindow 的繪畫區域上使用 GDI+ 和適當的 alpha 通道將其重新繪制。 實現很簡單,但可以完善可用性,但這不是這個問題的重點。 然而,結果非常令人滿意:

玻璃上的真實文本框

但是,需要修復兩個區域才能使其真正可用。

  1. 雙緩沖,因為這個疊加圖和真實控件之間的閃爍頻繁又可怕(自己用代碼測試一下)。 使用SetStyles(this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true)將基本控件設置為雙緩沖不起作用,但我懷疑我們可以通過一些試驗和錯誤使其工作。
  2. 某些控件不起作用 我已經能夠完成以下工作:

    • 文本框
    • 蒙面組合框
    • ComboBox (DropDownStyle == DropDownList)
    • 列表框
    • 選中列表框
    • 列表顯示
    • TreeView
    • 日期時間選擇器
    • 月歷

    但我不能讓這些工作,雖然我不明白為什么不。 我有根據的猜測是,我引用整個控件的實際 NativeWindow 句柄,而我需要引用它的“輸入”(文本)部分,可能是一個孩子。 歡迎 WinAPI 專家就如何獲得輸入 window 句柄提供任何幫助。

    • ComboBox (DropDownStyle != DropDownList)
    • 數字上下
    • 富文本框

但是修復雙緩沖將是可用性的主要焦點

這是一個示例用法:

new GlassControlRenderer(textBox1);

這是代碼:

public class GlassControlRenderer : NativeWindow
{
    private Control Control;
    private Bitmap Bitmap;
    private Graphics ControlGraphics;

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case 0xF: // WM_PAINT
            case 0x85: // WM_NCPAINT
            case 0x100: // WM_KEYDOWN
            case 0x200: // WM_MOUSEMOVE
            case 0x201: // WM_LBUTTONDOWN
                this.Control.Invalidate();
                base.WndProc(ref m);
                this.CustomPaint();
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }

    public GlassControlRenderer(Control control)
    {
        this.Control = control;
        this.Bitmap = new Bitmap(this.Control.Width, this.Control.Height);
        this.ControlGraphics = Graphics.FromHwnd(this.Control.Handle);
        this.AssignHandle(this.Control.Handle);
    }

    public void CustomPaint()
    {
        this.Control.DrawToBitmap(this.Bitmap, new Rectangle(0, 0, this.Control.Width, this.Control.Height));
        this.ControlGraphics.DrawImageUnscaled(this.Bitmap, -1, -1); // -1, -1 for content controls (e.g. TextBox, ListBox)
    }
}

我真的很高興解決這個問題,並且一勞永逸地在玻璃上進行渲染,對於所有 .NET 控件,沒有 WPF。

編輯:雙緩沖/防閃爍的可能路徑:

  • 刪除this.Control.Invalidate()行會刪除閃爍,但會中斷文本框中的輸入。
  • 我嘗試了 WM_SETREDRAW 方法和 SuspendLayout 方法,但沒有成功:

     [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam); private const int WM_SETREDRAW = 11; public static void SuspendDrawing(Control parent) { SendMessage(parent.Handle, WM_SETREDRAW, false, 0); } public static void ResumeDrawing(Control parent) { SendMessage(parent.Handle, WM_SETREDRAW, true, 0); parent.Refresh(); } protected override void WndProc(ref Message m) { switch (m.Msg) { case 0xF: // WM_PAINT case 0x85: // WM_NCPAINT case 0x100: // WM_KEYDOWN case 0x200: // WM_MOUSEMOVE case 0x201: // WM_LBUTTONDOWN //this.Control.Parent.SuspendLayout(); //GlassControlRenderer.SuspendDrawing(this.Control); //this.Control.Invalidate(); base.WndProc(ref m); this.CustomPaint(); //GlassControlRenderer.ResumeDrawing(this.Control); //this.Control.Parent.ResumeLayout(); break; default: base.WndProc(ref m); break; } }

這是一個閃爍少得多的版本,但仍然不完美。

public class GlassControlRenderer : NativeWindow
{
    private Control Control;
    private Bitmap Bitmap;
    private Graphics ControlGraphics;

    private object Lock = new object();

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case 0x14: // WM_ERASEBKGND
                this.CustomPaint();
                break;

            case 0x0F: // WM_PAINT
            case 0x85: // WM_NCPAINT

            case 0x100: // WM_KEYDOWN
            case 0x101: // WM_KEYUP
            case 0x102: // WM_CHAR

            case 0x200: // WM_MOUSEMOVE
            case 0x2A1: // WM_MOUSEHOVER
            case 0x201: // WM_LBUTTONDOWN
            case 0x202: // WM_LBUTTONUP
            case 0x285: // WM_IME_SELECT

            case 0x300: // WM_CUT
            case 0x301: // WM_COPY
            case 0x302: // WM_PASTE
            case 0x303: // WM_CLEAR
            case 0x304: // WM_UNDO
                base.WndProc(ref m);
                this.CustomPaint();
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }

    private Point Offset { get; set; }

    public GlassControlRenderer(Control control, int xOffset, int yOffset)
    {
        this.Offset = new Point(xOffset, yOffset);
        this.Control = control;
        this.Bitmap = new Bitmap(this.Control.Width, this.Control.Height);
        this.ControlGraphics = Graphics.FromHwnd(this.Control.Handle);
        this.AssignHandle(this.Control.Handle);
    }

    public void CustomPaint()
    {
        this.Control.DrawToBitmap(this.Bitmap, new Rectangle(0, 0, this.Control.Width, this.Control.Height));
        this.ControlGraphics.DrawImageUnscaled(this.Bitmap, this.Offset); // -1, -1 for content controls (e.g. TextBox, ListBox)
    }
}

我之前有閃爍的問題(表單上有很多控件,用戶控件)。 幾乎什么都試過了。 這對我有用:

您是否嘗試將其放入您的表格 class 中?

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
            cp.ExStyle |= 0x00080000; // WS_EX_LAYERED
            return cp;
        }
    }

在您的構造函數中,您必須啟用雙緩沖,否則它將不起作用:

this.DoubleBuffered = true;
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

它僅在啟用 aero 時有效,否則會使閃爍更加嚴重。

你也可以添加這個

  protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;

                cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
            return cp;
        }
    } 

到您的用戶控件 class。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM