Solución:
Muy bien, la segunda es la vencida. Según la captura de pantalla de su diseño, puedo inferir de inmediato que lo que necesita es un WrapPanel
, un panel de diseño que permite que los elementos se llenen hasta llegar a un borde, momento en el que los elementos restantes fluyen a la siguiente línea. Pero todavía quieres usar un ItemsControl
para que pueda obtener todos los beneficios del enlace de datos y la generación dinámica. Entonces, para esto, usaremos el ItemsControl.ItemsPanel
propiedad, que nos permite especificar el panel en el que se colocarán los elementos. Comencemos con el código subyacente nuevamente:
public partial class Window1 : Window
{
public ObservableCollection<Field> Fields { get; set; }
public Window1()
{
InitializeComponent();
Fields = new ObservableCollection<Field>();
Fields.Add(new Field() { Name = "Username", Length = 100, Required = true });
Fields.Add(new Field() { Name = "Password", Length = 80, Required = true });
Fields.Add(new Field() { Name = "City", Length = 100, Required = false });
Fields.Add(new Field() { Name = "State", Length = 40, Required = false });
Fields.Add(new Field() { Name = "Zipcode", Length = 60, Required = false });
FieldsListBox.ItemsSource = Fields;
}
}
public class Field
{
public string Name { get; set; }
public int Length { get; set; }
public bool Required { get; set; }
}
No ha cambiado mucho aquí, pero he editado los campos de muestra para que coincidan mejor con su ejemplo. Ahora veamos dónde ocurre la magia: el XAML para el Window
:
<Window x:Class="DataBoundFields.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataBoundFields"
Title="Window1" Height="200" Width="300">
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisConverter"/>
</Window.Resources>
<Grid>
<ListBox x:Name="FieldsListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name}" VerticalAlignment="Center"/>
<TextBox Width="{Binding Length}" Margin="5,0,0,0"/>
<Label Content="*" Visibility="{Binding Required, Converter={StaticResource BoolToVisConverter}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=ActualHeight}"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=ActualWidth}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
Primero, notará que el ItemTemplate
ha cambiado ligeramente. La etiqueta todavía está vinculada a la propiedad del nombre, pero ahora el ancho del cuadro de texto está vinculado a la propiedad de la longitud (por lo que puede tener cuadros de texto de longitud variable). Además, agregué un “*” a todos los campos que son obligatorios, usando un simplista BoolToVisibilityConverter
(que puede encontrar el código en cualquier lugar, y no lo publicaré aquí).
Lo principal a notar es el uso de un WrapPanel
en el ItemsPanel
propiedad de nuestro ListBox
. Esto le dice al ListBox
que cualquier elemento que genere debe insertarse en un diseño envuelto horizontal (esto coincide con su captura de pantalla). Lo que hace que esto funcione aún mejor es la encuadernación de alto y ancho en el panel, lo que dice es, “haz que este panel tenga el mismo tamaño que mi ventana principal”. Eso significa que cuando cambio el tamaño del Window
, los WrapPanel
ajusta su tamaño en consecuencia, lo que resulta en un mejor diseño de los elementos.
No se recomienda agregar controles como este. Lo que idealmente hace en WPF es colocar un ListBox (o ItemsControl) y vincular su colección de objetos comerciales como la propiedad itemsControl.ItemsSource. Ahora defina DataTemplate en XAML para su tipo de DataObject y estará listo, esa es la magia de WPF.
Las personas que provienen de un entorno de winforms tienden a hacer lo que describiste y que no es la forma correcta en WPF.
Escucharía las respuestas de Charlie y Jobi, pero por el simple hecho de responder la pregunta directamente … (Cómo agregar controles y colocarlos manualmente).
Utilizar una Canvas
control, en lugar de un Grid
. Los lienzos le dan al control una cantidad infinita de espacio y le permiten colocarlos manualmente. Utiliza propiedades adjuntas para realizar un seguimiento de la posición. En código, se vería así:
var tb = new TextBox();
myCanvas.Children.Add(tb);
tb.Width = 100;
Canvas.SetLeft(tb, 50);
Canvas.SetTop(tb, 20);
En XAML …
<Canvas>
<TextBox Width="100" Canvas.Left="50" Canvas.Top="20" />
</Canvas>
También puede colocarlos en relación con los bordes derecho e inferior. Si especifica tanto la parte superior como la inferior, el control cambiará de tamaño verticalmente con el lienzo. De manera similar para Izquierda y Derecha.