Click here to Skip to main content
14,733,866 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
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:

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 3: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[^]?
Vali Maties 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?
Vali Maties 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.
Vali Maties 6-Mar-20 8:12am
   
Thanks @Maciej , I found the problem and the solution, thanks to you. Debugger was salvation, and you with the idea :)

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.
   
Comments
Vali Maties 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:

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
if (m.Msg == 0x201) 
{ 
    Select();
}
to this
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:
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:
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);
            }
        }
    }
}
   
v4
Comments
Vali Maties 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