Putting multiple elements in a Silverlight 3 DataForm DataField while retaining auto Label and Description generation
- Name your DataField
- Add an ItemsControl as wrapping element inside the DataField
- Bind the property to the ItemsControl
- Bind the property of your controls inside the ItemsControl to the ItemsControls' DataContext
using System; using System.ComponentModel.DataAnnotations; namespace DataFormDemo { public class Coordinate { private double _xmin; public double XMin { get { return _xmin; } } private double _ymin; public double YMin { get { return _ymin; } } private double _xmax; public double XMax { get { return _xmax; } } private double _ymax; public double YMax { get { return _ymax; } } public Coordinate(double x, double y, double xLow, double yLow, double xHigh, double yHigh) { _x = x; _y = y; _xmax = xHigh; _ymax = yHigh; _xmin = xLow; _ymin = yLow; } private double _x; [Display(Name = "X Coordinate:", Description = "The X coordinate of the point")] public double X { get { return _x; } set { ValidateValue(value, XMin, XMax); _x = value; } } private double _y; [Display(Name = "Y Coordinate:", Description = "The Y coordinate of the point")] public double Y { get { return _y; } set { ValidateValue(value, YMin, YMax); _y = value; } } private void ValidateValue(double value, double minValue, double maxValue) { if (value < minValue || value > maxValue) { throw new ArgumentException( string.Format("Valid value {0} - {1}", minValue,maxValue)); } } } }and just like Mike did, I added some labels and descriptions (in red) to get a nicer looking form. The purpose of this class is that a user can enter an X and Y value that fall in a range, which is validated. All parameters are set by the constructor. Then I made some minimal XAML
<UserControl x:Class="DataFormDemo.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" xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit" mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot"> <dataFormToolkit:DataForm x:Name="MyForm" HorizontalAlignment="Center" VerticalAlignment="Top"> </dataFormToolkit:DataForm> </Grid> </UserControl>Then I bound a Coordinate in the constructor
public MainPage() { InitializeComponent(); MyForm.CurrentItem = new Coordinate( 150000, 450000, 0, 300000, 300000, 600000); }

<dataFormToolkit:DataForm x:Name="MyForm" HorizontalAlignment="Center" VerticalAlignment="Top"> <dataFormToolkit:DataForm.EditTemplate> <DataTemplate> <StackPanel> <dataFormToolkit:DataField > <TextBox Text="{Binding X,Mode=TwoWay}"/> </dataFormToolkit:DataField> <dataFormToolkit:DataField > <TextBox Text="{Binding Y,Mode=TwoWay}"/> </dataFormToolkit:DataField> </StackPanel> </DataTemplate> </dataFormToolkit:DataForm.EditTemplate> </dataFormToolkit:DataForm>
That worked out nicely, but wanting to make this a little more slickey and user friendly I introduced a slider connected to the text box with the new Silverlight 3 component-to-component binding:
<dataFormToolkit:DataForm x:Name="MyForm" HorizontalAlignment="Center" VerticalAlignment="Top"> <dataFormToolkit:DataForm.EditTemplate> <DataTemplate> <StackPanel> <dataFormToolkit:DataField > <StackPanel> <TextBox x:Name="tbX" Text="{Binding X, Mode=TwoWay}"/> <Slider Maximum="{Binding XMax}" Minimum="{Binding XMin}" Value="{Binding Text, ElementName=tbX, Mode=TwoWay}"/> </StackPanel> </dataFormToolkit:DataField> <dataFormToolkit:DataField > <StackPanel> <TextBox x:Name="tbY" Text="{Binding Y, Mode=TwoWay}"/> <Slider Maximum="{Binding YMax}" Minimum="{Binding YMin}" Value="{Binding Text, ElementName=tbY, Mode=TwoWay}"/> </StackPanel> </dataFormToolkit:DataField> </StackPanel> </DataTemplate> </dataFormToolkit:DataForm.EditTemplate> </dataFormToolkit:DataForm>

<dataFormToolkit:DataForm x:Name="MyForm" HorizontalAlignment="Center" VerticalAlignment="Top"> <dataFormToolkit:DataForm.EditTemplate> <DataTemplate> <StackPanel> <dataFormToolkit:DataField> <ItemsControl x:Name="icX" DataContext="{Binding X,Mode=TwoWay}"> <StackPanel> <TextBox x:Name="tbX" Text="{Binding DataContext, ElementName=icX, Mode=TwoWay}"/> <Slider Maximum="{Binding XMax}" Minimum="{Binding XMin}" Value="{Binding Text, ElementName=tbX, Mode=TwoWay}"/> </StackPanel> </ItemsControl> </dataFormToolkit:DataField> <dataFormToolkit:DataField > <ItemsControl x:Name="icY" DataContext="{Binding Y,Mode=TwoWay}"> <StackPanel> <TextBox x:Name="tbY" Text="{Binding DataContext, ElementName=icY, Mode=TwoWay}"/> <Slider Maximum="{Binding YMax}" Minimum="{Binding YMin}" Value="{Binding Text, ElementName=tbY, Mode=TwoWay}"/> </StackPanel> </ItemsControl> </dataFormToolkit:DataField> </StackPanel> </DataTemplate> </dataFormToolkit:DataForm.EditTemplate> </dataFormToolkit:DataForm>But now my sliders do not work anymore: they are locked in the right position. This is logical, because the DataContext of anything inside the ItemsControl is now no longer the Coordinate object, but its X or Y property. So XMin, XMax, YMin and YMax are now unknown (and thus zero). This, too, can be solved by naming your DataFields, using the Path binding syntax and then once again using control-to-control binding:
<dataFormToolkit:DataForm x:Name="MyForm" HorizontalAlignment="Center" VerticalAlignment="Top"> <dataFormToolkit:DataForm.EditTemplate> <DataTemplate> <StackPanel> <dataFormToolkit:DataField x:Name="dfX"> <ItemsControl x:Name="icX" DataContext="{Binding X,Mode=TwoWay}"> <StackPanel> <TextBox x:Name="tbX" Text="{Binding DataContext, ElementName=icX, Mode=TwoWay}"/> <Slider Maximum="{Binding Path=DataContext.XMax,ElementName=dfX}" Minimum="{Binding Path=DataContext.XMin,ElementName=dfX}" Value="{Binding Text, ElementName=tbX, Mode=TwoWay}"/> </StackPanel> </ItemsControl> </dataFormToolkit:DataField> <dataFormToolkit:DataField x:Name="dfY"> <ItemsControl x:Name="icY" DataContext="{Binding Y,Mode=TwoWay}"> <StackPanel> <TextBox x:Name="tbY" Text="{Binding DataContext, ElementName=icY, Mode=TwoWay}"/> <Slider Maximum="{Binding Path=DataContext.YMax,ElementName=dfY}" Minimum="{Binding Path=DataContext.YMin,ElementName=dfY}" Value="{Binding Text, ElementName=tbY, Mode=TwoWay}"/> </StackPanel> </ItemsControl> </dataFormToolkit:DataField> </StackPanel> </DataTemplate> </dataFormToolkit:DataForm.EditTemplate> </dataFormToolkit:DataForm>
