Andreas
Andreas

Reputation:

Editbox portion of ComboBox gets selected automatically

I have a small problem that has been annoying me for some hours.

In my WinForms (.NET 3.5) application I create some ComboBoxes (DropDownStyle = DropDown) in a TableLayoutPanel at runtime and fill it with strings. The ComboBoxes are configured to resize automatically (Anchor = Left | Right).

The problem is that whenever the ComboBoxes are resized (i.e. the dialog is resized), the editbox portion of the ComboBox gets selected/highlighted entirely. In my opinion this creates a very confusing effect for the customer which I want to avoid.

The problem doesn't appear if the ComboBox has a fixed size.

Also note that changing the DropDownStyle is not an option - I need the possibility to enter text manually.

I already tried messing around with overriding the OnPaint method, which didn't quite work. I also tried clearing the selection in the ComboBox.Resize event, which worked in a way, but seemed like a very ugly solution - there was a lot of flicker, intentionally selected text became deselected and I would have to add the event handler to each and every ComboBox on my dialog.

Is there a better solution to this problem?

Thank you in advance.

Regards, Andy

Upvotes: 20

Views: 4764

Answers (9)

IllegalKniegal
IllegalKniegal

Reputation: 83

Yep this one is still around, and it's not specific to being on a TableLayoutPanel. ComboBox just does this.

Regardless what point in the stack you insert a correction to this undesired behavior, I see that the generally suggested correction is to let it go ahead and Select All, and then quickly set SelectionLength = 0 afterward.

That's still a pretty visible flash it makes, highlighting all text and then unhighlighting it. Can I suggest a flash so mild you may not even see it? Remove the text, let ComboBox do its thing with nothing to select, then put the text back. It would go something like this...

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_COMMAND)
    {
        if (m.WParam.ToInt32() >> 16 == EN_SETFOCUS)
        {
            // Unnecessary Select All #1: it wants to select all whenever you click on it.
            // Solution: Remove all text. Checkmate.
            _resizing = true;
            string temp = Text;
            Text = "";
            base.WndProc(ref m);
            Text = temp;
            _resizing = false;
            return;
        }
    }
    else if (m.Msg == WM_SIZE)
    {
        // Unnecessary Select All #2: it wants to select all whenever you resize.
        // Solution: Remove all text again.
        _resizing = true;
        string temp = Text;
        Text = "";
        base.WndProc(ref m);
        Text = temp;
        _resizing = false;
        return;
    }
    base.WndProc(ref m);
}

protected override void OnTextChanged(EventArgs e)
{
    if (!_resizing) base.OnTextChanged(e);
}

Upvotes: 0

Alex L
Alex L

Reputation: 4251

For a ComboBox inside a TableLayoutPanel setting the .SelectionLength = 0 on the ComboBox.Resize event does not work, but doing this on the TableLayoutPanel.Resize event does:

Private Sub TableLayoutPanel_Resize(sender As Object, e As EventArgs)
    Dim curr_panel = TryCast(sender, System.Windows.Forms.TableLayoutPanel)
    For Each curr_combo As ComboBox In curr_panel.Controls.OfType(Of ComboBox)
        If ((Not curr_combo.Focused) And curr_combo.DropDownStyle = ComboBoxStyle.DropDown) Then
                curr_combo.SelectionLength = 0
        End If
    Next
End Sub


dim panel as new TableLayoutPanel with {
    ...
}
AddHandler panel.Resize, AddressOf TableLayoutPanel_Resize

Upvotes: 0

StefanH
StefanH

Reputation: 31

Worked for me to change the selectionLength to 0 when the WM_WINDOWPOSCHANGED gets called. Works even with the tableLayoutPanel set to %.

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

    if(m.Msg == 0x0047) {   // WM_WINDOWPOSCHANGED = 0x0047 
        if (SelectionLength != 0) {
            SelectionLength = 0;
        }
    }
}

Upvotes: 3

Jeff Roe
Jeff Roe

Reputation: 3224

This appears to be a bug in the native Windows implementation of ComboBox with DropDownStyle of DropDown.

The fix detailed in the other answers here (setting the SelectionLength property to 0 (zero) in the ComboBox's Resize event) works well most of the time.

However, I found that even that fix to work around this bug does not always work. If the ComboBox is in a TableLayoutPanel, and if that TableLayoutPanel has more than one column with a Percent Size Type, then that fix often does not work.

A picture is worth a thousand words. See the following screen shot of a form I made to demonstrate the problem.

enter image description here

Upvotes: 3

bp21128
bp21128

Reputation: 11

Handle the Resize event for the ComboBox's parent container. Put this line in there:

MyComboBox.SelectionLength = 0

An Example (VB, obviously):

Private Sub MyControl_Resize(sender As Object, e As EventArgs) Handles Me.Resize

    MyComboBox.SelectionLength = 0

End Sub

Good Luck to you!

--BP

Upvotes: 1

Brian Gideon
Brian Gideon

Reputation: 48979

None of the answers so far worked for me. The only reliable method I have found was to post a message asynchronously via BeginInvoke that sets SelectionLength back to zero after all other activity on the control has completed. The amount of flicker is very annoying and unprofessional, but it is the best I could come up with...so far.

internal class FixedComboBox : ComboBox
{
    protected override void OnResize(EventArgs e)
    {
        if (IsHandleCreated && !Focused)
        {
            BeginInvoke((Action)(() =>
                {
                    SelectionLength = 0;
                }));
        }
        base.OnResize(e);
    }
}

Upvotes: 3

tony
tony

Reputation: 21

Wow. Thank you guys!

Apparently this bug has persisted many years. I'm building a UserControl with .Net 4 (Visual Studio 2010). Here's is my slightlty modified version of bsneeze's code.

Cheers

using System.Windows.Forms;
using System.Linq;

public MyUserControlCtor()
{
    InitializeComponent();

    foreach( Control ctrl in Controls)
    {
        ComboBox cb = ctrl as ComboBox;
        if (cb != null)
        {
            cb.Resize += (sender, e) =>
            {
                if (!cb.Focused)
                    this.FCHZ_ComboBox.SelectionLength = 0;
            };
        }
    } 
}

Upvotes: 2

Will
Will

Reputation: 78

I found setting the selection length to 0 for the combo-box on the resize event of whatever control the combo-box is on causes a lot less flickering instead of doing it on the resize of the combo itself.

I actually achieved this in VB.Net but it should apply the same to C#.

Upvotes: 0

bsneeze
bsneeze

Reputation: 4479

This is an old question, but I found it searching for an answer and ended up implementing my own solution. Might as well post it here, right?

    foreach (var cb in Controls.OfType<ComboBox>())
    {
        cb.Resize += (sender, e) => {
            if (!cb.Focused)
                cb.SelectionLength = 0;
        };
    }

intentionally selected text became deselected

This WinForms bug doesn't affect selected ComboBoxes, so by ignoring the ones with Focus, we take care of the problem of losing current selections.

I would have to add the event handler to each and every ComboBox on my dialog.

Taken care of by the foreach loop. Put it in InitializeComponent() or your .ctor if you don't want to break the designer, or have the designer break this.

there was a lot of flicker

I'm only seeing flicker if I resize very fast, but I'm on Win7 so it might be different on XP.

Upvotes: 17

Related Questions