Vali Maties Ответов: 2

Выпадающий список C# winforms combobox нажмите enter и нажмите кнопку


Привет.

У меня есть пользовательский класс, основанный на ComboBox, в Winforms. Он установлен как выпадающий список, так как я хочу только выбрать элемент, а не вставлять или изменять его текстовую область.

Я хочу, чтобы он отбрасывал свой список, когда у него есть фокус, с клавиатуры, повторяя элементы формы с помощью клавиши TAB, в отпуске, чтобы закрыть свой отброшенный список. Все работает нормально, имея этот код в методах Enter и Leave (in Что я уже пробовал, это то, что у меня есть, консольные линии предназначены только для отладки):

Проблема в том, что я щелкаю по нему мышью. Список будет выпадать, закрываться и снова открываться.
Порядок событий, запускаемых при щелчке мыши, если этот элемент управления не имеет фокуса, - это Enter, а затем MouseClick, событие Leave не срабатывает, поэтому я не знаю, почему DropDownList закрывается!

Как сделать так, чтобы он упал только один раз, если фокус не на нем, и я щелкаю мышью?

Что я уже пробовал:

private void MyNewComboBoxCheckable_SelectionChangeCommitted(object sender, EventArgs e)
{
    SuspendLayout();
    if (readOnly)
    {
        SelectedIndex = prevIndex;
        Console.WriteLine("SelectionChangeCommited");
    }
    ResumeLayout(false);
}

private void MyNewComboBoxCheckable_Enter(object sender, EventArgs e)
{
    prevIndex = SelectedIndex;
    if (ReadOnly == false)
    {
        BorderColor = Color.Red;
        if (!DroppedDown && Droppable)
        {
            DroppedDown = true;
            Console.WriteLine("Enter - Dropped = set to true, {0}", DroppedDown);
        }
    }
    Invalidate();
}

private void MyNewComboBoxCheckable_Leave(object sender, EventArgs e)
{
    DroppedDown = false;
    BorderColor = Color.DarkGray;
    Invalidate();
    Console.WriteLine("Leave - Dropped = false");
}

private void MyNewComboBoxCheckable_MouseClick(object sender, MouseEventArgs e)
{
    DroppedDown = true;
    Console.WriteLine("MouseClick");
}

protected override void WndProc(ref Message m)
{

    if (ReadOnly)
    {
        switch (m.Msg)
        {
            case 0x201:
            case 0x203:
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }
    else
    {
        base.WndProc(ref m);
    }

    if (m.Msg == WM_PAINT)
    {
        using (var g = Graphics.FromHwnd(Handle))
        {
            // Uncomment this if you don't want the "highlight border".

            using (var p = new Pen(this.BorderColor, 1))
            {
                g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
            }
        }
    }
}

Richard MacCutchan

"У меня есть пользовательский класс, основанный на ComboBox, в Winforms. Он установлен как выпадающий список, так как я хочу только выбрать элемент, а не вставлять или изменять его текстовую область."

Так почему бы не сделать его простым для себя и не использовать Класс ListBox (System.Окна.Формы) | Microsoft Docs[^]?

Vali Maties

Из-за пространства на форме...
Картинка с элементами управления в форме

Maciej Los

Может ли пользователь выбрать (checked=true) только один элемент или их много?

Vali Maties

Я не вижу релевантности, но нет, он выберет только одну запись.
Отредактировано: а также, я не вижу никакого свойства в WinForms Combobox "проверено"!
Кстати, этот маленький проверенный значок перед текстом ComboBox показывает пользователю, активна ли эта запись или нет.

Maciej Los

ОК. Спасибо за ответ.
Пожалуйста, смотрите мой ответ.

Vali Maties

Спасибо @Maciej , я нашел проблему и решение, благодаря вам. Отладчик был спасением, а вы с идеей :)

2 Ответов

Рейтинг:
12

Vali Maties

Благодаря @Maciej Los , я нашел проблему и решение, обойдя сообщение, обработанное для щелчка левой кнопкой мыши (0x201) и выбрав только Combobox:

protected override void WndProc(ref Message m)
{

    if (ReadOnly)
    {
        switch (m.Msg)
        {
            case 0x201:
            case 0x203:
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }
    else
    {
        if (m.Msg == 0x201) 
        { 
            Select();
        }
        else 
        base.WndProc(ref m);
        
    }

    if (m.Msg == WM_PAINT)
    {
        using (var g = Graphics.FromHwnd(Handle))
        {
            // Uncomment this if you don't want the "highlight border".

            using (var p = new Pen(this.BorderColor, 1))
            {
                g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
            }
        }
    }
}


Отредактированный:
Это нормально, но не хватает одной вещи. При первом щелчке это нормально, выпадающий список будет отображаться только один раз, но при втором щелчке он больше не будет падать. Итак, решение состоит в том, чтобы измениться
if (m.Msg == 0x201) 
{ 
    Select();
}
до настоящего времени
if (m.Msg == 0x201) 
{ 
    Select();
    DroppedDown = true;
}


Правка 2:
Я обнаружил, что вышеописанное решение хорошо, но чего-то не хватает.
Если я наведу курсор мыши на элемент управления, появится некоторый эффект мерцания, но только если элемент управления не имеет фокуса, поэтому я улучшил код с помощью этого:
else
{
    if (m.Msg == 0x201)
    {
        Select();
        DroppedDown = true;
    }
    else
    if (!Focused || m.Msg != 0x0200) // Do only when not MouseMove and not focus
    {
        base.WndProc(ref m);
    }
}

Итак, давайте посмотрим весь код, чтобы его было легко скопировать и вставить:
protected override void WndProc(ref Message m)
{
    if (ReadOnly)
    {
        switch (m.Msg)
        {
            case 0x201: // LeftClick
            case 0x203:
                break;
            default:
                base.WndProc(ref m);
                break;
        }
        return;
    }
    else
    {
        if (m.Msg == 0x201)
        {
            Select();
            DroppedDown = true;
        }
        else
        if (!Focused || m.Msg != 0x0200) // Do only when not MouseMove and not focus
        {
            base.WndProc(ref m);
        }
    }
    if (m.Msg == WM_PAINT)
    {
        using (var g = Graphics.FromHwnd(Handle))
        {
            // Uncomment this if you don't want the "highlight border".
            using (var p = new Pen(this.BorderColor, 1))
            {
                g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
            }
        }
    }
}


Vali Maties

Найдено решение, также с некоторыми 2 правками :)

Рейтинг:
0

Maciej Los

Цитата:
Проблема в том, что я щелкаю по нему мышью. Список выпадет, закроется и откроется снова


Мое лучшее предположение таково...
Когда вы нажимаете на неактивный элемент управления, происходят две вещи:
- управление переходит в активное состояние (запускается событие Enter, и это, вероятно, вызывает событие SelectionChange),
- управление было нажато (событие щелчка срабатывает).

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


Vali Maties

Нет, все происходит не так.
Когда я использую отладчик, Enter-это первое событие, запущенное после того, как я нажму на этот combobox. Следующий-WndProc, который должен перерисовать границу. А вот и проблема: on line

base.WndProc(ref m);
после этой строки выпадающий список закроется...