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(); }