以编程方式将控件添加到WPF表单

use*_*757 26 wpf controls dynamic

我试图动态地(以编程方式)向UserControl添加控件.我从我的业务层(从数据库中检索)获得了一个通用的对象列表,并且对于每个对象,我想向WPF UserControl添加一个Label和一个TextBox,并设置位置和宽度以使其看起来很漂亮,并且希望利用WPF验证功能.这在Windows Forms编程中很容易,但我是WPF的新手.我该怎么做(见问题评论)说这是我的对象:

public class Field {
   public string Name { get; set; }
   public int Length { get; set; }
   public bool Required { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的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;
}
Run Code Online (Sandbox Code Playgroud)

Cha*_*lie 24

好吧,第二次是魅力.根据你的布局截图,我可以立即推断出你需要的是一个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; }
}
Run Code Online (Sandbox Code Playgroud)

这里没有太大的改变,但我编辑了示例字段以更好地匹配您的示例.现在让我们来看看魔术发生的地方 - 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>
Run Code Online (Sandbox Code Playgroud)

首先,你会注意到它ItemTemplate已经略有改变.标签仍然绑定到name属性,但现在文本框宽度绑定到length属性(因此您可以使用不同长度的文本框).此外,我已经为所有需要的字段添加了一个"*",使用了一个简单的BoolToVisibilityConverter(你可以在任何地方找到代码,我不会在这里发布).

要注意的主要是在我们WrapPanelItemsPanel财产中使用a ListBox.这告诉ListBox它生成的任何项目都需要被推入水平包装布局(这与您的屏幕截图相匹配).使这项工作更加出色的是面板上的高度和宽度绑定 - 这就是说,"使这个面板与我父窗口的尺寸相同".这意味着当我调整大小时Window,相应地WrapPanel调整其大小,从而为项目提供更好的布局.这里的两个截图证明了这一点:

替代文字http://img156.imageshack.us/img156/6849/wrappanelfields.png

替代文字http://img12.imageshack.us/img12/2426/wrappanelfields2.png


Job*_*Joy 17

建议不要添加这样的控件.理想情况下,您在WPF中执行的操作是放置ListBox(或ItemsControl)并将Business对象集合绑定为itemsControl.ItemsSource属性.现在在XAML中为DataObject类型定义DataTemplate,你很高兴,这就是WPF的神奇之处.

人们来自winforms背景倾向于按照你描述的方式进行,而这在WPF中不是正确的方式.

  • +1.我很乐意坦率地说,"这是错的." 使用正则表达式解析XML就是其中之一.在WPF应用程序中使用WinForms技术是另一种方法. (7认同)

Yot*_*aXP 8

我会听Charlie和Jobi的答案,但是为了直接回答这个问题...(如何添加控件并手动定位它们.)

使用Canvas控件,而不是Grid.画布为控件提供了无限的空间,并允许您手动定位它们.它使用附加属性来跟踪位置.在代码中,它看起来像这样:

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

在XAML ......

<Canvas>
  <TextBox Width="100" Canvas.Left="50" Canvas.Top="20" />
</Canvas>
Run Code Online (Sandbox Code Playgroud)

您还可以相对于右边和底边定位它们.指定"顶部"和"底部"将使控件与"画布"垂直调整大小.左右相似.