问题描述:

I want to create an arbitrary amount of labels and textboxes on a WPF window. Such a thing was easy to do in WinForms, and I thought I got how to do it in WPF, but I get strange results.

Namely what I want to visually happen is below (mocked). The amount and contents of the new controls is arbitrary, probably to be gotten from a text file. There's also the problem of making the form scrollable if there's a big amount of controls, but first things first.

So I named the default grid that VS creates to "grdWiz" and created the following utility function inside my window. Crude, I know, but first I want to make sure things work and beautify only afterwards. UPDATE: I now use a Canvas object instead of a Grid, and use the Canvas type instead of the InkCanvas type to try to set position. See below:

 private int nInputs = 0;

private void AddInput(string defLabel, string defValue)

{

Label newLabel = new Label() { Name = "lblConf" + nInputs };

TextBox newText = new TextBox() { Name = "tbConf" + nInputs };

grdWiz.Children.Add(newLabel);

Canvas.SetLeft(newLabel, 0);

Canvas.SetTop(newLabel, nInputs * 30);

newLabel.Width = grdWiz.Width / 3;

grdWiz.Children.Add(newText);

Canvas.SetLeft(newText, grdWiz.Width / 3);

Canvas.SetTop(newText, nInputs * 30);

newText.Width = grdWiz.Width * 0.6666;

newText.Height = 30;

newText.Text = defValue;

nInputs++;

}

Inside the button click code, I do something like:

 thatInitialLabel.Visibility = Visibility.Hidden;

AddInput("Main Course:", "Grits");

AddInput("Dessert:", "Apple Pie");

AddInput("Fun activity to be had afterwards:", "Sleep");

What I get is something like this:

I'm doing something obviously wrong, but I don't know what. Also, I will no longer emit opinions on the relative merits of GUI frameworks. Suffice it to say I'm one of these.

网友答案:

Well, you got the source of the problem right: WPF is not WinForms.

Without seeing the parent XAML, I can't say for sure what your current problem is. You are using attached properties that may not have any effect without the correct parent control. That being said, there is a much easier way.

First, create a class that models your data; say:

public class Input
{
    public string Label {get; set;}
    public string Value {get; set;}
}

Without going through how to set up MVVM: MVVM: Tutorial from start to finish?

Do this:

<ListView ItemsSource="{Binding InputCollection}">
   <ListView.ItemTemplate>
      <DataTemplate>
         <StackPanel Orientation="Horizontal">
             <TextBlock Text="{Binding Label}"/>
             <TextBox Text="{Binding Value}"/>
         </StackPanel>
      </DataTemplate>
   </ListView.ItemTemplate>
</ListView>

This does the following:

  1. Sets up a ListView that populates off of the "InputCollection" property in your ViewModel, which is likely an ObservableCollection<Input>
  2. Makes each item a horizontal stack panel that

    a. Has a text block bound to the "Label" property of the item

    b. Has a text box bound to the "Value" property of the item

Take that compared to the equivalent WinForms code. I would argue that it is much clearer, easier to maintain and understand, and overall, is much better practice. I would strongly disagree that life was "easier" with WinForms in this instance.

相关阅读:
Top