Saltar al contenido

Custom ComboBox – Icon, Back, Text & Border Color – WinForm C#

Hola, en este tutorial crearemos un ComboBox personalizado con un aspecto muy elegante, plano, moderno y manteniendo toda la funcionalidad básica e importantes de un ComboBox tradicional.

Por ejemplo, un ComboBox con el estilo DropDownList, permite abrir la lista desplegable haciendo clic en cualquier parte del control, y poder filtrar la lista según la tecla alfanumérica que se presione.

Por otro lado, un ComboBox con el estilo DropDown, solamente permite abrir la lista desplegable desde el icono, y poder escribir con total libertad sobre el control, como si fuese un cuadro de texto. Esto es muy útil para un filtro avanzado, autocompletar y sugerir algún elemento de la lista según la palabra escrita.

En cuanto a la apariencia, se puede cambiar el color de fondo, borde, icono, texto y la lista desplegable, cambiar el tamaño de borde y el tamaño del control a placer.

Bueno, hacerlo será un poco más complicado que los controles personalizados anteriores, no porque sea difícil, sino porque lleva un poco más de tiempo volver a implementar todas las funcionalidades esenciales de un ComboBox tradicional. Así que comencemos con el tutorial:

1.- Crear clase y heredar la clase UserControl

    [DefaultEvent("OnSelectedIndexChanged")]
    class RJComboBox : UserControl
    {

2.- Definir los campos de apariencia, componentes y eventos.

    //Fields
    private Color backColor = Color.WhiteSmoke;
    private Color iconColor = Color.MediumSlateBlue;
    private Color listBackColor = Color.FromArgb(230, 228, 245);
    private Color listTextColor = Color.DimGray;
    private Color borderColor = Color.MediumSlateBlue;
    private int borderSize = 1;

    //Items
    private ComboBox cmbList;
    private Label lblText;
    private Button btnIcon;

    //Events
    public event EventHandler OnSelectedIndexChanged;//Default event

3.- Constructor (Inicializar los componentes)

    //Constructor
    public RJComboBox()
    {
        cmbList = new ComboBox();
        lblText = new Label();
        btnIcon = new Button();
        this.SuspendLayout();

        //ComboBox: Dropdown list
        cmbList.BackColor = listBackColor;
        cmbList.Font = new Font(this.Font.Name, 10F);
        cmbList.ForeColor = listTextColor;
        cmbList.SelectedIndexChanged += new EventHandler(ComboBox_SelectedIndexChanged);//Default event
        cmbList.TextChanged += new EventHandler(ComboBox_TextChanged);//Refresh text

        //Button: Icon
        btnIcon.Dock = DockStyle.Right;
        btnIcon.FlatStyle = FlatStyle.Flat;
        btnIcon.FlatAppearance.BorderSize = 0;
        btnIcon.BackColor = backColor;
        btnIcon.Size = new Size(30, 30);
        btnIcon.Cursor = Cursors.Hand;
        btnIcon.Click += new EventHandler(Icon_Click);//Open dropdown list
        btnIcon.Paint += new PaintEventHandler(Icon_Paint);//Draw icon

        //Label: Text
        lblText.Dock = DockStyle.Fill;
        lblText.AutoSize = false;
        lblText.BackColor = backColor;
        lblText.TextAlign = ContentAlignment.MiddleLeft;
        lblText.Padding = new Padding(8, 0, 0, 0);
        lblText.Font = new Font(this.Font.Name, 10F);
        //->Attach label events to user control event
        lblText.Click += new EventHandler(Surface_Click);//Select combo box
        lblText.MouseEnter += new EventHandler(Surface_MouseEnter);
        lblText.MouseLeave += new EventHandler(Surface_MouseLeave);

        //User Control
        this.Controls.Add(lblText);//2
        this.Controls.Add(btnIcon);//1
        this.Controls.Add(cmbList);//0
        this.MinimumSize = new Size(200, 30);
        this.Size = new Size(200, 30);
        this.ForeColor = Color.DimGray;
        this.Padding = new Padding(borderSize);//Border Size
        this.Font = new Font(this.Font.Name, 10F);
        base.BackColor = borderColor; //Border Color
        this.ResumeLayout();
        AdjustComboBoxDimensions();
    }

4.- Crear método para posicionar ComboBox

    //Private methods
    private void AdjustComboBoxDimensions()
    {
        cmbList.Width = lblText.Width;
        cmbList.Location = new Point()
        {
            X = this.Width - this.Padding.Right - cmbList.Width,
            Y = lblText.Bottom - cmbList.Height
        };
    }

5.- Implementar los métodos de evento

   //Event methods

    //-> Default event
    private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (OnSelectedIndexChanged != null)
            OnSelectedIndexChanged.Invoke(sender, e);
        //Refresh text
        lblText.Text = cmbList.Text;
    }
    //-> Items actions
    private void Icon_Click(object sender, EventArgs e)
    {
        //Open dropdown list
        cmbList.Select();
        cmbList.DroppedDown = true;
    }
    private void Surface_Click(object sender, EventArgs e)
    {
        //Attach label click to user control click
        this.OnClick(e);
        //Select combo box
        cmbList.Select();
        if (cmbList.DropDownStyle == ComboBoxStyle.DropDownList)
            cmbList.DroppedDown = true;//Open dropdown list
    }
    private void ComboBox_TextChanged(object sender, EventArgs e)
    {
        //Refresh text
        lblText.Text = cmbList.Text;
    }

6.- Dibujar el icono (Flecha hacia abajo)

    //-> Draw icon
    private void Icon_Paint(object sender, PaintEventArgs e)
    {
        //Fields
        int iconWidht = 14;
        int iconHeight = 6;
        var rectIcon = new Rectangle((btnIcon.Width - iconWidht) / 2, (btnIcon.Height - iconHeight) / 2, iconWidht, iconHeight);
        Graphics graph = e.Graphics;

        //Draw arrow down icon
        using (GraphicsPath path = new GraphicsPath())
        using (Pen pen = new Pen(iconColor, 2))
        {
            graph.SmoothingMode = SmoothingMode.AntiAlias;
            path.AddLine(rectIcon.X, rectIcon.Y, rectIcon.X + (iconWidht / 2), rectIcon.Bottom);
            path.AddLine(rectIcon.X + (iconWidht / 2), rectIcon.Bottom, rectIcon.Right, rectIcon.Y);
            graph.DrawPath(pen, path);
        }
    }

7.- Crear las propiedades de apariencia

    //Properties
    //-> Appearance
    [Category("RJ Code - Appearance")]
    public new Color BackColor
    {
        get { return backColor; }
        set
        {
            backColor = value;
            lblText.BackColor = backColor;
            btnIcon.BackColor = backColor;
        }
    }

    [Category("RJ Code - Appearance")]
    public Color IconColor
    {
        get { return iconColor; }
        set
        {
            iconColor = value;
            btnIcon.Invalidate();//Redraw icon
        }
    }

    [Category("RJ Code - Appearance")]
    public Color ListBackColor
    {
        get { return listBackColor; }
        set
        {
            listBackColor = value;
            cmbList.BackColor = listBackColor;
        }
    }

    [Category("RJ Code - Appearance")]
    public Color ListTextColor
    {
        get { return listTextColor; }
        set
        {
            listTextColor = value;
            cmbList.ForeColor = listTextColor;
        }
    }

    [Category("RJ Code - Appearance")]
    public Color BorderColor
    {
        get { return borderColor; }
        set
        {
            borderColor = value;
            base.BackColor = borderColor; //Border Color
        }
    }

    [Category("RJ Code - Appearance")]
    public int BorderSize
    {
        get { return borderSize; }
        set
        {
            borderSize = value;
            this.Padding = new Padding(borderSize);//Border Size
            AdjustComboBoxDimensions();
        }
    }

    [Category("RJ Code - Appearance")]
    public override Color ForeColor
    {
        get { return base.ForeColor; }
        set
        {
            base.ForeColor = value;
            lblText.ForeColor = value;
        }
    }

    [Category("RJ Code - Appearance")]
    public override Font Font
    {
        get { return base.Font; }
        set
        {
            base.Font = value;
            lblText.Font = value;
            cmbList.Font = value;//Optional
        }
    }

    [Category("RJ Code - Appearance")]
    public string Texts
    {
        get { return lblText.Text; }
        set { lblText.Text = value; }
    }

    [Category("RJ Code - Appearance")]
    public ComboBoxStyle DropDownStyle
    {
        get { return cmbList.DropDownStyle; }
        set
        {
            if (cmbList.DropDownStyle != ComboBoxStyle.Simple)
                cmbList.DropDownStyle = value;
        }
    }

8.- Crear las propiedades de datos o funcionalidades

//Properties
    //-> Data
    [Category("RJ Code - Data")]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
    [Localizable(true)]
    [MergableProperty(false)]
    public ComboBox.ObjectCollection Items
    {
        get { return cmbList.Items; }
    }

    [Category("RJ Code - Data")]
    [AttributeProvider(typeof(IListSource))]
    [DefaultValue(null)]
    public object DataSource
    {
        get { return cmbList.DataSource; }
        set { cmbList.DataSource = value; }
    }

    [Category("RJ Code - Data")]
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [Localizable(true)]
    public AutoCompleteStringCollection AutoCompleteCustomSource
    {
        get { return cmbList.AutoCompleteCustomSource; }
        set { cmbList.AutoCompleteCustomSource = value; }
    }

    [Category("RJ Code - Data")]
    [Browsable(true)]
    [DefaultValue(AutoCompleteSource.None)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public AutoCompleteSource AutoCompleteSource
    {
        get { return cmbList.AutoCompleteSource; }
        set { cmbList.AutoCompleteSource = value; }
    }

    [Category("RJ Code - Data")]
    [Browsable(true)]
    [DefaultValue(AutoCompleteMode.None)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public AutoCompleteMode AutoCompleteMode
    {
        get { return cmbList.AutoCompleteMode; }
        set { cmbList.AutoCompleteMode = value; }
    }

    [Category("RJ Code - Data")]
    [Bindable(true)]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public object SelectedItem
    {
        get { return cmbList.SelectedItem; }
        set { cmbList.SelectedItem = value; }
    }

    [Category("RJ Code - Data")]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public int SelectedIndex
    {
        get { return cmbList.SelectedIndex; }
        set { cmbList.SelectedIndex = value; }
    }

    [Category("RJ Code - Data")]
    [DefaultValue("")]
    [Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
    [TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
    public string DisplayMember
    {
        get { return cmbList.DisplayMember; }
        set { cmbList.DisplayMember = value; }
    }

    [Category("RJ Code - Data")]
    [DefaultValue("")]
    [Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
    public string ValueMember
    {
        get { return cmbList.ValueMember; }
        set { cmbList.ValueMember = value; }
    }

9.- Adjuntar eventos

    //->Attach label events to user control event
    private void Surface_MouseLeave(object sender, EventArgs e)
    {
        this.OnMouseLeave(e);
    }

    private void Surface_MouseEnter(object sender, EventArgs e)
    {
        this.OnMouseEnter(e);
    }
    //::::+

10.- Anular métodos

    //Overridden methods
    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        AdjustComboBoxDimensions();
    }

Descargas

Ver video tutorial