Click here to Skip to main content
15,867,834 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: , +
Hi.

I have a custom class based on ComboBox, in Winforms. It is set as DropDownList, as I only want to select an item, not to insert or modify the Text area of it.

I want to make it to drop its list when it has focus, from keyboard, by iterate thru form's items using TAB key, on leave to close its dropped list. All works fine by having this code in Enter and Leave methods (in What I have tried, this is what I have, Console lines are only for debugging):

The problem is when I click with mouse on it. The list will drop down, it will close and open again.
The order of events fired on click, if this control does not have focus, is Enter and then MouseClick, Leave event does not fire, so I don't know why DropDownList is closing!

How to make it drop only one time if the focus is not on it and I click with mouse?

What I have tried:

C#
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);
            }
        }
    }
}
Posted
Updated 6-Mar-20 2:04am
Comments
Richard MacCutchan 6-Mar-20 5:16am    
"I have a custom class based on ComboBox, in Winforms. It is set as DropDownList, as I only want to select an item, not to insert or modify the Text area of it."

So why not make it simple for yourself and use a ListBox Class (System.Windows.Forms) | Microsoft Docs[^]?
MVSoftVM 6-Mar-20 5:21am    
Because of space on the form...
Picture with controls in form
Maciej Los 6-Mar-20 6:23am    
Does user can select (checked=true) only one item or many of them?
MVSoftVM 6-Mar-20 7:18am    
I don't see the relevance, but, no, it will select only one record.
Edited: And also, I don't see any property in WinForms Combobox "Checked"!
BTW, That little checked icon in front of text of ComboBox shows user if that record is actively or not.
Maciej Los 6-Mar-20 7:42am    
OK. Thanks for reply.
Please, see my answer.

Quote:
The problem is when I click with mouse on it. The list will drop down, it will close and open again


My best guess is...
When you click on inactive control, two things happen:
- control is going into active (Enter event is fired and this probably calls SelectionChange event),
- control has been clicked (Click event is fired).

I'd suggest to use debugger to find out why do you see the dropdown list twice.
 
Share this answer
 
Comments
MVSoftVM 6-Mar-20 7:52am    
No, is not happening like this.
When I use debugger, Enter is the first event fired after I click on that combobox. The next one is WndProc, which must redraw border. Here comes problem: on line
base.WndProc(ref m);
after this line, the dropdown list it will close...
Thanks to @Maciej Los , I found the problem and the solution, bypassing the message processed for Mouse Left Click (0x201) and only selecting the Combobox:

C#
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);
            }
        }
    }
}


Edited:
This is ok, but missing one thing. On first click it is Ok, DropDownList will show only one time, but on second click it will not dropping anymore. So, the solution is to change
C#
if (m.Msg == 0x201) 
{ 
    Select();
}
to this
C#
if (m.Msg == 0x201) 
{ 
    Select();
    DroppedDown = true;
}


Edit 2:
I found that above solution is good, but something missing.
If I move the mouse over the control some flickering effect appears, but only if control does not have focus, so I have improved the code with this one:
C#
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);
    }
}

So, let's see the entire code, to be easy for copy paste:
C#
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);
            }
        }
    }
}
 
Share this answer
 
v4
Comments
MVSoftVM 8-Mar-20 9:01am    
Found solution, also with some 2 edits :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900