hola a todos, en una aplicación que estoy construyendo tengo un formulario para llenar los datos de un usuario, pero como característica adicional este usuario puede escoger entre 3 servicios, estos los tengo con checkboxs para que elija los que quiera, pero puede suceder que el administrador del sistema agregue un nuevo servicio y entonces sean 4 servicios, Es posible crear estos checkbox en tiempo de ejecución, para cuando en la base de datos exista un nuevo servicio aparezca en el formulario o simplemente debo crear otro formulario donde asigne los servicios uno a uno para no modificar la App en caso existan nuevos servicios disponibles? estoy trabajando VS for desktop...
Sí, es posible crear los controles dinámicamente, pero en este caso creo que sería mejor que en vez de checkboxes utilizaras un combobox, listbox o un componente similar y agregaras los servicios dinámicamente, según existan, en dicho componente.
Te pongo un ejemplo, tómalo como tal
Aquí creo y cargo los controles en tiempo de ejecución. Provienen de un DataSet:
foreach (DataColumn s in dt.Columns.Cast<DataColumn>()
.Where(x => x.Caption.StartsWith(prefixParam))
.OrderBy(x => x.DataType.ToString())
.ThenBy(x => x.Caption))
{
//var GenricTypeValue = asb[s.Name] == null ? null : Convert.ChangeType(asb[s.Name], s.PropertyType);
lblGeneric = new Label();
lblGeneric.TabStop = false;
lblGeneric.Name = "lbl" + s.Caption.Substring(prefixParam.Length);
lblGeneric.Text = lblGeneric.Name.Substring(iPrefixControl + 2);
this.Controls.Add(lblGeneric);
txtGeneric = new RichTextBox();
txtGeneric.DetectUrls = true;
txtGeneric.Multiline = false;
if ("Pepe Juan WebOficial ".Contains(s.Caption + " "))
txtGeneric.Multiline = true;
txtGeneric.TabIndex = txtCount++;
txtGeneric.Name = "txt" + s.Caption.Substring(prefixParam.Length);
txtGeneric.DataBindings.Add(new Binding("Text", dt, s.Caption));
this.Controls.Add(txtGeneric);
}
Y aquí les sitúo:
private void posCtrlsInTP(TabPage tbpGeneric, List<Label> listLabel, int tbpGenericMaxHeight, DataTable dt, int iTabPages)
{
//SetTabHeader(tbpGeneric, this.BackColor);
Label lastLabel = new Label();
int iLbl = 0;
foreach (Label ctrlLabel in listLabel)
{
Control ctrlGeneric = this.Controls[this.Controls.IndexOf(ctrlLabel) + 1];
ctrlLabel.Parent = tbpGeneric;
ctrlLabel.Left = 2 * sepLeft;
ctrlLabel.Height = (int)(3F * sepTop);
ctrlLabel.Top = (lastLabel.Top == 0 ? 0 : sepTop) + lastLabel.Top + lastLabel.Height;
ctrlLabel.TabStop = false;
ctrlLabel.AutoSize = true;
((Label)ctrlLabel).TextAlign = ContentAlignment.MiddleCenter;
iLbl++;
if (iLbl > 3)
ctrlLabel.Left = ((tbpGeneric.Width / 2) + (tbpGeneric.Width / 8)) - ctrlLabel.Width - (2 * sepLeft);
else
ctrlLabel.Left = (tbpGeneric.Width / 8) - ctrlLabel.Width - (2 * sepLeft);
ctrlGeneric.Parent = tbpGeneric;
ctrlGeneric.Width = defWidth;
//ctrlGeneric.Left = (tbpGeneric.Width / iPosMiddle) - ctrlGeneric.Width - (3 * sepLeft);
ctrlGeneric.Left = ctrlLabel.Left + ctrlLabel.Width + (2 * sepLeft);
if ((iTabPages > 1) && (iTabPages < 5))
{
((RichTextBox)ctrlGeneric).Multiline = true;
ctrlGeneric.Height = tbpGeneric.Height - (3 * sepTop);
ctrlGeneric.Width = tbpGeneric.Width - (4 * sepLeft);
ctrlGeneric.Left = (sepLeft);
ctrlLabel.Visible = false;
}
else
ctrlGeneric.Height = ctrlLabel.Height + (1 * sepTop);
ctrlGeneric.Top = ctrlLabel.Top - (sepTop / 3); //re-Top for correct Alignment
ToolTip tt = new ToolTip();
string sName = ctrlGeneric.Name.Substring(iPrefixControl + 2);
string sTip = sName;// rmTT.GetString("TIP_" + sName.ToUpper());
tt.ReshowDelay = 400;
tt.AutoPopDelay = 20000;
tt.UseFading = true;
tt.IsBalloon = true;
tt.ToolTipTitle = sName;
tt.SetToolTip(ctrlGeneric, sTip);
lastLabel = ctrlLabel;
if (iLbl == 3)
lastLabel = new Label();
}
//calculate Maximum Height
int tbpGenericHeight = lastLabel.Top + lastLabel.Height + (4 * sepTop);
if (tbpGenericHeight > tbpGenericMaxHeight) tbpGeneric.Parent.Height = tbpGenericHeight;
}
Con este resultado:
(http://picload.org/image/ipiwcwa/ooccosmosmhd.jpg)
Hace muchos años que cargo casi todos los controles en tiempo de ejecución. Si mal no recuerdo, VB3 tenía la limitación de no poder crear controles, el formulario debía tener uno y a partir de el creabas una matriz de controles. Algo que como verás es´ta superado hace tiempo.
Saludos!!
Buenas
Añadir checkboxes a la UI suena bastante feo, aunque de todas formas no puedo hacer una buena evaluación del problema y post-recomendación sin saber el propósito de esos checkboxes, pero sea como sea yo creo que te convendría mucho más utilizar un ListBox al que añadirle las "opciones" o entradas, establecerle al control el modo de selección múltiple, y actualizar las entradas del ListBox en consecuencia. De esta manera sería un mecanismo más eficiente y además desocuparía parte del espacio que llenarían tantos chekboxes de la interfáz gráfica.
( ya te dió la idea el compañero @El Benjo, pero quise profundizar un poco en algunos de los motivos por los que podrías preferir utilizarlo. )
Aparte, quiero hacer un pequeño matíz:
Los controles implementan la interfáz IDisposable, por lo tanto se debe mantener una referencia a los controles que se crean en tiempo de ejecución, y una vez ya no se necesiten, liberarlos (llamando al método Control.Dispose()), de lo contrario se convierte en una metodología expensiva para la aplicación, pudiendo producir un buen leak de memoria dependiendo de cuantas veces se repita el procedimiento dinámico de creación de controles.
( Lo comento por si acaso para que lo tengas en cuenta si prefieres crear controles. )
Esto es una mezcla del ejemplo que ha mostrado el usuario @mOrfiUs0 y lo que he mencionado (aunque como ya dije, yo utilizaría un listbox):
' Si el orden de los controles te importa, entonces utiliza una colección de tipo [b]IList(of T)[/b].
Private ctrls As ICollection(Of Control) = New HashSet(Of Control)
Private Sub CreateControls(ByRef ctrls As ICollection(Of Control), ByVal dt As DataTable)
ctrls.Clear()
For Each col As DataColumn In dt.Columns.Cast(Of DataColumn)()
Dim ctrl As Control = New CheckBox
With ctrl
.Text = String.Empty
.Location = Point.Empty
' etc...
End With
ctrls.Add(ctrl)
Next
With Me
.SuspendLayout()
.Controls.AddRange(ctrls.ToArray)
.ResumeLayout()
End With
End Sub
Saludos
Cita de: Eleкtro en 20 Diciembre 2015, 04:32 AM
Aparte, quiero hacer un pequeño matíz:
Los controles implementan la interfáz IDisposable, por lo tanto se debe mantener una referencia a los controles que se crean en tiempo de ejecución...
No es necesario, por lo menos en c# y entiendo que IML es común. Así estás duplicando la información.
Un simple for each Control in Formulario.Controls te da una referencia y por tanto no es necesario almacenar otro array.
Para todos los controles que no están heredados de la API nativa de Windows, es decir no tienen un hWnd, el tema se complica un poco más, pero creo que esta fuera del alcance de este post
Saludos!!
Cita de: mOrfiUs0 en 20 Diciembre 2015, 15:03 PMNo es necesario, por lo menos en c# y entiendo que IML es común. Así estás duplicando la información.
¿Por qué antes de contradecir a alguien no haces la prueba del algodón con un profiller de memoria para .Net que te muestre las fugas o
leaks de memoria provocadas (y la relación a objetos, etc) antes de afirmar algo así?, las cosas se pueden decir de otra manera, parece que critiques por criticar o compitas por competir.
Simplemente dale un vistazo a la referencia del código fuente online de .Net Framework, a los controles built-in de
WinForms, y si profundizas verás de lo que hablo...
- http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,fa57e2a4f7bd434c
- http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeMethods.cs,85e8023899eb9d10
Cómo ya comenté, la class
Control (y
UserControl) implementa la interfáz
IDisposable; pues obviamente los controles built-in de
Winforms utilizan recursos administrados, cómo no administrados, por lo tanto se debe llamar al método implementado
Control.Dispose() si o si.
Hay otras classes que implementan la interfáz
IDisposable y no es estrictamente necesario llamar al método
Dispose() (ej. la class
Process), pero este no es el caso.
El caso en el que realmente no sería necesario llamar a
Control.Dispose(), sería si el control se añadiese a una colección de controles administrada (class
ControlCollection) y liberases el control padre de esa colección (por ejemplo, al añadir los controles a la colección de controles de un
Form, y despues de eso, liberar el
Form), en ese caso
WinForms administraría automaticamente las llamadas al método
Control.Dispose() por cada control de la colección, de lo contrario, no, y ese no es el caso, ya que aquí la pregunta trata sobre crear controles dinámica e indefinidamente cada vez que suceda "x" evento, y así... si no los liberas, el
garbage-collector no los va a liberar por ti.
Cita de: mOrfiUs0 en 20 Diciembre 2015, 15:03 PMUn simple for each Control in Formulario.Controls te da una referencia y por tanto no es necesario almacenar otro array.
1. No conozco a ninguna otra persona que le resulte más "simple" escribir código repetitivo con un For, que declarar una referencia de nombre corto o
shortname ref;
Indiscutiblemente es más productivo que escribir el bloque de un For, y es más eficiente que irerar una colección de controles hasta encontrar dicho control.
2. No es duplicar.
Aquí tienes un simple ejemplo demostrativo siguiendo la linea del código que publiqué arriba:
Dim ctrls As ICollection(Of Control) = New HashSet(Of Control) From {
New Control
}
Me.Controls.AddRange(ctrls.ToArray)
Me.Controls(0).Dispose()
MsgBox(ctrls(0).IsDisposed())
...Conviértelo a C# si quieres, estoy vago.
Cita de: mOrfiUs0 en 20 Diciembre 2015, 15:03 PMPara todos los controles que no están heredados de la API nativa de Windows, es decir no tienen un hWnd
Todo control (o user-control) es practicamente una ventana
Win32, ya que implementa la interfáz
IWin32Window, por lo tanto tienen un handle de ventana o
hWnd, todos, ya que los controles de
WinForms están basados en este modelo
Win32.
Sin embargo, si estuviesemos hablando de
WPF, la cosa sería distinta, pero obviamente tampoco es el caso. De todas formas de tecnología WPF si que se muy poquito, lo he usado muy poco.
Sin más, espero que esto haya sido suficiente con los motivos o argumentos que he dado. Si tienes preguntas al respecto, haz las que quieras (pero por favor, formula cualquier pregunta en un nuevo post, no invadamos más este hilo con offtopics).
Saludos.
vale