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