I am trying to add controls to a UserControl dynamically (programatically). I get a generic List of objects from my Business Layer (retrieved from the database), and for each object, I want to add a Label, and a TextBox to the WPF UserControl and set the Position and widths to make look nice, and hopefully take advantage of the WPF Validation capabilities. This is something that would be easy in Windows Forms programming but I'm new to WPF. How do I do this (see comments for questions) Say this is my object:
public class Field {
public string Name { get; set; }
public int Length { get; set; }
public bool Required { get; set; }
}
Then in my WPF UserControl, I'm trying to create a Label and TextBox for each object:
public void createControls() {
List<Field> fields = businessObj.getFields();
Label label = null;
TextBox textbox = null;
foreach (Field field in fields) {
label = new Label();
// HOW TO set text, x and y (margin), width, validation based upon object?
// i have tried this without luck:
// Binding b = new Binding("Name");
// BindingOperations.SetBinding(label, Label.ContentProperty, b);
MyGrid.Children.Add(label);
textbox = new TextBox();
// ???
MyGrid.Children.Add(textbox);
}
// databind?
this.DataContext = fields;
}
Best Answer
Alright, second time's the charm. Based on your layout screenshot, I can infer right away that what you need is a
WrapPanel
, a layout panel that allows items to fill up until it reaches an edge, at which point the remaining items flow onto the next line. But you still want to use anItemsControl
so you can get all the benefits of data-binding and dynamic generation. So for this we're going to use theItemsControl.ItemsPanel
property, which allows us to specify the panel the items will be put into. Let's start with the code-behind again:Not much has changed here, but I've edited the sample fields to better match your example. Now let's look at where the magic happens- the XAML for the
Window
:First, you will notice that the
ItemTemplate
has changed slightly. The label is still bound to the name property, but now the textbox width is bound to the length property (so you can have textboxes of varying length). Furthermore, I've added a "*" to any fields that are required, using a simplisticBoolToVisibilityConverter
(which you can find the code for anywhere, and I will not post here).The main thing to notice is the use of a
WrapPanel
in theItemsPanel
property of ourListBox
. This tells theListBox
that any items it generates need to be pushed into a horizontal wrapped layout (this matches your screenshot). What makes this work even better is the height and width binding on the panel- what this says is, "make this panel the same size as my parent window." That means that when I resize theWindow
, theWrapPanel
adjusts its size accordingly, resulting in a better layout for the items.