Программно добавлять элементы управления в форму WPF - PullRequest
25 голосов
/ 12 мая 2010

Я пытаюсь добавить элементы управления в UserControl динамически (программно). Я получаю общий список объектов из моего бизнес-уровня (полученный из базы данных), и для каждого объекта я хочу добавить метку и TextBox в пользовательский элемент управления WPF и установить положение и ширину, чтобы сделать внешний вид красивым, и, надеюсь, использовать возможности проверки WPF. Это то, что было бы легко в программировании Windows Forms, но я новичок в WPF. Как мне это сделать (см. Комментарии к вопросам) Скажите, что это мой объект:

public class Field {
   public string Name { get; set; }
   public int Length { get; set; }
   public bool Required { get; set; }
}

Затем в моем WPF UserControl я пытаюсь создать Label и TextBox для каждого объекта:

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;
}

Ответы [ 3 ]

23 голосов
/ 12 мая 2010

Хорошо, второй раз это очарование. Основываясь на скриншоте вашего макета, я сразу могу сделать вывод, что вам нужна WrapPanel, панель макета, которая позволяет элементам заполняться до тех пор, пока не достигнет края, после чего оставшиеся элементы переходят на следующую строку. Но вы все еще хотите использовать ItemsControl, чтобы получить все преимущества привязки данных и динамической генерации. Поэтому для этого мы будем использовать свойство ItemsControl.ItemsPanel, которое позволяет нам указать панель, в которую будут помещены элементы. Давайте снова начнем с кода:

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; }
}

Здесь мало что изменилось, но я отредактировал примеры полей, чтобы лучше соответствовать вашему примеру. Теперь давайте посмотрим, где происходит волшебство - XAML для 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>

Во-первых, вы заметите, что ItemTemplate немного изменился. Метка по-прежнему привязана к свойству name, но теперь ширина текстового поля привязана к свойству length (поэтому вы можете иметь текстовые поля различной длины). Кроме того, я добавил «*» во все обязательные поля, используя упрощенный BoolToVisibilityConverter (код которого вы можете найти где угодно, и я не буду публиковать здесь).

Главное, на что нужно обратить внимание - это использование WrapPanel в ItemsPanel свойстве ListBox. Это говорит ListBox, что любые элементы, которые он генерирует, должны быть помещены в горизонтальную упаковку (это соответствует вашему скриншоту). Что делает эту работу еще лучше, так это привязка к высоте и ширине на панели - это говорит о том, что «сделайте эту панель того же размера, что и мое родительское окно». Это означает, что когда я изменяю размер Window, WrapPanel соответствующим образом корректирует его размер, что улучшает расположение элементов.

16 голосов
/ 12 мая 2010

Не рекомендуется добавлять подобные элементы управления. В WPF вы в идеале должны поместить ListBox (или ItemsControl) и связать свою коллекцию бизнес-объектов как свойство itemsControl.ItemsSource. Теперь определите DataTemplate в XAML для вашего типа DataObject, и все готово. В этом волшебство WPF.

Люди, происходящие из винформ, имеют тенденцию поступать так, как вы описали, а это не совсем правильно в WPF.

7 голосов
/ 12 мая 2010

Я бы слушал ответы Чарли и Джоби, но ради того, чтобы ответить на вопрос напрямую ... (Как добавить элементы управления и расположить их вручную.)

Используйте элемент управления Canvas вместо Grid. Холсты дают элемент управления бесконечное количество места, и позволяют размещать их вручную. Он использует вложенные свойства для отслеживания положения. В коде это будет выглядеть так:

var tb = new TextBox();
myCanvas.Children.Add(tb);
tb.Width = 100;
Canvas.SetLeft(tb, 50);
Canvas.SetTop(tb, 20);

В XAML ...

<Canvas>
  <TextBox Width="100" Canvas.Left="50" Canvas.Top="20" />
</Canvas>

Вы также можете расположить их относительно правого и нижнего края. При указании вершины и низа элемент управления будет изменен по вертикали с помощью Canvas. Аналогично для левого и правого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...