Disegnare nella Nonclient area di una Form con GDI+ e C#
Oggi un amico mi ha chiesto un esempio di come disegnare nella Nonclient area di una Windows Forms, cioè l'area normalmente in cui non è possibile posizionare controlli; tipicamente i bordi e la title-bar o caption. La soluzione prevede l'utilizzo di interoperability per poter richiamare direttamente le GDI API del sistema operativo.

Bisogna quindi intercettare i messaggi inviati dall'OS alla form eseguendo l'override del metodo WndProc e verificare che il messaggio sia WM_NCPAINT. A questo punto si può gestire il messaggio, ottenere un HDC con l'API GetHdc e quindi usare GDI+ Managed.

Qui una classe che wrappa le API necessarie, e un enum con alcun messaggi Windows:
internal class GdiNativeMethods
{
    [DllImport("user32.dll")]
    public extern static IntPtr GetDesktopWindow();

    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowDC(IntPtr hwnd);

    [DllImport("user32.dll")]
    public static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc);

    [DllImport("gdi32.dll")]
    public static extern UInt64 BitBlt
    (IntPtr hDestDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, System.Int32 dwRop);
}

internal enum WM_Message
{
    WM_NCCALCSIZE = 131,
    WM_NCPAINT = 133,
    WM_NCHITTEST = 132,
    WM_NCLBUTTONDOWN = 161,
}

Qui invece il codice che utilizza GDI+ e permette di disegnare intercettando i messaggi da WndProc:
public partial class Main : Form
{
    public Main()
    {
        InitializeComponent();

        base.SetStyle(ControlStyles.DoubleBuffer, true);
        base.SetStyle(ControlStyles.ResizeRedraw, true);
        base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        base.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        switch (m.Msg) {
            case (int)WM_Message.WM_NCPAINT:
                IntPtr hDC = GdiNativeMethods.GetWindowDC(this.Handle);
                Graphics gr = Graphics.FromHdc(hDC);

                int penSize = 3;
                gr.DrawRectangle(new Pen(Brushes.Red, penSize), 0, 0, this.Width - 1, this.Height - 1);
                gr.DrawLine(new Pen(Brushes.Green, penSize), 0, 0, this.Width - 1, 0);                    
                GdiNativeMethods.ReleaseDC(m.HWnd, hDC);
                m.Result = IntPtr.Zero;
                break;
        }            
    }
}

Qui è possibile scaricare l'applicazione di esempio: GDIPlusUnManaged

Matteo Migliore.
Comments have been closed on this topic.