2010-05-11 12 views
22

Ich versuche, Steuerelemente zu einem UserControl dynamisch (programmgesteuert) hinzuzufügen. Ich erhalte eine generische Liste von Objekten aus meiner Business-Schicht (aus der Datenbank abgerufen), und für jedes Objekt möchte ich eine Beschriftung und eine TextBox zum WPF-Benutzersteuerelement hinzufügen und die Position und die Breite so einstellen, dass sie schön aussieht und hoffentlich Nutzen Sie die WPF-Validierungsfunktionen. Dies ist etwas, das in der Windows Forms-Programmierung einfach wäre, aber ich bin neu in WPF. Wie kann ich dies tun (Kommentare zu Fragen sehen) sagen, das ist mein Ziel:Programmgesteuertes Hinzufügen von Steuerelementen zu WPF-Formular

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

Da ist in meinem WPF-Usercontrol, ich versuche ein Label und TextBox für jedes Objekt zu erstellen:

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

Antwort

21

Okay, das zweite Mal ist der Charme. Basierend auf Ihrem Layout-Screenshot kann ich sofort ableiten, dass Sie eine WrapPanel benötigen, ein Layout-Panel, mit dem Elemente gefüllt werden können, bis sie eine Kante erreicht haben. An diesem Punkt fließen die verbleibenden Elemente in die nächste Zeile. Aber Sie möchten immer noch eine ItemsControl verwenden, damit Sie alle Vorteile der Datenbindung und dynamischen Generierung erhalten können. Deshalb werden wir die ItemsControl.ItemsPanel Eigenschaft verwenden, die es uns ermöglicht, das Panel anzugeben, in das die Elemente eingefügt werden. Beginnen wir mit der Code-behind wieder von vorn anfangen:

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

Nicht viel hat sich hier geändert, aber ich habe die Beispielfelder bearbeitet, um besser Ihrem Beispiel zu entsprechen. Nun wollen wir sehen, wo die Magie happens- die XAML für die 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> 

Zunächst werden Sie feststellen, dass die ItemTemplate leicht verändert hat. Das Label ist immer noch an die Eigenschaft name gebunden, aber jetzt ist die Breite des Textfelds an die length -Eigenschaft gebunden (so können Sie Textboxen unterschiedlicher Länge haben). Darüber hinaus habe ich ein "*" zu allen Feldern hinzugefügt, die benötigt werden, mit einer vereinfachten BoolToVisibilityConverter (wo Sie den Code überall finden können, und ich werde hier nicht posten).

Die Hauptsache ist die Verwendung einer WrapPanel in der ItemsPanel Eigenschaft unserer ListBox. Dies sagt der ListBox, dass alle Elemente, die es erzeugt, in ein horizontal gewundenes Layout (das entspricht Ihrem Screenshot) geschoben werden müssen. Was diese Arbeit noch besser macht, ist die Höhe und die Breite, die auf dem Panel gebunden werden - was das sagt, "mache dieses Panel zur gleichen Größe wie mein Elternfenster." Das bedeutet, dass, wenn ich die Größe Window, die WrapPanel passt seine Größe entsprechend, was zu einem besseren Layout für die Elemente. Die beiden Screenshots hier zeigen dies:

alt text http://img156.imageshack.us/img156/6849/wrappanelfields.png

alt text http://img12.imageshack.us/img12/2426/wrappanelfields2.png

+0

+1, aber ich denke, das OP wollte Länge und Erforderlich, um die Eingabe zu beschränken. –

+0

Natürlich, aber ich bin nicht sicher, wie genau er diese Einschränkungen verwenden will, also habe ich es einfach ohne sie verlassen. – Charlie

+0

Problem ist - und ich habe wahrscheinlich verwirrt, indem Sie den Variablennamen "MyGrid" -Ich füge diese zu einem , kein DataGrid.Dies ist ein Dateneingabeformular, das ich in einer bestimmten Reihenfolge herausgeben muss.Es könnte 3 Label sein/textbox combminations mit texboxes unterschiedlicher Länge in einer Zeile und 10 in der nächsten Zeile. Es gibt verschiedene Versionen dieses Formulars, warum die Felder aus der Datenbank geladen werden, und warum ich es dynamisch nennen. So tut es nicht passen gut in ein DataGrid-Layout und deshalb habe ich versucht, sie dynamisch zu erstellen.Sagen, ich habe ein anderes Feld im Objekt - LastFieldOnLine, um neue Zeile – user210757

15

Es wird nicht empfohlen, solche Steuerelemente hinzuzufügen. Was Sie idealerweise in WPF tun, ist, eine ListBox (oder ItemsControl) zu setzen und Ihre Business-Objektsammlung als itemsControl.ItemsSource-Eigenschaft zu binden. Definieren Sie jetzt DataTemplate in XAML für Ihren DataObject-Typ und Sie können loslegen. Das ist die Magie von WPF.

Menschen kommen aus einem Winforms-Hintergrund neigen dazu, die Art und Weise zu tun, die Sie beschrieben haben, und das ist nicht der richtige Weg in WPF.

+6

+1. Es gibt sehr wenige Dinge, über die ich zu sagen bereit bin: "Das ist einfach falsch." Das Analysieren von XML mit regulärem Ausdruck ist eins. Die Verwendung von WinForms-Techniken in WPF-Anwendungen ist eine andere. –

+0

Amen. --------- –

7

ich zu Charlie und Jobi die Antworten hören würde, sondern um die Frage direkt zu beantworten ... (Wie Steuerelemente hinzufügen und manuell Position Sie.)

Verwenden Sie eine Canvas Steuerung anstelle einer Grid. Leinwände geben der Steuerung unendlich viel Platz und erlauben Ihnen, sie manuell zu positionieren. Es verwendet angehängte Eigenschaften, um die Position zu verfolgen. Im Code würde es aussehen, wie so:

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

in XAML ...

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

Sie sie auch in Bezug auf den rechten und unteren Rand positionieren kann. Wenn Sie sowohl einen oberen als auch einen unteren Rand angeben, ändert sich die Größe des Steuerelements vertikal mit dem Canvas. Ähnlich für Links und Rechts.

Verwandte Themen