Search code examples
c#wpfmvvmbindingitemscontrol

Align controls correctly when using ItemsControl in WPF


Can someone tell me how to align and resize controls correctly when using the ItemsControl.

I want to have a description on the left and a TextBox on the right for multiple fields which are defined in an ObservableCollection to end up with something like:

First Name:    [FirstNameTextBox]
Last Name:     [LastNameTextBox]
Date of Birth: [DobTextBox]

but instead I'm getting this:

First Name: [FirstNameTextBox]
Last Name: [LastNameTextBox]
Date of Birth: [DobTextBox]

I want all the textbox to be aligned based on the largest <TextBlock>. If this was done directly in a <Grid> control, it would be straight forward as all controls are directly in the grid and you would just have the following columns definition defined

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition/>
</Grid.ColumnDefinitions>

I thought I could use the SharedSizeGroup property in the <Grid> but it still doesn't resize correctly. Instead it only displays the <TextBlock> stretch across the <Grid>.

Here's my code:

<Grid Grid.IsSharedSizeScope="True" Grid.Row="1">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" SharedSizeGroup="Labels" />
        <ColumnDefinition Width="*" SharedSizeGroup="InputControls" />
    </Grid.ColumnDefinitions>
    <ItemsControl Grid.Row="1" ItemsSource="{Binding SelectedTemplate.Fields}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="Labels"/>
                        <ColumnDefinition SharedSizeGroup="InputControls"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Path=Label}" Grid.Column="0" Margin="5" 
                     VerticalAlignment="Center" Background="Red" />
                    <TextBox Text="{Binding Path=Value}"  Grid.Column="1" Margin="5" 
                     VerticalAlignment="Center" Background="Blue" />
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Any idea how I can resolve this?

Thanks.

UPDATE1: I cannot get this to work as I need it to. This is what I've got so far:

<Grid Grid.Row="1" Background="Purple">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" SharedSizeGroup="Overall" />
    </Grid.ColumnDefinitions>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition SharedSizeGroup="Labels" Width="Auto" />
            <ColumnDefinition SharedSizeGroup="InputControls" Width="*" />
        </Grid.ColumnDefinitions>
        <ItemsControl ItemsSource="{Binding SelectedTemplate.Fields}"                               
                  Background="Yellow" 
                  Grid.IsSharedSizeScope="True">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Background="Green">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition SharedSizeGroup="Labels"/>
                            <ColumnDefinition SharedSizeGroup="InputControls"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="{Binding Path=Label}" 
                               Grid.Column="0" 
                               Margin="5"
                               VerticalAlignment="Center"/>
                        <TextBox Text="{Binding Path=Name}"  
                             Grid.Column="1" 
                             Margin="5"
                             VerticalAlignment="Center"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>

This ends up displaying my layout this way:

enter image description here

As you can see, my TextBox are correctly aligned based on the largest TextBlock but my ItemsControls is not stretched all the way across. I guess that makes sense as it is within the same grid where the ColumnDefinitions are defined.

Now, if I move the ColumnDefinitions' out this grid to the outer grid and remove all instances ofGrid.IsSharedSizeScope`, I guess the following:

enter image description here

Which once again is closer to what I need as my ItemsControl is now stretching all the way as I've set its Grid.ColumnSpan="2" and my TextBox are still aligned to the TextBlock and are stretching all the way across but the problem now is that the TextBlock should be smaller as the Column is set to Auto but they appear to behave as if the column was set to * and I guess I'm losing the purpose of using IsSharedSizeScope since it has been removed.

Now if I add IsSharedSizeScope="True" to the outer grid, I get the following result:

enter image description here

Again, this is close to what I want as my ItemsControl is stretched, my textboxes are also stretch but they are no longer aligned to the largest TextBlock.

Finally, if I add Grid.IsSharedSizeScope="True" to ItemsControl as originally suggested by @mm8,

<Grid Grid.Row="1" Background="Purple" Grid.IsSharedSizeScope="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition SharedSizeGroup="Labels" Width="Auto" />
        <ColumnDefinition SharedSizeGroup="InputControls" Width="*" />
    </Grid.ColumnDefinitions>
    <Grid Grid.ColumnSpan="2" >
        <ItemsControl ItemsSource="{Binding SelectedTemplate.Fields}"                               
                      Background="Yellow" 
                      Grid.IsSharedSizeScope="True">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Background="Green">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition SharedSizeGroup="Labels"/>
                            <ColumnDefinition SharedSizeGroup="InputControls"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="{Binding Path=Label}" 
                               Grid.Column="0" 
                               Margin="5"
                               VerticalAlignment="Center"/>
                        <TextBox Text="{Binding Path=Name}"  
                             Grid.Column="1" 
                             Margin="5"
                             VerticalAlignment="Center"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>

    <!--<TextBlock Text="Invoice Number" Grid.Column="0" Margin="5" VerticalAlignment="Center"/>
        <TextBox Text="InvoiceNumber" Grid.Column="1" Margin="5" VerticalAlignment="Center"/>-->
</Grid>

I get the following:

enter image description here

Which brings me back to square one, though the definitions are different?

I need to achieve the following:

enter image description here

What am I doing wrong??

Thanks.


Solution

  • Try to set the Grid.IsSharedSizeScope property of the ItemsControl itself:

    <ItemsControl Grid.Row="1" ItemsSource="{Binding SelectedBarcodeTemplate.Fields}" 
                  Grid.IsSharedSizeScope="True">
    

    Synchronizing the width of elements in an ItemsControl: https://joshsmithonwpf.wordpress.com/2008/09/06/synchronizing-the-width-of-elements-in-an-itemscontrol/