3

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.

Before creating controls

After creating controls

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:

Slightly-less-epic FAIL

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.

9
  • your controls should be added as children of your canvas. I think a simple Canvas control is enough. You can use a ScrollViewer, put the Canvas in this scrollviewer and add controls to the canvas. Hopefully when the controls are filled out of the visible bounds, the scrollbars will appear and let user scroll to view the hidden parts. Commented Oct 6, 2014 at 20:55
  • 1
    No, a <Grid /> is a Grid. Replace it with a <Canvas></Canvas>. Commented Oct 6, 2014 at 21:00
  • 2
    WPF will let you do it the same as WinForms (basically) but also much more powerful and maintainable with data-binding and Templates. Your choice. Commented Oct 6, 2014 at 21:02
  • 1
    That article is pretty funny. I understand where you are coming from, but forsaking progress for the sake of forsaking progress seems a bit silly to me (especially at the start of a project, where it appears you are)... Anyways, I'll leave my answer up in case it is useful to future readers. If you want to talk about MVVM. I'd be happy to go chat with you. I don't see the obvious flaw in your code, but WPF wasn't intended to be written this way, so its really hard to tell. Perhaps do the Children.Add last? The layout engine may not be picking up the updates. Commented Oct 6, 2014 at 21:54
  • 1
    So you know, Canvas and ScrollViewer typically don't mix. You may want to try StackPanel so it does the layout for you. Incidentally, that is what ListView uses under the hood (by default). Commented Oct 6, 2014 at 22:10

1 Answer 1

6

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.