Home > Mobile >  Adding a Label on WinForms ComboBox
Adding a Label on WinForms ComboBox

Time:07-29

I want to add a "Clear" button on the WinForms ComboBox. So I created a custom control that inherits from ComboBox and added a Label to it. Here's the entire code:

public class ComboBoxClear : ComboBox
{
    private readonly Label lblClear;

    public ComboBoxClear()
    {
        lblClear = new Label
        {
            Location = new Point(0, 0),
            AutoSize = true,
            Text = "✖",
            ForeColor = Color.Gray,
            Visible = false,
            Font = new Font("Tahoma", Font.Size),
            Cursor = Cursors.Hand,
        };

        Controls.Add(lblClear);
        lblClear.Click  = (s, e) =>
        {
            lblClear.Visible = false;
            SelectedIndex = -1;
        };

        lblClear.BringToFront();
        SetLocation();
    }

    [DefaultValue(true)]
    [Category("Appearance")]
    public bool ShowClearButton { get; set; } = true;

    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);
        lblClear.Visible = ShowClearButton && !string.IsNullOrEmpty(Text);
    }

    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);
        lblClear.Font = new Font("Tahoma", Font.Size);
    }

    protected override void OnSizeChanged(EventArgs e)
    {
        base.OnSizeChanged(e);
        SetLocation();
    }

    private void SetLocation() =>
        lblClear.Location = new Point(Width - (lblClear.Width * 2), ((Height - lblClear.Height) / 2) - 3);
}

However, this does not work as expected. When I type, the label shows for a moment then goes hidden if I type again or move the mouse. Interestingly when I hover the mouse over it, the cursor changes but it's like the intersection of the label and the ComboBox is cleared.

I tried overriding the "OnPaint" or handling "Paint" event, none seem to be triggered.

CodePudding user response:

I'm not sure if I fully understand your issue, but you are instantiating a label, adding it to your controls, setting a click event on it, and bringing it to the front inside your combo box constructor, and that could be the problem.

I'm not sure if a Form.Label can be instantiated and used from a Form.ComboBox or not, and I'm not sure if you want to use some parent Label property of combo box and add it to some parent Controls property of combo box, or if you want to use Form.Label, but unless you want your label to somehow be part of your combo box, then you should put your label somewhere next to your combo box as part of your Form and try setting up your label within your form instead of your combo box constructor. For example:

public partial class Form1 : Form {
  
    public Form1()
    {
        InitializeComponent();
    }
  
    private void Form1_Load(object sender, EventArgs e)
    {
        Label label = new Label();
        label .Location = new Point(50, 50);
        label .AutoSize = true;
        label .Text = "Whatever";
  
        this.Controls.Add(label );
  
        ComboBox mybox = new ComboBox();
        mybox.Location = new Point(50, 100);
        mybox.Size = new Size(200, 25);
        mybox.Items.Add("beep");
        mybox.Items.Add("bop");
        mybox.Items.Add("boop");
        mybox.Items.Add("bleep");
        mybox.Items.Add("blop");
  
        this.Controls.Add(mybox);
    }
}

CodePudding user response:

The OnPaint method is not triggered for a combo box like it is for other forms controls because this particular control handles painting differently. You may be able to achieve what you are trying to by overriding the OnDrawItem method instead. Take a look at this answer which may give you some inspiration! ClearComboBox


public class ComboBoxClear : ComboBox, IMessageFilter
{
    public ComboBoxClear()
    {
        Application.AddMessageFilter(this);
    }
    const int CB_DROP_HANDLE_WIDTH = 20;
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        if(!(DesignMode || _isHandleInitialized))
        {
            _isHandleInitialized = true;
            _lblClear.Location = 
                new Point(
                    Location.X   Width - _lblClear.Width - CB_DROP_HANDLE_WIDTH,
                    Location.Y   2
            );
            Parent.Controls.Add(_lblClear);
            _lblClear.Click  = (sender, e) =>
            {
                Text = String.Empty;
            };
            TextChanged  = (sender, e) =>
            {
                _lblClear.Visible = Text.Any();
            };
        }
    }
    bool _isHandleInitialized = false;
    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if(disposing)
        {
            Application.RemoveMessageFilter(this);
        }
    }
    const int WM_PAINT = 0x000f;
    public bool PreFilterMessage(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_PAINT:
                _lblClear.BringToFront();
                _lblClear.Refresh();
                break;
        }
        return false;
    }
    private Label _lblClear = new Label
    {
        BackColor = SystemColors.Window,
        BorderStyle = BorderStyle.None,
        Size = new Size(28, 28),
        Text = "✖",
        TextAlign = ContentAlignment.MiddleCenter,
        Font = new Font("Tahoma", 6),
        Padding = new Padding(),
        Margin = new Padding(),
        Visible = false,
    };
}

UPDATE

Wrapping it in a UserControl made this work better in flow-layout-panel

  • Related