Как настроить клиентскую область (ClientRectangle) в виде без полей?

Я хочу знать, можно ли установить клиентскую область формы без полей. Скажем, например, я определяю форму так:

Код

public class MyForm : Form
{
    public MyForm()
    {
        this.FormBorderStyle = FormBorderStyle.None;
    }
}

Результат

Форма с полной клиентской областью

Что я хочу сделать, так это указать клиентскую область, чтобы форма имела рамку (например, стандартную рамку Windows, но нарисованную на заказ).

Результат

Форма с пересчитанной клиентской областью

По сути, синяя область станет неклиентской областью, а серая останется клиентской.

Я попытался установить клиентскую область, но кажется, что это просто изменяет размер всей формы, поэтому не оставляет после себя «неклиентскую» область.

Это возможно?


person Matthew Layton    schedule 02.02.2015    source источник
comment
Вы пытаетесь скрыть его?... stackoverflow.com/questions/6329632/   -  person DonBoitnott    schedule 02.02.2015
comment
customerborderform.codeplex.com   -  person Sriram Sakthivel    schedule 02.02.2015


Ответы (1)


Это возможно, однако я не знаю, насколько хорошо это работает с формой Windows с параметром WindowStyle, установленным на Borderless. Используя функции PInvoke (Platform Invoke), вы можете удалить темы окон, что даст вам очень простую форму Windows. Затем вы можете использовать различные функции PInvoke для управления неклиентской областью формы окна.

Рекомендую прочитать эти темы. Они предназначены для приложений Win32 с использованием C++, но PInvoke — это процесс вызова этих собственных API-интерфейсов с использованием управляемого кода (C#).

WM_NCCALCSIZE: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632634(v=vs.85).aspx

WM_NCPAINT: https://msdn.microsoft.com/en-us/library/windows/desktop/dd145212(v=vs.85).aspx

GetDCEx: https://msdn.microsoft.com/en-us/library/windows/desktop/dd144873(v=vs.85).aspx

GetWindowDC: https://msdn.microsoft.com/en-us/library/windows/desktop/dd144947(v=vs.85).aspx

SetWindowTheme: https://msdn.microsoft.com/en-us/library/windows/desktop/bb759827(v=vs.85).aspx

Этот пример очень грубый, но обеспечивает базовую функциональность. Я не знаю, как SetWindowTheme работает в Windows 8 или 8.1, но в Windows 7 он дает окнам «классическую» тему.

public partial class MyForm : Form
{
    //Window Messages
    public const uint WM_NCPAINT = 0x85;
    public const uint WM_NCCALCSIZE = 0x83;
    public const uint WM_NCHITTEST = 0x84;

    //GetDCEx Flags
    public const int DCX_WINDOW = 0x00000001;
    public const int DCX_CACHE = 0x00000002;
    public const int DCX_PARENTCLIP = 0x00000020;
    public const int DCX_CLIPSIBLINGS = 0x00000010;
    public const int DCX_CLIPCHILDREN = 0x00000008;
    public const int DCX_NORESETATTRS = 0x00000004;
    public const int DCX_LOCKWINDOWUPDATE = 0x00000400;
    public const int DCX_EXCLUDERGN = 0x00000040;
    public const int DCX_INTERSECTRGN = 0x00000080;
    public const int DCX_INTERSECTUPDATE = 0x00000200;
    public const int DCX_VALIDATE = 0x00200000;

    //RECT Structure
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct RECT
    {
        public int left, top, right, bottom;
    }

    //WINDOWPOS Structure
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct WINDOWPOS
    {
        public IntPtr hwnd;
        public IntPtr hwndinsertafter;
        public int x, y, cx, cy;
        public int flags;
    }

    //NCCALCSIZE_PARAMS Structure
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct NCCALCSIZE_PARAMS
    {
        public RECT rgrc0, rgrc1, rgrc2;
        public WINDOWPOS lppos;
    }

    //SetWindowTheme UXtheme Function
    [System.Runtime.InteropServices.DllImport("uxtheme.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
    public static extern int SetWindowTheme(
        IntPtr hWnd,
        String pszSubAppName,
        String pszSubIdList);

    //GetWindowRect User32 Function
    [System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling = true)]
    [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
    public static extern bool GetWindowRect(
        IntPtr hwnd,
        out  RECT lpRect
        );

    //GetWindowDC User32 Function
    [System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling = true)]
    public static extern IntPtr GetWindowDC(
        IntPtr hWnd
        );

    //GetDCEx User32 Function
    [System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling = true)]
    public static extern IntPtr GetDCEx(
        IntPtr hWnd,
        IntPtr hrgnClip,
        int flags
        );

    //Window Procedure Hook
    protected override void WndProc(ref Message m)
    {
        //Don't style window in designer...
        if (DesignMode)
            base.WndProc(ref m);

        //Handle Message
        switch ((uint)m.Msg)
        {
            case WM_NCCALCSIZE: WmNCCalcSize(ref m); break;
            case WM_NCPAINT: WmNCPaint(ref m); break;
            default: base.WndProc(ref m); break;
        }
    }

    //Handle Creation
    protected override void OnHandleCreated(EventArgs e)
    {
        //Base Procedure...
        base.OnHandleCreated(e);

        //Remove Theme
        SetWindowTheme(this.Handle, string.Empty, string.Empty);
    }

    //WM_NCCALCSIZE
    private void WmNCCalcSize(ref Message m)
    {
        //Get Window Rect
        RECT formRect = new RECT();
        GetWindowRect(m.HWnd, out formRect);

        //Check WPARAM
        if (m.WParam != IntPtr.Zero)    //TRUE
        {
            //When TRUE, LPARAM Points to a NCCALCSIZE_PARAMS structure
            var nccsp = (NCCALCSIZE_PARAMS)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));

            //We're adjusting the size of the client area here. Right now, the client area is the whole form.
            //Adding to the Top, Bottom, Left, and Right will size the client area.
            nccsp.rgrc0.top += 30;      //30-pixel top border
            nccsp.rgrc0.bottom -= 4;    //4-pixel bottom (resize) border
            nccsp.rgrc0.left += 4;      //4-pixel left (resize) border
            nccsp.rgrc0.right -= 4;     //4-pixel right (resize) border

            //Set the structure back into memory
            System.Runtime.InteropServices.Marshal.StructureToPtr(nccsp, m.LParam, true);
        }
        else    //FALSE
        {
            //When FALSE, LPARAM Points to a RECT structure
            var clnRect = (RECT)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(RECT));

            //Like before, we're adjusting the rectangle...
            //Adding to the Top, Bottom, Left, and Right will size the client area.
            clnRect.top += 30;      //30-pixel top border
            clnRect.bottom -= 4;    //4-pixel bottom (resize) border
            clnRect.left += 4;      //4-pixel left (resize) border
            clnRect.right -= 4;     //4-pixel right (resize) border

            //Set the structure back into memory
            System.Runtime.InteropServices.Marshal.StructureToPtr(clnRect, m.LParam, true);
        }

        //Return Zero
        m.Result = IntPtr.Zero;
    }

    //WM_NCPAINT
    private void WmNCPaint(ref Message m)
    {
        //Store HDC
        IntPtr HDC = IntPtr.Zero;
        Graphics gfx = null;

        //Check the WPARAM
        if(m.WParam == (IntPtr)1)
        {
            //For reasons unknown to me, the update region doesn't contain valid data and calling GetDCEx will do nothing.
            //So I call GetWindowDC and exclude the area using System.Drawing.Graphics instead.

            //Graphics Object from HDC
            HDC = GetWindowDC(m.HWnd);
            gfx = Graphics.FromHdc(HDC);

            //Exclude Client Area
            gfx.ExcludeClip(new Rectangle(4, 30, Width - 8, 34));  //Exclude Client Area (GetWindowDC grabs the WHOLE window's graphics handle)
        }
        else
        {
            //Graphics Object from HDC
            HDC = GetDCEx(m.HWnd, m.WParam, DCX_WINDOW | DCX_INTERSECTRGN);
            gfx = Graphics.FromHdc(HDC);
        }

        //Call Paint
        using (PaintEventArgs ncPaintArgs = new PaintEventArgs(gfx, new Rectangle(0, 0, Width, Height)))
            MyForm_NCPaint(this, ncPaintArgs);

        //Return Zero
        m.Result = IntPtr.Zero;
    }

    public MyForm()
    {
        InitializeComponent();
    }

    private void MyForm_NCPaint(object sender, PaintEventArgs e)
    {
        //Clear
        e.Graphics.Clear(Color.Green);
    }
}

Результат приведенного выше кода

person Mike    schedule 22.04.2015