5

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
  • 112,050
  • 16
  • 178
  • 345
mr.G
  • 79
  • 1
  • 6

1 Answers1

12

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

Here is the code:

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);
}
Reza Aghaei
  • 112,050
  • 16
  • 178
  • 345
  • 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
  • Good code, thank you. I have found some flicker on resize that can be somewhat solved with `SetStyle( ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true )` – Lara 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