I've been scratching my head over this one for a few days now. I'm trying to create a UserControl
(s) that will allow me to simplify the process of creating I/O forms. In many forms I need something with a structure such as this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto">
<ColumnDefinition Width="*">
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition>
<RowDefinition>
<RowDefinition>
...
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="..."/>
<TextBox Grid.Column="1" Grid.Row="0" Text="..."/>
<TextBlock Grid.Column="0" Grid.Row="1" Text="..."/>
<TextBox Grid.Column="1" Grid.Row="1" Text="..."/>
<TextBlock Grid.Column="0" Grid.Row="2" Text="..."/>
<TextBox Grid.Column="1" Grid.Row="2" Text="..."/>
...
<Grid>
What I'd like to write is something like this:
<mycontrols:ContainerControl>
<mycontrols:RowControl Label="Some label" Text="{Binding SomeProperty}">
<mycontrols:RowControl Label="Some label" Text="{Binding SomeProperty}">
<mycontrols:RowControl Label="Some label" Text="{Binding SomeProperty}">
<mycontrols:RowControl Label="Some label" Text="{Binding SomeProperty}">
</mycontrols:ContainerControl>
The RowControl
's XAML would simply be a TextBlock
and a TextBox
style to my liking:
<TextBlock Text="{Binding Label}"/>
<TextBlock Text="{Binding Text}"/>
As I understand there are a few issues with this:
1.- In order that an element is affected by the grid layout, it has to be an immediate child of that Grid.
2.- The number of rows is arbitrary. In a Grid
you need to specify each row definition.
If I could somehow have the Grid
's layout affect not only immediate children but nested ones, I think I could do this. I'm also willing to solve this with a different approach if you can think of one.
The expected finally behavior is a mixture of a StackPanel
and a Grid
, which is:
~ An arbitrary amount of elements can be added without defining rows (just stack them).
~ The container is divided into two columns for, first takes the size of the biggest element contained (auto), and the second takes the rest of the width available (*).
All help is welcome! Thanks in advance :)
You might be able to leverage ColumnDefinition.SharedSizeGroup
and the Grid.IsSharedSizeScope
attached property. Try something like this:
<UserControl x:Class="CSharpTest.RowControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="RowLabel"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Label}"/>
<TextBlock Text="{Binding Text}" Grid.Column="1"/>
</Grid>
</UserControl>
And then to use it:
<mycontrols:ContainerControl Grid.IsSharedSizeScope="True">
<mycontrols:RowControl Label="Some label" Text="{Binding SomeProperty}"/>
<mycontrols:RowControl Label="Some label" Text="{Binding SomeProperty}"/>
<mycontrols:RowControl Label="Some label" Text="{Binding SomeProperty}"/>
<mycontrols:RowControl Label="Some label" Text="{Binding SomeProperty}"/>
</mycontrols:ContainerControl>
mycontrols:ContainerControl
could be any type of container (e.g. StackPanel
, UniformGrid
, etc).
In the above, the first column of each RowControl
's Grid
will be uniform (they will all grow to accomidate the largest Label
). Depending on what's desired, you might also need to set SharedSizeGroup
on the second column (to a different value, something like "RowText").