Как изменить цвет фрейма?

134
03 декабря 2020, 04:20

Хочу изменить цвет фрейма/рамки у формы. Большая кастомизация не требуется и убирать рамку и делать свою тоже не нужно. Как просто поменять цвет фрейма на другой? К примеру как на скриншотах (1 - оригинал, 2 - желаемый вид). На втором панель серая.

Answer 1

Обрабатывать WM_NCPAINT (и еще несколько сообщений) и рисовать эту рамку самостоятельно. Вот простейший пример, основанный на OpenSource-проекте customerborderform.codeplex.com. Он не совершенный, так как нет двойной буферизации и при изменении размера окно мерцает, но думаю как основа будет полезен.

Вспомогательные классы

NativeMethods

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsTest
{
    public class NativeMethods
    {

        #region WindowMessages
        public enum WindowMessages
        {
            WM_NULL = 0x0000,
            WM_CREATE = 0x0001,
            WM_DESTROY = 0x0002,
            WM_MOVE = 0x0003,
            WM_SIZE = 0x0005,
            WM_ACTIVATE = 0x0006,
            WM_SETFOCUS = 0x0007,
            WM_KILLFOCUS = 0x0008,
            WM_ENABLE = 0x000A,
            WM_SETREDRAW = 0x000B,
            WM_SETTEXT = 0x000C,
            WM_GETTEXT = 0x000D,
            WM_GETTEXTLENGTH = 0x000E,
            WM_PAINT = 0x000F,
            WM_CLOSE = 0x0010,
            WM_QUIT = 0x0012,
            WM_ERASEBKGND = 0x0014,
            WM_SYSCOLORCHANGE = 0x0015,
            WM_SHOWWINDOW = 0x0018,
            WM_ACTIVATEAPP = 0x001C,
            WM_SETCURSOR = 0x0020,
            WM_MOUSEACTIVATE = 0x0021,
            WM_GETMINMAXINFO = 0x24,
            WM_WINDOWPOSCHANGING = 0x0046,
            WM_WINDOWPOSCHANGED = 0x0047,
            WM_CONTEXTMENU = 0x007B,
            WM_STYLECHANGING = 0x007C,
            WM_STYLECHANGED = 0x007D,
            WM_DISPLAYCHANGE = 0x007E,
            WM_GETICON = 0x007F,
            WM_SETICON = 0x0080,
            // non client area
            WM_NCCREATE = 0x0081,
            WM_NCDESTROY = 0x0082,
            WM_NCCALCSIZE = 0x0083,
            WM_NCHITTEST = 0x84,
            WM_NCPAINT = 0x0085,
            WM_NCACTIVATE = 0x0086,
            WM_GETDLGCODE = 0x0087,
            WM_SYNCPAINT = 0x0088,
            // non client mouse
            WM_NCMOUSEMOVE = 0x00A0,
            WM_NCLBUTTONDOWN = 0x00A1,
            WM_NCLBUTTONUP = 0x00A2,
            WM_NCLBUTTONDBLCLK = 0x00A3,
            WM_NCRBUTTONDOWN = 0x00A4,
            WM_NCRBUTTONUP = 0x00A5,
            WM_NCRBUTTONDBLCLK = 0x00A6,
            WM_NCMBUTTONDOWN = 0x00A7,
            WM_NCMBUTTONUP = 0x00A8,
            WM_NCMBUTTONDBLCLK = 0x00A9,
            // keyboard
            WM_KEYDOWN = 0x0100,
            WM_KEYUP = 0x0101,
            WM_CHAR = 0x0102,
            WM_SYSCOMMAND = 0x0112,
            // menu
            WM_INITMENU = 0x0116,
            WM_INITMENUPOPUP = 0x0117,
            WM_MENUSELECT = 0x011F,
            WM_MENUCHAR = 0x0120,
            WM_ENTERIDLE = 0x0121,
            WM_MENURBUTTONUP = 0x0122,
            WM_MENUDRAG = 0x0123,
            WM_MENUGETOBJECT = 0x0124,
            WM_UNINITMENUPOPUP = 0x0125,
            WM_MENUCOMMAND = 0x0126,
            WM_CHANGEUISTATE = 0x0127,
            WM_UPDATEUISTATE = 0x0128,
            WM_QUERYUISTATE = 0x0129,
            // mouse
            WM_MOUSEFIRST = 0x0200,
            WM_MOUSEMOVE = 0x0200,
            WM_LBUTTONDOWN = 0x0201,
            WM_LBUTTONUP = 0x0202,
            WM_LBUTTONDBLCLK = 0x0203,
            WM_RBUTTONDOWN = 0x0204,
            WM_RBUTTONUP = 0x0205,
            WM_RBUTTONDBLCLK = 0x0206,
            WM_MBUTTONDOWN = 0x0207,
            WM_MBUTTONUP = 0x0208,
            WM_MBUTTONDBLCLK = 0x0209,
            WM_MOUSEWHEEL = 0x020A,
            WM_MOUSELAST = 0x020D,
            WM_PARENTNOTIFY = 0x0210,
            WM_ENTERMENULOOP = 0x0211,
            WM_EXITMENULOOP = 0x0212,
            WM_NEXTMENU = 0x0213,
            WM_SIZING = 0x0214,
            WM_CAPTURECHANGED = 0x0215,
            WM_MOVING = 0x0216,
            WM_ENTERSIZEMOVE = 0x0231,
            WM_EXITSIZEMOVE = 0x0232,
            WM_MOUSELEAVE = 0x02A3,
            WM_MOUSEHOVER = 0x02A1,
            WM_NCMOUSEHOVER = 0x02A0,
            WM_NCMOUSELEAVE = 0x02A2,
            WM_MDIACTIVATE = 0x0222,
            WM_HSCROLL = 0x0114,
            WM_VSCROLL = 0x0115,
            WM_PRINT = 0x0317,
            WM_PRINTCLIENT = 0x0318,
        }
        #endregion //WindowMessages

        #region DCX enum
        [Flags()]
        internal enum DCX
        {
            DCX_CACHE = 0x2,
            DCX_CLIPCHILDREN = 0x8,
            DCX_CLIPSIBLINGS = 0x10,
            DCX_EXCLUDERGN = 0x40,
            DCX_EXCLUDEUPDATE = 0x100,
            DCX_INTERSECTRGN = 0x80,
            DCX_INTERSECTUPDATE = 0x200,
            DCX_LOCKWINDOWUPDATE = 0x400,
            DCX_NORECOMPUTE = 0x100000,
            DCX_NORESETATTRS = 0x4,
            DCX_PARENTCLIP = 0x20,
            DCX_VALIDATE = 0x200000,
            DCX_WINDOW = 0x1,
        }
        #endregion //DCX



        #region RECT structure
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
            public RECT(int left, int top, int right, int bottom)
            {
                this.left = left;
                this.top = top;
                this.right = right;
                this.bottom = bottom;
            }
            public Rectangle Rect { get { return new Rectangle(this.left, this.top, this.right - this.left, this.bottom - this.top); } }
            public static RECT FromXYWH(int x, int y, int width, int height)
            {
                return new RECT(x,
                                y,
                                x + width,
                                y + height);
            }
            public static RECT FromRectangle(Rectangle rect)
            {
                return new RECT(rect.Left,
                                 rect.Top,
                                 rect.Right,
                                 rect.Bottom);
            }
        }
        #endregion RECT structure


        #region TRACKMOUSEEVENT structure
        [StructLayout(LayoutKind.Sequential)]
        public class TRACKMOUSEEVENT
        {
            public TRACKMOUSEEVENT()
            {
                this.cbSize = Marshal.SizeOf(typeof(NativeMethods.TRACKMOUSEEVENT));
                this.dwHoverTime = 100;
            }
            public int cbSize;
            public int dwFlags;
            public IntPtr hwndTrack;
            public int dwHoverTime;
        }
        #endregion

        #region TernaryRasterOperations enum
        public enum TernaryRasterOperations
        {
            SRCCOPY = 0x00CC0020, /* dest = source*/
            SRCPAINT = 0x00EE0086, /* dest = source OR dest*/
            SRCAND = 0x008800C6, /* dest = source AND dest*/
            SRCINVERT = 0x00660046, /* dest = source XOR dest*/
            SRCERASE = 0x00440328, /* dest = source AND (NOT dest )*/
            NOTSRCCOPY = 0x00330008, /* dest = (NOT source)*/
            NOTSRCERASE = 0x001100A6, /* dest = (NOT src) AND (NOT dest) */
            MERGECOPY = 0x00C000CA, /* dest = (source AND pattern)*/
            MERGEPAINT = 0x00BB0226, /* dest = (NOT source) OR dest*/
            PATCOPY = 0x00F00021, /* dest = pattern*/
            PATPAINT = 0x00FB0A09, /* dest = DPSnoo*/
            PATINVERT = 0x005A0049, /* dest = pattern XOR dest*/
            DSTINVERT = 0x00550009, /* dest = (NOT dest)*/
            BLACKNESS = 0x00000042, /* dest = BLACK*/
            WHITENESS = 0x00FF0062, /* dest = WHITE*/
        };
        #endregion
        #region Constants
        public static readonly IntPtr TRUE = new IntPtr(1);
        public static readonly IntPtr FALSE = new IntPtr(0);
        public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        #endregion
        #region API methods

        [DllImport("user32.dll")]
        public static extern IntPtr GetDCEx(IntPtr hwnd, IntPtr hrgnclip, uint fdwOptions);
        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hwnd, IntPtr hDC);
        [DllImport("user32.dll")]
        public static extern int GetWindowRect(IntPtr hwnd, ref RECT lpRect);

        public const int VK_LBUTTON = 0x01;
        public const int VK_RBUTTON = 0x02;


        public static int GetLastError()
        {
            return System.Runtime.InteropServices.Marshal.GetLastWin32Error();
        }

        [DllImport("gdi32.dll")]
        public static extern bool DeleteDC(IntPtr hDC);
        #endregion

    }
}

SystemMetric

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WindowsFormsTest
{
    public enum SystemMetric : int
    {

        /// <summary>
        /// The width of a window border, in pixels. This is equivalent to the SM_CXEDGE value for windows with the 3-D look.
        /// </summary>
        SM_CXBORDER = 5,
        /// <summary>
        /// The width of a cursor, in pixels. The system cannot create cursors of other sizes.
        /// </summary>
        SM_CXCURSOR = 13,
        /// <summary>
        /// This value is the same as SM_CXFIXEDFRAME.
        /// </summary>
        SM_CXDLGFRAME = 7,

        /// <summary>
        /// This value is the same as SM_CXSIZEFRAME.
        /// </summary>
        SM_CXFRAME = 32,

        /// <summary>
        /// The width of a button in a window caption or title bar, in pixels.
        /// </summary>
        SM_CXSIZE = 30,
        /// <summary>
        /// The thickness of the sizing border around the perimeter of a window that can be resized, in pixels. 
        /// SM_CXSIZEFRAME is the width of the horizontal border, and SM_CYSIZEFRAME is the height of the vertical border. 
        /// This value is the same as SM_CXFRAME.
        /// </summary>
        SM_CXSIZEFRAME = 32,

        /// <summary>
        /// The height of a window border, in pixels. This is equivalent to the SM_CYEDGE value for windows with the 3-D look.
        /// </summary>
        SM_CYBORDER = 6,
        /// <summary>
        /// The height of a caption area, in pixels.
        /// </summary>
        SM_CYCAPTION = 4,


        /// <summary>
        /// This value is the same as SM_CYSIZEFRAME.
        /// </summary>
        SM_CYFRAME = 33,

        /// <summary>
        /// The height of a button in a window caption or title bar, in pixels.
        /// </summary>
        SM_CYSIZE = 31,
        /// <summary>
        /// The thickness of the sizing border around the perimeter of a window that can be resized, in pixels. 
        /// SM_CXSIZEFRAME is the width of the horizontal border, and SM_CYSIZEFRAME is the height of the vertical border. 
        /// This value is the same as SM_CYFRAME.
        /// </summary>
        SM_CYSIZEFRAME = 33,
        /// <summary>
        /// The height of a small caption, in pixels.
        /// </summary>
        SM_CYSMCAPTION = 51,
        /// <summary>
        /// The recommended height of a small icon, in pixels. Small icons typically appear in window captions and in small icon view.
        /// </summary>
        SM_CYSMICON = 50,
        /// <summary>
        /// The height of small caption buttons, in pixels.
        /// </summary>
        SM_CYSMSIZE = 53,

    }
}

Собственно форма

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;
/*https://customerborderform.codeplex.com*/
namespace WindowsFormsTest
{
    public partial class Form1 : Form
    {
        /*Параметры отображения окна*/
        Color TextColor = Color.Black;//цвет текста
        Color FrameColor = Color.Red;//цвет рамки
        bool EnableNonClientAreaPaint = true;//использовать нестандартную рамку
        [DllImport("user32.dll")]
        static extern int GetSystemMetrics(SystemMetric smIndex);
        public Form1()
        {
            InitializeComponent();
        }        
        /// <summary>
        /// Custom client area paint
        /// </summary>        
        protected void OnNonClientAreaPaint(NonClientPaintEventArgs e)
        {
            /*Размеры рамки*/
            int w = GetSystemMetrics(SystemMetric.SM_CXSIZEFRAME);
            int h = GetSystemMetrics(SystemMetric.SM_CYSIZEFRAME);
            /*Размеры кнопки*/
            int w2 = (GetSystemMetrics(SystemMetric.SM_CXSIZE));
            int h2 = GetSystemMetrics(SystemMetric.SM_CYSIZE);
            Rectangle rc;
            StringFormat fmt=StringFormat.GenericDefault;            
            fmt.Alignment = StringAlignment.Center;
            fmt.LineAlignment = StringAlignment.Center;
            Brush br=new SolidBrush(TextColor);//brush for text
            Brush fbr = new SolidBrush(FrameColor);//brush for frame
            Font font = new Font("Arial", 14);
            Pen p = new Pen(FrameColor, (float)w);
            using(br)
            using(fbr)
            using(font)
            using (p)
            {
                /*border*/
                e.Graphics.DrawRectangle(p, w / 2.0f, h / 2.0f, e.Bounds.Width - w, e.Bounds.Height - h );
                /*title area*/
                rc = new Rectangle(w, h, e.Bounds.Width, h2);
                e.Graphics.FillRectangle(fbr, rc);
                e.Graphics.DrawString(this.Text, SystemFonts.CaptionFont, br, rc);
                /*close butt*/
                rc=new Rectangle(e.Bounds.Width-w-w2, h, w2, h2-4);                
                e.Graphics.DrawString("x", font, br, rc ,fmt);
                /*max butt*/
                rc = new Rectangle(e.Bounds.Width - w - w2*2, h, w2, h2-4);                
                e.Graphics.DrawString("■", font, br, rc, fmt);
                /*min butt*/
                rc = new Rectangle(e.Bounds.Width - w - w2*3, h, w2, h2-4);                
                e.Graphics.DrawString("–", font, br, rc, fmt);
            }
        }

        protected override void WndProc(ref Message m)
        {
            if (!this.EnableNonClientAreaPaint)
            {
                base.WndProc(ref m);
                return;
            }
            switch (m.Msg)
            {
                case (int)NativeMethods.WindowMessages.WM_NCPAINT:
                    {
                        // Here should all our painting occur, but...
                        DefWndProc(ref m);
                        WmNCPaint(ref m);
                        this.Refresh();
                        break;
                    }
                case (int)NativeMethods.WindowMessages.WM_NCACTIVATE:
                    {
                        // ... WM_NCACTIVATE does some painting directly 
                        // without bothering with WM_NCPAINT ...
                        WmNCActivate(ref m);
                        break;
                    }
                case (int)NativeMethods.WindowMessages.WM_SETTEXT:
                    {
                        // ... and some painting is required in here as well
                        WmSetText(ref m);
                        break;
                    }
                case (int)NativeMethods.WindowMessages.WM_NCMOUSEMOVE:
                    {
                        //PaintNonClientArea(this.Handle, (IntPtr)1);
                        //this.Refresh();
                        WmNCMouseMove(ref m);
                        break;
                    }
                case (int)NativeMethods.WindowMessages.WM_ERASEBKGND:
                    {
                        WmEraseBkgnd(ref m);
                        break;
                    }
                default:
                    {
                        base.WndProc(ref m);
                        break;
                    }
            }
        }
        #region Wm ... (Windows message...)
        protected void OnUpdateWindowState()
        { }
        private void WmEraseBkgnd(ref Message m)
        {
            base.WndProc(ref m);
            //Log(MethodInfo.GetCurrentMethod(), "{0}", WindowState);
            OnUpdateWindowState();
        }
        #endregion

        public Point PointToWindow(Point screenPoint)
        {
            return new Point(screenPoint.X - Location.X, screenPoint.Y - Location.Y);
        }

        #region WmNC ... (Windows message... Non Client)               
        /// <summary>
        /// Handle WmNCMouseMove, so buttons aren't repainted due to mouse movement
        /// </summary>        
        private void WmNCMouseMove(ref Message msg)
        {
            Point clientPoint = this.PointToWindow(new Point(msg.LParam.ToInt32()));            
            msg.Result = IntPtr.Zero;
        }

        private void PaintNonClientArea(IntPtr hWnd, IntPtr hRgn)
        {
            NativeMethods.RECT windowRect = new NativeMethods.RECT();
            if (NativeMethods.GetWindowRect(hWnd, ref windowRect) == 0)
                return;
            Rectangle bounds = new Rectangle(0, 0,
                windowRect.right - windowRect.left,
                windowRect.bottom - windowRect.top);
            if (bounds.Width == 0 || bounds.Height == 0)
                return;
            // The update region is clipped to the window frame. When wParam is 1, the entire window frame needs to be updated. 
            Region clipRegion = null;
            if (hRgn != (IntPtr)1)
                clipRegion = System.Drawing.Region.FromHrgn(hRgn);
            // MSDN states that only WINDOW and INTERSECTRGN are needed,
            // but other sources confirm that CACHE is required on Win9x
            // and you need CLIPSIBLINGS to prevent painting on overlapping windows.
            IntPtr hDC = NativeMethods.GetDCEx(/*hWnd*/this.Handle, /*hRgn*/(IntPtr)0,
                (int)(NativeMethods.DCX.DCX_WINDOW /*| NativeMethods.DCX.DCX_INTERSECTRGN*/
                    | NativeMethods.DCX.DCX_CACHE | NativeMethods.DCX.DCX_CLIPSIBLINGS));
            if (hDC == IntPtr.Zero)
                return;

            try
            {
                    using (Graphics g = Graphics.FromHdc(hDC))
                    {
                        //cliping rect is not cliping rect but actual rectangle
                        OnNonClientAreaPaint(new NonClientPaintEventArgs(g, bounds, clipRegion));
                    }
                    //NOTE: The Graphics object would realease the HDC on Dispose.
                    // So there is no need to call NativeMethods.ReleaseDC(msg.HWnd, hDC);
            }
            finally
            {
                NativeMethods.ReleaseDC(this.Handle, hDC);
            }
        }
        private void WmNCPaint(ref Message msg)
        {            
            // The WParam contains handle to clipRegion or 1 if entire window should be repainted
            PaintNonClientArea(msg.HWnd, (IntPtr)msg.WParam);
            // we handled everything
            msg.Result = NativeMethods.TRUE;
        }
        private void WmSetText(ref Message msg)
        {
            // allow the system to receive the new window title
            DefWndProc(ref msg);
            // repaint title bar
            PaintNonClientArea(msg.HWnd, (IntPtr)1);
        }
        private void WmNCActivate(ref Message msg)
        {           
            bool active = (msg.WParam == NativeMethods.TRUE);            
            if (WindowState == FormWindowState.Minimized)
                DefWndProc(ref msg);
            else
            {
                // repaint title bar
                PaintNonClientArea(msg.HWnd, (IntPtr)1);
                // allow to deactivate window
                msg.Result = NativeMethods.TRUE;
            }
        }
        #endregion

    }
    public class NonClientPaintEventArgs : EventArgs
    {
        public Graphics Graphics;
        public Rectangle Bounds;
        public Region clipRegion;
        public NonClientPaintEventArgs(Graphics g, Rectangle bounds, Region cr)
        {
            this.Graphics = g;
            this.Bounds = bounds;
            clipRegion = cr;
        }
    }
}

Вот как это выглядит

READ ALSO
Кастомный UI для видеоплеера(MpvPlayerUI.NET) на WPF

Кастомный UI для видеоплеера(MpvPlayerUI.NET) на WPF

В общем на GitGub нашел библиотеку MpvPlayerUINET https://github

101
Обратная транслитерация

Обратная транслитерация

Пишу транслитератор, а точнее, пытаюсь уже обработанное слово перевести на русский язык

91
Process is terminating due to StackOverflowException

Process is terminating due to StackOverflowException

При попытке сравнения структур , получаю следующее сообщение "Process is terminating due to StackOverflowException"

120
Как красиво вывести данные

Как красиво вывести данные

Есть таблица какие люди когда приходятВ таблице данные лежат как на фото1

129