Flexible and Easy to Build Data Entry Forms in WPF

One of the things I find I’m often building in a WPF app is a nicely laid out Data Entry Form, with Label: TextBox combinations all down the page.

So usually you might build a form like this:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <TextBlock
        Text="First Name: " />
    <TextBox Grid.Column="1"
        Text="{Binding Path=FirstName}" />
</Grid>

The problem with this comes when you need to add another field you can’t just add it to the bottom, you have to add Row Definitions to the grid and manually specify the row for each TextBlock and TextBox.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <TextBlock
        Text="First Name: " />
    <TextBox Grid.Column="1"
        Text="{Binding Path=FirstName}" />
    <TextBlock Grid.Row="1"
        Text="Last Name: " />
    <TextBox
        Grid.Row="1"
        Grid.Column="1"
        Text="{Binding Path=LastName}" />
</Grid>

Now imagine you have 20 fields in this form, and you need to insert Middle Name between First and Last name… You’d have to re-number the rows for EVERY TextBlock and TextBox in the grid!!

The Better Way

What you’d like to be able to do is just add these Label/Field combinations in wherever and they all get stacked on top of each other.

So why not use a StackPanel? Well the problem with having all of your TextBLock and TextBox combinations in a StackPanel is the column widths no longer align. Here’s how you get around that:

<StackPanel
    Grid.IsSharedSizeScope="True">
    <StackPanel.Resources>
        <ControlTemplate
            x:Key="LabelledTextBox"
            TargetType="{x:Type ContentControl}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition
                        SharedSizeGroup="Label"
                        Width="Auto" />
                    <ColumnDefinition
                        SharedSizeGroup="Content"
                        Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock
                    Text="{TemplateBinding Content}" />
                <TextBox
                    Grid.Column="1"
                    Text="{Binding Path=.}" />
            </Grid>
        </ControlTemplate>
    </StackPanel.Resources>
    <ContentControl
        Template="{StaticResource LabelledTextBox}"
        Content="First Name: "
        DataContext="{Binding Path=FirstName}" />
    <ContentControl
        Template="{StaticResource LabelledTextBox}"
        Content="Last Name: "
        DataContext="{Binding Path=LastName}" />
</StackPanel>

Now if you need to add a Middle Name you just have to insert a content control where it belongs. These even gives you the flexibility to have different templates for different field types e.g. a LabelledDropDownList template could be added.

Enjoy!

Advertisements
  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: