Ir al contenido principal

[WinForms] Group-CheckBox

Introducción:

En muchas ocasiones nos hemos visto en la necesidad de usar CheckBox en lugar de RadioButton por el simple hecho de que el CheckBox lo podes colocar en cualquier parte de nuestro formulario a diferencia del RadioButton que ya por el simple hecho de estar dentro de un contenedor hace un poco difícil usarlo con liberta, pero necesitamos que el CheckBox tenga la misma funcionalidad del RadioButton.

bueno, entonces vamos a darle al CheckBox la funcionalidad de poder trabajar en grupo, así que manos al teclado.

Primero, es crear un control que herede del control CheckBox cuyo código deberá ser el siguiente:

1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
5 using System.Linq;
6 using System.Text;
7 using System.Windows.Forms;
8
9 namespace TestClassLib
10 {
11 public partial class MyCheckBox : CheckBox
12 {
13 }
14 }


Luego agregar una propiedad que indicara el índice del grupo al cual pertenece el CheckBox.


1 private int _RadioGroupIndex = -1;
2 [DefaultValue(-1), Browsable(true), Description("Indice de agrupación")]
3 public int RadioGroupIndex
4 {
5 get
6 {
7 return _RadioGroupIndex;
8 }
9 set
10 {
11 if (value < -1)
12 throw new Exception("RadioGroupIndex invalido");
13 else
14 _RadioGroupIndex = value;
15 }
16 }


Sobre escribir el evento OnCheckedChanged


1 protected override void OnCheckedChanged(EventArgs e)
2 {
3 if (RadioGroupIndex >= 0)
4 {
5 List<MyCheckBox> groupList = FindRadioGroupList(FindForm());
6 if (groupList.Count == 0 && !DesignMode) this.Checked = true;
7 else if (groupList.Count() > 1)
8 {
9 foreach (MyCheckBox ckBox in groupList)
10 {
11 if (ckBox.Name == this.Name) continue;
12 ckBox.Checked = false;
13 }
14 }
15 }
16
17 base.OnCheckedChanged(e);
18 }

y para terminar creamos una función que se encargara de buscando en el formulario todos los controles MyCheckBox que tengan el mismo índice de grupo y que estén marcados.


1 private List<MyCheckBox> FindRadioGroupList(Control Container)
2 {
3 List<MyCheckBox> retRGList = new List<MyCheckBox>();
4 retRGList.AddRange(Container.Controls.OfType<MyCheckBox>().Where(pre => pre.RadioGroupIndex == this.RadioGroupIndex && pre.Checked).ToList());
5 foreach (Control cc in Container.Controls.OfType<Control>().Where(pre => pre.HasChildren))
6 retRGList.AddRange(FindRadioGroupList(cc));
7
8 return retRGList;
9 }

como resultado final tendremos nuestro propio CheckBox con la funcionalidad de poder ser agrupado y poderlos colocar en cualquier parte de nuestro formulario sin preocuparnos de estar aplicando una lógica de programación para cada CheckBox que querríamos que tenga este comportamiento, solo bastaría con asignarle al primer grupo de MyCheckBox en la Propiedad RadioGroupIndex = 0 y así sucesivamente como nos sea necesario.


 


[C#] Código completo:


1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
5 using System.Linq;
6 using System.Text;
7 using System.Windows.Forms;
8 using System.Reflection;
9
10 namespace TestClassLib
11 {
12 public partial class MyCheckBox : CheckBox
13 {
14 private int _RadioGroupIndex = -1;
15 [DefaultValue(-1), Browsable(true), Description("Indice de agrupación")]
16 public int RadioGroupIndex
17 {
18 get
19 {
20 return _RadioGroupIndex;
21 }
22 set
23 {
24 if (value < -1)
25 throw new Exception("RadioGroupIndex invalido");
26 else
27 _RadioGroupIndex = value;
28 }
29 }
30
31 public MyCheckBox()
32 {
33 }
34
35 protected override void OnCheckedChanged(EventArgs e)
36 {
37 if (RadioGroupIndex >= 0)
38 {
39 List<MyCheckBox> groupList = FindRadioGroupList(FindForm());
40 if (groupList.Count == 0 && !DesignMode) this.Checked = true;
41 else if (groupList.Count() > 1)
42 {
43 foreach (MyCheckBox ckBox in groupList)
44 {
45 if (ckBox.Name == this.Name) continue;
46 ckBox.Checked = false;
47 }
48 }
49 }
50
51 base.OnCheckedChanged(e);
52 }
53
54 private List<MyCheckBox> FindRadioGroupList(Control Container)
55 {
56 List<MyCheckBox> retRGList = new List<MyCheckBox>();
57 retRGList.AddRange(Container.Controls.OfType<MyCheckBox>().Where(pre => pre.RadioGroupIndex == this.RadioGroupIndex && pre.Checked).ToList());
58 foreach (Control cc in Container.Controls.OfType<Control>().Where(pre => pre.HasChildren))
59 retRGList.AddRange(FindRadioGroupList(cc));
60
61 return retRGList;
62 }
63 }
64 }
65

[VB] Código Completo:



1 Imports System.ComponentModel
2
3 Partial Public Class MyCheckBox
4 Inherits CheckBox
5
6 Dim _RadioGroupIndex As Integer = -1
7 <DefaultValue(-1), Browsable(True), Description("Indice de agrupación")> _
8 Public Property RadioGroupIndex() As Integer
9 Get
10 Return _RadioGroupIndex
11 End Get
12 Set(ByVal value As Integer)
13 If value < -1 Then
14 Throw New Exception("RadiogroudIndex Invalido.")
15 Else
16 _RadioGroupIndex = value
17 End If
18 End Set
19 End Property
20
21 Protected Overrides Sub OnCheckedChanged(ByVal e As System.EventArgs)
22 If RadioGroupIndex >= 0 Then
23 Dim groupList As List(Of MyCheckBox) = FindRadioGroupList(FindForm())
24 If groupList.Count = 0 And Not DesignMode Then
25 Me.Checked = True
26 ElseIf groupList.Count > 1 Then
27 For Each ckBox As MyCheckBox In groupList
28 If ckBox.Name = Me.Name Then
29 Continue For
30 End If
31 ckBox.Checked = False
32 Next
33 End If
34 End If
35 MyBase.OnCheckedChanged(e)
36 End Sub
37
38 Private Function FindRadioGroupList(ByRef Container As Control) As List(Of MyCheckBox)
39 Dim retRGList As List(Of MyCheckBox) = New List(Of MyCheckBox)
40 retRGList.AddRange(Container.Controls.OfType(Of MyCheckBox).Where(Function(pre) pre.RadioGroupIndex = Me.RadioGroupIndex And pre.Checked).ToList())
41 For Each cc As Control In Container.Controls.OfType(Of Control).Where(Function(pre) pre.HasChildren)
42 retRGList.AddRange(FindRadioGroupList(cc))
43 Next
44 Return retRGList
45 End Function
46
47 End Class
48


Bueno!!!!…. espero les pueda ser de utilidad este código y cualquier mejora que se le pueda hacer pues no duden en comentarla,


Hasta el próximo blog.

Comentarios

Entradas populares de este blog

TextBox con Borde Personalizado

Bien, retomando nuevamente mi blog, luego de tanto tiempo ausente, veremos como personalizar el borde del control TextBox con un color diferente. hace poco vi en los foros de MSDN, en el foro de VB.Net esta pregunta, ¿ Cómo puedo cambiar el color del borde de un control TextBox ?, anteriormente también se hizo la misma pregunta en este mismo foro donde yo respondí como hacerlo VB2010 4.0 - Como crear un textbox personalizado . no hay manera fácil de personalizar un control, generalmente se tiene que sobre escribir el evento WndProc para escuchar los mensajes de window y reemplazar la funcionalidad de estos según sea la necesidad o el control. bien, para cambiar el color del borde del control TextBox sin mucha funcionalidad, se debe de escuchar y reemplazar el funcionamiento de los mensajes WM_PAINT y WM_NCPAINT . ¿ Porque WM_PAINT ?, porque cuando cambiamos la propiedad BorderStyle de este control a FixedSingle, quien pinta el borde es el mensaje WM_PAINT no asi el WM_NC

TextBox con Icon/Imagen

Bien, continuando con este articulo: TextBox con borde personalizado , ahora le dare la funcionalidad de poder mostrar un icono o imagen dentro del Control TextBox. Existen dos maneras de hacer esto: Pintar el icono/imagen dentro del control o Pintar el icono/imagen dentro del Non-Client Area del control. Pintar el icono/imagen dentro del control. Antes de escribir el código decidi googlear un poco, para ver si alguien más ya habia tenido la misma idea de usar el mensaje EM_SETMARGINS para dejar el espacio necesario para pintar el icono o imagen ya sea a la derecha o izquierda y me he encontrado con este articulo. Adding an Icon or Control to a TextBox or ComboBox . Pintar el icono/imagen de ntro del Non-Client Area del control. Us ando el Non- Client Area no encontre resultados googleando, así que es la forma que usar e para dib ujar un icono o imagen dentro de un control TextBox. En el control TextEditor que escrib í, utilizo esta manera para pintar el icono

Personalizar DataGridView (II) - Bloquear columnas de solo lectura

Personalizar DataGridView - Actualizaciones Personalizar DataGridView (II.1) - Bloquear columnas de solo lectura. Personalizar DataGridView (III) - Cambiar Diseñador. Bien, continuando con el articulo " Personalizar DataGridView (I) - Pintar área vacía ", ahora lo que haré es darle al control la funcionalidad de bloquear las columnas cuya propiedad " ReadOnly " se establezca en " true ", entiéndase por "Bloquear" el evitar que las columnas cuya propiedad " ReadOnly=true " puedan recibir el foco, ya sea por el teclado o por el ratón ( mouse ). Para tal objetivo agregare una nueva propiedad al control la cual llamare " AllowFocusReadOnlyColumns " cuyo valor predeterminado sera " true ", en caso de ser " false " las columnas marcadas como solo lectura no recibirán el foco. También le daré la funcionalidad de poder avanzar a la siguiente columna al presionar la tecla " ENTER " agregando otr