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.

JCCyC
  • 16,140
  • 11
  • 48
  • 75
  • 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. – King King Oct 06 '14 at 20:55
  • The default window is just a Window object and a System.Windows.Controls.Grid inside it. That's a canvas, right? – JCCyC Oct 06 '14 at 20:59
  • 1
    No, a `` is a `Grid`. Replace it with a ``. – H H Oct 06 '14 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. – H H Oct 06 '14 at 21:02
  • And you were using the attached property for `InkCanvas` which will do nothing unless contained in one of those. Still, MVVM is **way** easier here. – BradleyDotNET Oct 06 '14 at 21:04
  • @JCCyC To your point, both `Grid` and `Canvas` are `Panel` objects. – BradleyDotNET Oct 06 '14 at 21:05
  • @Henk: Replacing with did get me a bit of progress, but it's still ignoring left positioning and width. Also, the labels don't show up at all. (See edits.) – JCCyC Oct 06 '14 at 21:37
  • 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. – BradleyDotNET Oct 06 '14 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). – BradleyDotNET Oct 06 '14 at 22:10

1 Answers1

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.

Community
  • 1
  • 1
BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117