2010-05-11 6 views
22

동적으로 (프로그래밍 방식으로) UserControl에 컨트롤을 추가하려고합니다. 내 비즈니스 레이어 (데이터베이스에서 검색)에서 개체의 일반 목록을 얻고 각 개체에 대해 Label 및 TextBox를 WPF UserControl에 추가하고 위치 및 너비를 멋지게 만들고 잘하면되도록 설정하려고합니다. WPF 유효성 검사 기능을 활용하십시오. 이것은 Windows Forms 프로그래밍에서 쉽지만 WPF가 처음입니다. 나는이 (질문에 대한 의견을 참조) 말합니까 어떻게 내 객체입니다프로그래밍 방식으로 WPF 폼에 컨트롤 추가

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

답변

21

두 번째는 매력입니다. 레이아웃 스크린 샷을 기반으로 필요한 항목이 가장자리에 도달 할 때까지 항목을 채울 수있는 레이아웃 패널 인 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; } 
} 

많이하지 여기 변경,하지만 난 더 나은 예에 맞게 샘플 필드를 편집했습니다. 이제 마법은 Window의 XAML을 적 있었 어디를 살펴 보자 :

<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을 사용합니다.이 코드는 어디서나 코드를 찾을 수 있으며 여기서는 게시하지 않습니다.

주의해야 할 주요 사항은 의 ItemsPanel 속성에 WrapPanel을 사용하는 것입니다. 그러면 ListBox에 생성 된 모든 항목이 가로 포장 된 레이아웃으로 푸시되어야한다는 메시지가 표시됩니다 (스크린 샷과 일치 함). 이 작업을 더욱 효과적으로 만드는 것은 패널의 높이 및 너비 바인딩입니다.이 패널의 크기는 "이 패널을 부모 윈도우와 동일한 크기로 만듭니다."라고 말합니다. 즉, Window의 크기를 조정하면 WrapPanel이 크기를 적절하게 조정하므로 항목 레이아웃이 향상됩니다. 두 스크린 샷은 여기 보여줍니다

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

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

+0

+1,하지만 OP가 Length를 요구하고 입력을 제한해야한다고 생각합니다. –

+0

물론, 그가 제약 조건을 얼마나 정확히 사용하고 싶은지 잘 모르겠습니다. – Charlie

+0

문제가 있습니다. 변수 이름 "MyGrid"를 사용하여 문제를 혼동 시켰습니다. 이 아니라 DataGrid가 추가되었습니다. 이것은 특정 순서대로 데이터 입력 폼입니다. 레이블이 3 개가있을 수 있습니다./textbox combinesations는 한 줄에 다양한 길이를 가지고 있고 다음 줄에는 10 개가 있습니다.이 양식의 다른 버전이 있는데 왜 데이터베이스에서 필드가로드되고 왜 동적이라고 부르는 것입니까? DataGrid 레이아웃에 잘 맞게 & 이것이 내가 동적으로 만들려고했던 이유입니다. 객체에 다른 필드가 있습니다 - LastFieldOnLine은 새 라인을 지정합니다. – user210757

15
: 내 WPF UserControl을에서 다음

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

, 나는 각 개체에 대한 레이블 및 텍스트 상자를 만들려고 해요

이와 같은 컨트롤은 추가하지 않는 것이 좋습니다. WPF에서 이상적으로 ListBox (또는 ItemsControl)를 배치하고 Business Objects 컬렉션을 itemsControl.ItemsSource 속성으로 바인딩하는 것이 이상적입니다. 이제 DataObject 형식에 대해 XAML에서 DataTemplate을 정의하면 좋은 결과를 얻을 수 있습니다. WPF의 마술입니다.

winforms 배경에서 온 사람들은 당신이 설명한 방식대로하는 경향이 있으며 WPF에서 올바른 방법이 아닙니다.

+6

+1. 제가 기꺼이 말하고자하는 것은 거의 없습니다. "이것은 잘못되었습니다." 정규 표현식을 사용한 XML 구문 분석은 하나입니다. WPF 응용 프로그램에서 WinForms 기술을 사용하는 것도 또 다른 방법입니다. –

+0

아멘. --------- –

7
나는 찰리와 JOBI의 답변을들을 수 있지만 직접 질문에 대답을 위해서 것

(제어 및 수동으로 위치를 추가하는 방법 그들.)

Grid 대신 Canvas 컨트롤을 사용하십시오. 캔버스는 컨트롤에 무한한 공간을 제공하고 수동으로 위치를 지정할 수 있도록합니다. 첨부 된 속성을 사용하여 위치를 추적합니다. 코드에서, 그것과 같이 보일 것이다 : 당신은 또한 오른쪽과 아래쪽 가장자리에 그들이 상대 위치를 지정할 수 있습니다

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

XAML에서

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

을 .... 위쪽과 아래쪽을 모두 지정하면 컨트롤이 Canvas와 수직으로 크기가 조정됩니다. 마찬가지로 왼쪽과 오른쪽.

관련 문제