Переопределить цвет границы для отключенного FlatStyle ComboBox - PullRequest
1 голос
/ 25 января 2020

У меня есть пользовательский ComboBox в C# приложении WinForms, и мне удалось переопределить цвет фона, когда он включен и отключен, и переопределить границу, когда он включен, но когда элемент управления отключен, я не могу понять, Как изменить цвет его границы.

В настоящее время я ловлю WndProc сообщения, отправляемые на пользовательский элемент управления, что-то вроде этого:

protected override void WndProc (ref Message m)
{
    base.WndProc(ref m);

    if (m.Msg == WM_PAINT)
    {
        // set the enabled border color here, and it works
        using (var g = Graphics.FromHwnd(Handle)
        {
            using (var p = new Pen(Color.Black))
            {
                g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
            }
        }
    }
    if (m.Msg == WM_CTLCOLORSTATIC)
    {
        // set the disabled background color here
    ]
    if (m.Msg == WM_NCPAINT)
    {
        // try to set the disabled border color here, but its not working
        using (var g = Graphics.FromHwnd(Handle)
        {
            using (var p = new Pen(Color.Black))
            {
                g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
            }
        }
    }
}

Вот некоторые изображения Чтобы помочь понять проблему, я выбрал цвета, которые специально выделяют проблемную область, это не те цвета, которые я бы использовал:

Включен комбинированный список:

enter image description here

Отключенный комбинированный список:

enter image description here

Обратите внимание на толстую границу SystemGrey, применяемую при отключении комбинированного списка. Это то, что я хочу удалить, это выглядит хуже на старых windows системах, эти скриншоты были сделаны на Windows 10, но я нацеливаюсь на Windows Server 2012, где он производит странный эффект «ореола», который выходит за пределы контроль.

Глядя на MSDN, кажется, что WM_NCPAINT - это сообщение, которое я хочу, но пошаговое выполнение кода, кажется, граница уже была нарисована в этой точке. Я также попытался посмотреть на MSDN для WM_CTLCOLORSTATI C, так как имя кажется многообещающим, но, похоже, оно не вызывает ничего, кроме установки цветов фона.

Есть ли еще одно сообщение, на которое я должен обратить внимание, или я подхожу к этому неправильно? Я попытался пройтись и просмотреть каждое сообщение, но я просто не могу сказать, какое из них вызывает вызов для установки границы.

Редактировать : решение см. Ниже. быстрый и грязный пример того, чего я хотел достичь, и что будет делать код решения:

enter image description here

Ответы [ 2 ]

0 голосов
/ 26 января 2020

Этот ответ основан на Изменение цвета границы ComboBox в Windows Формах .

В этом посте добавлено несколько критериев для удаления белой внутренней границы из элемента управления, когда элемент управления имеет DropDown или DropDownList как DropDownStyle и во всех FlatStyle значениях и как для включения, так и для отключения. Это делается путем обработки сообщения WM_PAINT и рисования внешней и внутренней границы поля со списком, как показано на рисунке.

Для демонстрации, все элементы управления в изображении имеют BackColor = Color.Black и BorderColor = Color.Red, и вы можете видеть их DropDownStyle, FlatStyle и Enabled в качестве выбранного элемента:

enter image description here

using System;
using System.Drawing;
using System.Windows.Forms;
public class FlatCombo : ComboBox
{
    private const int WM_PAINT = 0xF;
    private int buttonWidth = SystemInformation.HorizontalScrollBarArrowWidth;
    Color borderColor = Color.Blue;
    public Color BorderColor
    {
        get { return borderColor; }
        set { borderColor = value; Invalidate(); }
    }
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == WM_PAINT && DropDownStyle != ComboBoxStyle.Simple)
        {
            using (var g = Graphics.FromHwnd(Handle))
            {
                var adjustMent = 0;
                if (FlatStyle == FlatStyle.Popup ||
                   (FlatStyle == FlatStyle.Flat &&
                   DropDownStyle == ComboBoxStyle.DropDownList))
                    adjustMent = 1;
                var innerBorderWisth = 3;
                var innerBorderColor = BackColor;
                if (DropDownStyle == ComboBoxStyle.DropDownList &&
                    (FlatStyle == FlatStyle.System || FlatStyle == FlatStyle.Standard))
                    innerBorderColor = Color.FromArgb(0xCCCCCC);
                if (DropDownStyle == ComboBoxStyle.DropDown && !Enabled)
                    innerBorderColor = SystemColors.Control;

                if (DropDownStyle == ComboBoxStyle.DropDown || Enabled == false)
                {
                    using (var p = new Pen(innerBorderColor, innerBorderWisth))
                    {
                        p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
                        g.DrawRectangle(p, 1, 1, 
                            Width - buttonWidth - adjustMent - 1, Height - 1);
                    }
                }
                using (var p = new Pen(BorderColor))
                {
                    g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
                    g.DrawLine(p, Width - buttonWidth - adjustMent, 
                        0, Width - buttonWidth - adjustMent, Height);
                }
            }
        }
    }
}

Примечание:

Чтобы узнать больше о том, как визуализировать плоский комбинированный список, вы можете взглянуть на исходный код внутреннего ComboBox.FlatComboAdapter класса. Net Фреймворк.

Если вы видите мерцание, для решения без мерцания вы можете использовать BeginPaint и EndPaint. Или как обходной путь, в вашей форме добавьте следующий код:

private const int WS_EX_COMPOSITED = 0x02000000;
protected override CreateParams CreateParams
{
    get
    {
        var c = base.CreateParams;
        c.ExStyle |= WS_EX_COMPOSITED;
        return c;
    }
}
0 голосов
/ 25 января 2020

Редактировать : поскольку кажется, что существует некоторая путаница в том, почему различные другие ответы там неуместны, я объясню проблему. Когда комбинированный список отключен, каркас создает вокруг него рамку шириной 3 пикселя, используя по умолчанию windows цвет фона «Серый». Если вы используете цвет фона формы windows по умолчанию, вы никогда не увидите эту границу, и просто нарисовав черную «границу» шириной в 1 пиксель (на самом деле это не граница, а часть области управления), вы решите проблему. , Однако, если фон формы имеет, например, черный цвет, вы увидите эту цветную границу формы по умолчанию. Это то, что решает приведенное ниже решение, и я надеюсь, что это кому-то поможет.

Оказывается, иногда нужно просто отойти на 10 минут, я разобрался, как это сделать. Это, вероятно, не идеально, и это определенно не изящно, но это работает.

Вы можете фактически нарисовать новую границу при обработке сообщения WM_CTLCOLORSTATI C, я думал, что вы не могли, потому что прямоугольник настолько мал, но на самом деле он рисуется, и хотя это определенно не сообщение, которое обрабатывает исходный рисунок границы, оно следует за ним, так что вы можете рисовать прямо поверх.

Этот код будет рисовать поверх существующей светло-серой границы с темно-серой границей:

if (m.Msg == WM_CTLCOLORSTATIC)
{
    // set your other colors for disabled controls

    using (var g = Graphics.FromHwnd(Handle)
    {
        using (var p = new Pen(Color.FromArgb(93, 100, 103))
        {
            // its a fat border so we need to draw 3 rectangles to cover it
            g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
            g.DrawRectangle(p, 1, 1, Width - 3, Height - 3);
            g.DrawRectangle(p, 2, 2, Width - 5, Height - 5);
        }
    }
]

Я знаю, что это отвратительно, но я на 4-м часу пытаюсь нарисовать границы вокруг контроля ... так что если у кого-то есть более чистое решение, я более чем готов принять его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...