Home > English > Problems customizing the layout of a DataForm using a Grid.

Problems customizing the layout of a DataForm using a Grid.

The DataForm is a pretty powerful control. It comes built with a lot of functionality that you don’t need to write multiple times. One of the things I do a lot with the DataForm is override the ReadOnly and Edit templates. In this post I am going to show a simple model class that would represent a Customer object and collection. I will then go into the using the AutoGeneration=”True” attribute of the DataForm. Finally, I will walk you through two paths of customizing the look and feel of the DataForm. The first path seemed to me to be the better of the two but as we look at the output we will quickly realize that the latter is by far the better.

Okay so lets first examine the data object we will be using in your example:

namespace DataFormSample.Models
{
    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string BusinessName { get; set; }
        public string BusinessBio { get; set; }
        public string Fax { get; set; }
        public string Phone { get; set; }
    };
    public class Customers : ObservableCollection<Customer>
    {
        public Customers()
        {
            Customer c = new Customer()
            {
                FirstName = "George",
                LastName = "Washington",
                BusinessName = "Washington Mutual",
                BusinessBio = "This company specializes in watching your money.",
                Phone = "8005551212",
                Fax = "8005551213"
            };
            this.Add(c);
            c = new Customer()
            {
                FirstName = "Abraham",
                LastName = "Lincoln",
                BusinessName = "Lincoln Cabins",
                BusinessBio = "Good lodging for good folk.",
                Phone = "8005558100",
                Fax = "8005558101"
            };
            this.Add(c);

        }
    };
}

As you can see from the source code, I am exposing two classes. The first class is just a simple data transfer object with the name of Customer. The other class is a sample hydrated ObservableCollection that will act as the ItemsSource of the DataForm. You can see that I have added two records.

We will now look at the XAML required to get this class wired up to a DataForm with the AutoGenerateFields attribute set to “True”.

<UserControl x:Class="SilverlightApplication2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    xmlns:l="clr-namespace:SilverlightApplication2.Models"
    >
    <Grid x:Name="LayoutRoot" Background="White">
        <toolkit:DataForm Header="Data Entry Sample"
                          AutoGenerateFields="True">
            <toolkit:DataForm.ItemsSource>
                <l:Customers />
            </toolkit:DataForm.ItemsSource>
        </toolkit:DataForm>
	</Grid>
</UserControl>

When the sample appliation is run the output looks like this:

DataForm with AutoGenerateFields='True'

As you can see, the DataForm is great for generating simple admin screens. It doesn’t really look like something that you would want to throw in front of a client though.

Let’s look at the next set of approaches that will have the AutoGenerateFields attribute set to “False”.

The first example of XAML involves using a Grid for managing the layout in a DataForm:

<UserControl x:Class="SilverlightApplication2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    xmlns:l="clr-namespace:SilverlightApplication2.Models"
    >
    <UserControl.Resources>
		<DataTemplate x:Key="DataFormGridTemplate">
			<Grid>
				<Grid.ColumnDefinitions>
					<ColumnDefinition />
					<ColumnDefinition />
				</Grid.ColumnDefinitions>
				<Grid.RowDefinitions>
					<RowDefinition />
					<RowDefinition />
					<RowDefinition />
				</Grid.RowDefinitions>
				<toolkit:DataField Label="First Name" Grid.Column="0" Grid.Row="0">
					<TextBox Text="{Binding FirstName, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Last Name" Grid.Column="0" Grid.Row="1">
					<TextBox Text="{Binding LastName, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Business Name" Grid.Column="1" Grid.Row="0">
					<TextBox Text="{Binding BusinessName, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Bio of Business" Grid.Column="1" Grid.Row="1">
					<TextBox Text="{Binding BusinessBio, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Fax" Grid.Column="0" Grid.Row="2">
					<TextBox Text="{Binding Fax, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Phone" Grid.Column="1" Grid.Row="2">
					<TextBox Text="{Binding Phone, Mode=TwoWay}" />
				</toolkit:DataField>
			</Grid>
		</DataTemplate>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <toolkit:DataForm Header="Data Entry Sample"
			AutoGenerateFields="False"
			ReadOnlyTemplate="{StaticResource DataFormGridTemplate}"
			EditTemplate="{StaticResource DataFormGridTemplate}"
            >
            <toolkit:DataForm.ItemsSource>
                <l:Customers />
            </toolkit:DataForm.ItemsSource>
        </toolkit:DataForm>
	</Grid>
</UserControl>

This time when we run the application, the output looks like this:

DataForm Grid Template

Clearly something is wrong with the way I wrote that DataTemplate.

Update: It turns out that this is not at all a problem with the DataForm. It is merely an issue with my Grid definition. Please see Part II for further clarification.

Let’s take a look at the DataTemplate when it is outside of the DataForm:

<UserControl x:Class="SilverlightApplication2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    xmlns:l="clr-namespace:SilverlightApplication2.Models"
    >
    <UserControl.Resources>
		<DataTemplate x:Key="DataFormGridTemplate">
			<Grid>
				<Grid.ColumnDefinitions>
					<ColumnDefinition />
					<ColumnDefinition />
				</Grid.ColumnDefinitions>
				<Grid.RowDefinitions>
					<RowDefinition />
					<RowDefinition />
					<RowDefinition />
				</Grid.RowDefinitions>
				<toolkit:DataField Label="First Name" Grid.Column="0" Grid.Row="0">
					<TextBox Text="{Binding FirstName, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Last Name" Grid.Column="0" Grid.Row="1">
					<TextBox Text="{Binding LastName, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Business Name" Grid.Column="1" Grid.Row="0">
					<TextBox Text="{Binding BusinessName, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Bio of Business" Grid.Column="1" Grid.Row="1">
					<TextBox Text="{Binding BusinessBio, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Fax" Grid.Column="0" Grid.Row="2">
					<TextBox Text="{Binding Fax, Mode=TwoWay}" />
				</toolkit:DataField>
				<toolkit:DataField Label="Phone" Grid.Column="1" Grid.Row="2">
					<TextBox Text="{Binding Phone, Mode=TwoWay}" />
				</toolkit:DataField>
			</Grid>
		</DataTemplate>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <ContentControl Content="{Binding Customer}"
			ContentTemplate="{StaticResource DataFormGridTemplate}">
            <ContentControl.DataContext>
                <l:CustomerSample />
            </ContentControl.DataContext>
        </ContentControl>
	</Grid>
</UserControl>

When run we get this output which looks more like what I would have expected:

Grid Template

So is there something inherently wrong with the DataForm? It took me some time to stop staring at my DataTemplate and start examing what the documentation showed for building custom layout for the DataForm.

It turns out that the following DataTemplate example is the best suited for the DataForm:

<UserControl x:Class="SilverlightApplication2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    xmlns:l="clr-namespace:SilverlightApplication2.Models"
    >
    <UserControl.Resources>
        <DataTemplate x:Key="DataFormStackPanelTemplate">
            <StackPanel Orientation="Horizontal">
                <StackPanel toolkit:DataField.IsFieldGroup="True">
                    <toolkit:DataField Label="First Name">
                        <TextBox Text="{Binding FirstName, Mode=TwoWay}" />
                    </toolkit:DataField>
                    <toolkit:DataField Label="Last Name">
                        <TextBox Text="{Binding LastName, Mode=TwoWay}" />
                    </toolkit:DataField>
                    <toolkit:DataField Label="Fax">
                        <TextBox Text="{Binding Fax, Mode=TwoWay}" />
                    </toolkit:DataField>
                </StackPanel>
                <StackPanel toolkit:DataField.IsFieldGroup="True">
                    <toolkit:DataField Label="Business Name">
                        <TextBox Text="{Binding BusinessName, Mode=TwoWay}" />
                    </toolkit:DataField>
                    <toolkit:DataField Label="Bio of Business">
                        <TextBox Text="{Binding BusinessBio, Mode=TwoWay}" />
                    </toolkit:DataField>
                    <toolkit:DataField Label="Phone">
                        <TextBox Text="{Binding Phone, Mode=TwoWay}" />
                    </toolkit:DataField>
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <toolkit:DataForm Header="Data Entry Sample"
			AutoGenerateFields="False"
			ReadOnlyTemplate="{StaticResource DataFormStackPanelTemplate}"
			EditTemplate="{StaticResource DataFormStackPanelTemplate}"
			>
            <toolkit:DataForm.ItemsSource>
                <l:Customers />
            </toolkit:DataForm.ItemsSource>
        </toolkit:DataForm>
	</Grid>
</UserControl>

When run, we get this output as expected:

DataForm StackPanel Template

So the secret is in the sauce! Well not really it is actually a little attached property that tells the StackPanel that it is in a FieldGroup. Once you have that in place, you should be able to author your custom DataForm layouts to your heart’s desire.

Note: It may seem like this is an intuitive problem but I was certain that I was doing something wrong with my Grid and it took me way too long to solve this. I personally lean towards using the Grid as a carpentar would lean toward this hammer.

Hope this helps you and keeps you from any frustrations that I have already had! 🙂

Advertisements
  1. Karl Shifflett
    July 22, 2010 at 4:17 am

    Matt,

    In your first example, why not use Auto or Fixed sizing instead of *. That why it would look like your last example.

    Cheers,

    Karl

  1. No trackbacks yet.

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: