6

enter image description here

How could I change the slide button color? not border color and not slide item colors.

I already change the slide item colors

enter image description here

Is there any way to change the color?

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
mr.G
  • 109
  • 1
  • 7

1 Answers1

20

Flat ComboBox - Change border color and Dropdown button color

You need to handle WM_PAINT yourself and draw the border and the dropdown rectangle. This is the way that internal ComboBox.FlatComboAdapter class of .Net Framework works.

In this post, I've created a FlatComboBox, which draws the border and the dropdown in a flat style, having the following additional properties:

  • BorderColor: used for border and for the dropdown arrow
  • ButtonColor: used for dropdown area color.

enter image description here enter image description here

Example - .NET Framework 4.8 and .NET 7.0

You can download or clone a working example for .NET Framework 4.8 and .NET 7.0 in the following links:

Code

Here is the code of FlatComboBox:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class FlatComboBox : ComboBox
{
    private Color borderColor = Color.Gray;
    [DefaultValue(typeof(Color), "Gray")]
    public Color BorderColor
    {
        get { return borderColor; }
        set
        {
            if (borderColor != value)
            {
                borderColor = value;
                Invalidate();
            }
        }
    }
    private Color buttonColor = Color.LightGray;
    [DefaultValue(typeof(Color), "LightGray")]
    public Color ButtonColor
    {
        get { return buttonColor; }
        set
        {
            if (buttonColor != value)
            {
                buttonColor = value;
                Invalidate();
            }
        }
    }
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_PAINT && DropDownStyle != ComboBoxStyle.Simple)
        {
            var clientRect = ClientRectangle;
            var dropDownButtonWidth = SystemInformation.HorizontalScrollBarArrowWidth;
            var outerBorder = new Rectangle(clientRect.Location,
                new Size(clientRect.Width - 1, clientRect.Height - 1));
            var innerBorder = new Rectangle(outerBorder.X + 1, outerBorder.Y + 1,
                outerBorder.Width - dropDownButtonWidth - 2, outerBorder.Height - 2);
            var innerInnerBorder = new Rectangle(innerBorder.X + 1, innerBorder.Y + 1,
                innerBorder.Width - 2, innerBorder.Height - 2);
            var dropDownRect = new Rectangle(innerBorder.Right + 1, innerBorder.Y,
                dropDownButtonWidth, innerBorder.Height + 1);
            if (RightToLeft == RightToLeft.Yes)
            {
                innerBorder.X = clientRect.Width - innerBorder.Right;
                innerInnerBorder.X = clientRect.Width - innerInnerBorder.Right;
                dropDownRect.X = clientRect.Width - dropDownRect.Right;
                dropDownRect.Width += 1;
            }
            var innerBorderColor = Enabled ? BackColor : SystemColors.Control;
            var outerBorderColor = Enabled ? BorderColor : SystemColors.ControlDark;
            var buttonColor = Enabled ? ButtonColor : SystemColors.Control;
            var middle = new Point(dropDownRect.Left + dropDownRect.Width / 2,
                dropDownRect.Top + dropDownRect.Height / 2);
            var arrow = new Point[]
            {
                new Point(middle.X - 3, middle.Y - 2),
                new Point(middle.X + 4, middle.Y - 2),
                new Point(middle.X, middle.Y + 2)
            };
            var ps = new PAINTSTRUCT();
            bool shoulEndPaint = false;
            IntPtr dc;
            if (m.WParam == IntPtr.Zero)
            {
                dc = BeginPaint(Handle, ref ps);
                m.WParam = dc;
                shoulEndPaint = true;
            }
            else
            {
                dc = m.WParam;
            }
            var rgn = CreateRectRgn(innerInnerBorder.Left, innerInnerBorder.Top, 
                innerInnerBorder.Right, innerInnerBorder.Bottom);
            SelectClipRgn(dc, rgn);
            DefWndProc(ref m);
            DeleteObject(rgn);
            rgn = CreateRectRgn(clientRect.Left, clientRect.Top, 
                clientRect.Right, clientRect.Bottom);
            SelectClipRgn(dc, rgn);
            using (var g = Graphics.FromHdc(dc))
            {
                using (var b = new SolidBrush(buttonColor))
                {
                    g.FillRectangle(b, dropDownRect);
                }
                using (var b = new SolidBrush(outerBorderColor))
                {
                    g.FillPolygon(b, arrow);
                }
                using (var p = new Pen(innerBorderColor))
                {
                    g.DrawRectangle(p, innerBorder);
                    g.DrawRectangle(p, innerInnerBorder);
                }
                                    using (var p = new Pen(outerBorderColor))
                {
                    g.DrawRectangle(p, outerBorder);
                }
            }
            if (shoulEndPaint)
                EndPaint(Handle, ref ps);
            DeleteObject(rgn);
        }
        else
            base.WndProc(ref m);
    }

    private const int WM_PAINT = 0xF;
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int L, T, R, B;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct PAINTSTRUCT
    {
        public IntPtr hdc;
        public bool fErase;
        public int rcPaint_left;
        public int rcPaint_top;
        public int rcPaint_right;
        public int rcPaint_bottom;
        public bool fRestore;
        public bool fIncUpdate;
        public int reserved1;
        public int reserved2;
        public int reserved3;
        public int reserved4;
        public int reserved5;
        public int reserved6;
        public int reserved7;
        public int reserved8;
    }
    [DllImport("user32.dll")]
    private static extern IntPtr BeginPaint(IntPtr hWnd,
        [In, Out] ref PAINTSTRUCT lpPaint);

    [DllImport("user32.dll")]
    private static extern bool EndPaint(IntPtr hWnd, ref PAINTSTRUCT lpPaint);

    [DllImport("gdi32.dll")]
    public static extern int SelectClipRgn(IntPtr hDC, IntPtr hRgn);

    [DllImport("user32.dll")]
    public static extern int GetUpdateRgn(IntPtr hwnd, IntPtr hrgn, bool fErase);
    public enum RegionFlags
    {
        ERROR = 0,
        NULLREGION = 1,
        SIMPLEREGION = 2,
        COMPLEXREGION = 3,
    }
    [DllImport("gdi32.dll")]
    internal static extern bool DeleteObject(IntPtr hObject);

    [DllImport("gdi32.dll")]
    private static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2);
}

Related Posts

You may also want to take a look at the following flat style controls:

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • I'd suggest inverting your flow control. No need to run all that on every single windows message (including ones you don't care about). You can short circuit by doing something like `if (!youCareAboutThisMessage) { base.WndProc(ref m); return; }` – Zer0 Jan 31 '21 at 07:26
  • @Zer0 Makes sense, I didn't invert the control flow, but I moved the declarations into the if block. Just as a side-node: this is a very quick implementation and I assume future readers care about the performance themselves. There are a lot of considerations that should be taken into account while implementing something for production environments. I'll leave it to the future readers to do more optimizations/corrections. – Reza Aghaei Jan 31 '21 at 07:33
  • @mr.G You may be interested in [FlatNumericUpdown](https://stackoverflow.com/a/65879068/3110834) and [TextBox with BorderColor](https://stackoverflow.com/a/39420512/3110834). – Reza Aghaei Jan 31 '21 at 07:40
  • @RezaAghaei Thx a lot :) , but there's some problem in the code If I use dropdown style dropdownList the combobox1.text doesn't visible – mr.G Jan 31 '21 at 08:26
  • @mr.G I'll take a look and update the answer. – Reza Aghaei Jan 31 '21 at 08:34
  • @mr.G I fixed it for now, however I may change the fix in future. I'll update you if I changed it. The way that I render the background is a bit different and I may change it. – Reza Aghaei Jan 31 '21 at 09:09
  • @RezaAghaei Yes. If i used this code(dropdownList), blue focus always on – mr.G Jan 31 '21 at 12:38
  • @mr.G I don't have this issue, but if you want to customize it here is the piece of the code which draws background: `using (var brush = new SolidBrush(backColor)) ...` change it to what you like. – Reza Aghaei Jan 31 '21 at 14:29
  • I believe the control is good enough as a start point, for further customization. But I'll try to improve it as soon as I find a bit of time :) – Reza Aghaei Jan 31 '21 at 14:30
  • Actually, that's does not work, so I changed the color to myself but failed only dropdownList List color changed. the selected items blue highlight still exists. Anyway thanks for your help. and I wait for the fix :D hav a good day @RezaAghaei – mr.G Jan 31 '21 at 15:09
  • @mr.G OwnerDraw problem fixed - RTL support added. – Reza Aghaei Jan 31 '21 at 17:36
  • 1
    Good code, thank you. I have found some flicker on resize that can be somewhat solved with `SetStyle( ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true )` –  Sep 20 '21 at 00:02
  • @Lara thanks for the feedback; I didn't have any flicker on my attempts, but I'll give it a try again as soon as I can and will update the code if double buffering is required. – Reza Aghaei Sep 20 '21 at 09:21
  • Someone asked: I copied the file in the project but after building and dropping the control on the form it shows an exception like "Failed to create component FlatComboBox". The error is because in .NET 5/6/7 controls in designer need to be in a namespace and apparently they don't work with global namespace, while in 4.8 components in global namespace work. I added examples for .NET 4.8 and .NET 7.0 to make it easier to use. – Reza Aghaei Jul 17 '23 at 21:24