#1,052 – Making Two Columns in a Grid the Same Size

You can force two columns (or rows) in a Grid to always be the same size using the SharedSizeGroup property when defining the columns (or rows).

Below, we specify that the third column should auto-size and that the first and third columns should be the same size.  The first column will be sized to match the auto-sized third column.  We do the following:

  • Set Grid.IsSharedSizeScope property of the Grid to true
  • Set SharedSizeGroup property in the first and third ColumnDefinition elements to the same value (in this case it’s “A”)
        <Grid Name="myGrid" Grid.IsSharedSizeScope="True">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="A"/>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" SharedSizeGroup="A"/>
            </Grid.ColumnDefinitions>

            <Label Content="0,0" Background="Azure" />
            <Label Content="1,0" Background="Moccasin" Grid.Row="1" />
            <Label Content="Row 0 Col 2" Background="Lavender" Grid.Column="1" />
            <Label Content="Row 1 Col 2" Background="Honeydew" Grid.Row="1" Grid.Column="1" />
            <Label Content="Row 0 Col 3" Background="LightSalmon" Grid.Column="2" />
            <Label Content="Row 1 Col 3" Background="GreenYellow" Grid.Row="1" Grid.Column="2" />
        </Grid>

1052-001

#1,051 – How the GridSplitter Behaves when Cells Use Auto Sizing

If one or both columns on either size of a GridSplitter (in its own column) have a width set to Auto, the behavior is as follows (“proportional” refers to star sizing):

  • Both columns set to Auto: Left column changes size and is switched to Absolute sizing
  • Left column Auto, right column proportional: Both columns change size, left switches to Absolute, right remains at original proportion (e.g. “1*”)
  • Right column Auto, left column proportional: Both columns change size, right switches to Absolute, left remains at original proportion

In the example below, columns 0 and 2 surround a GridSplitter in column 1.  Columns 0 and 2 are set to Auto size (as is the column with the GridSplitter) and column 3 is set to proportional.

Initially, we see that both columns 0 and 2 are auto-sized.
1051-001

If we slide the GridSplitter to the right, column 0 changes size and switches to Absolute sizing.  Column 2 remains as Auto.

1051-002

#1,050 – How the GridSplitter Behaves when Cells Use Absolute Sizing

Recall that a GridSplitter in its own column and with its HorizontalAlignment set to Center will resize columns on either side of it.  In the example below, columns 0 and 2 are resized, but the width of column 3 is not changed.

1049-002

If one of the columns whose size is not changing as a result of using the GridSplitter has its size set to an absolute value, it will retain that value.  In the example below column 3 (far right) has an absolute width of 100.  The two columns whose size is changing are using star sizing and have had their relative sizes adjusted.

1050-001

If one of the columns being resized with the GridSplitter has an absolute size, moving the GridSplitter will change its size, but it will continue to have an absolute size.

Before resize:

1050-002

 

After resize:

1050-003

We can see that column 0 has an absolute size if we make the entire Grid wider.

1050-004

#1,049 – How the GridSplitter Behaves when Cells Use Star Sizing

Recall that a GridSplitter in its own column and with its HorizontalAlignment set to Center will resize columns on either side of it.  In the example below, columns 0 and 2 are resized, but the width of column 3 is not changed.

1049-001

1049-002

If all of the content columns are sized using star sizing, the GridSplitter will leave them as star sized, but change the coefficients to match the final size after moving the GridSplitter.  We can see this by dumping out the column widths before and after our sizing operation.

Before resizing, columns 0, 2 and 3 are all “1*” (or just “*”).  They take up equal space.

1049-003

After resizing, the coefficients change.  Columns are still proportionally sized, but the proportions are different.

1049-004

We can verify that the columns are sized proportionally by resizing the entire Grid (containing window).  The columns retain their post-GridSplitter relative sizes.

1049-005

#1,048 – How to Set a GridSplitter’s Alignment Properties

The most common way to use a GridSplitter is to place it in its own auto-sized row or column.  In the example below, we want a vertical GridSplitter, so we place it in its own auto-sized column.

        <Grid Name="myGrid">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

            <Label Content="Row 0 Col 0" Background="Azure" Grid.Row="0" Grid.Column="0" />
            <!-- more here -->

            <GridSplitter Grid.Column="1" Grid.RowSpan="2" Width="3" Background="Blue"
                      VerticalAlignment="Stretch" HorizontalAlignment="Center" />
        </Grid>

When a vertical GridSplitter is in its own column, you’ll want to set its VerticalAlignment to Stretch and its HorizontalAlignment to Center.  (A horizontal GridSplitter would reverse this).

With the HorizontalAlignment set to Center, the column containing the GridSplitter does not itself resize.  Moving the GridSplitter resizes the two columns on either side of it, which is what we want.

1048-001

1048-002

1048-003

#1,047 – Setting the Size of a Grid’s Rows or Columns from Code

There are three ways to set the size of a row or a column in a Grid:

  • Auto – set height/width to fit the size of the contained element
  • numeric value – explicitly set height/width to specified number of device independent units (1/96th in)
  • * or n* – distribute remaining space proportionately

You can set row or column sizes from code for all three of these methods.

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // Set to Auto
            myGrid.RowDefinitions[0].Height = GridLength.Auto;

            // Set explicit size
            myGrid.RowDefinitions[1].Height = new GridLength(35.0, GridUnitType.Pixel);

            // Set star sizing (proportional)
            myGrid.RowDefinitions[2].Height = new GridLength(1.0, GridUnitType.Star);
            myGrid.RowDefinitions[3].Height = new GridLength(2.0, GridUnitType.Star);
        }

Before clicking the button, the row heights default to “*” (evenly spaced):
1047-001
After executing the code shown above, the Grid looks like this:
1047-002

#1,046 – Setting the Background Color of a Cell in a Grid

There are several different ways to set the background color of a cell within a Grid.  They include:

  • Setting the Background property of the element within a cell (e.g. a Label)
  • Including a Rectangle in the same cell as another element and setting its Fill property
  • Using a Border element and setting its Background property
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Content="Larry" Margin="5" Background="Lavender"/>
        <Label Grid.Row="0" Grid.Column="1" Content="Moe" Margin="5" />

        <Rectangle Grid.Row="1" Grid.Column="0" Fill="Bisque"/>
        <CheckBox Grid.Row="1" Grid.Column="0" Margin="5" Content="Nose Tweak"
                  VerticalAlignment="Center"/>

        <Border Grid.Row="1" Grid.Column="1"  Background="MediumSpringGreen"
                CornerRadius="5" Margin="5">
            <Button Grid.Row="1" Grid.Column="1" Margin="5" Content="Do Eye Poke"
                    VerticalAlignment="Center"/>
        </Border>
    </Grid>

1046-001
 

#1,045 – Control Layering of Elements in a Grid Using ZIndex

If you have more than one user interface element in a particular grid cell, the elements are layered on top of each other.  Elements added earlier in XAML appear below (lower layer) than elements added later.

You can change the layering of the elements by changing the order in which they are added to the Grid.  You can also control the layering by giving each element a value for Panel.ZIndex.  Elements with higher values for ZIndex will appear to be on top of elements with lower values, regardless of the order in which they were added to the Grid.

In the example below, the Label appears on top of the Button because it has a higher value for ZIndex.

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

        <Label Content="Oops I'm in the same cell with a button !"
               Panel.ZIndex="2"/>
        <Button Content="Push Me" Width="100"
                Panel.ZIndex="1"/>
    </Grid>

1045-001

#1,044 – Placing More than One Element in a Grid Cell

There is nothing to prevent you from placing more than one element within a given cell in a Grid.  In the example below, the Button and the Label share the same cell.

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

        <Button Content="Push Me" Width="100" />
        <Label Content="Oops I'm in the same cell with a button !" />
    </Grid>

1044-001

By default, controls that share the same Grid cell are layered in the order in which they are added to the Grid.  Elements added earlier appear below elements added later.

If we reverse the order of the Button and Label elements in XAML, listing the Label first, it will appear under the Button.

1044-002

#1,043 – Using a DockPanel as the Items Panel for a ListBox

You can replace the default StackPanel used as the items panel for a ListBox with any other panel element.  Below is an example of displaying some news stories in a DockPanel.

Assume that we have a NewsStory class as follow:

    public class NewsStory
    {
        public string Story { get; set; }
        public Brush Color { get; set; }
        public Dock Dock { get; set; }
        public double Rotate { get; set; }

        public NewsStory(string story, Color color, Dock dock, double rotate)
        {
            Story = story;
            Color = new SolidColorBrush(color);
            Dock = dock;
            Rotate = rotate;
        }

        public override string ToString()
        {
            return Story;
        }
    }

We can then create a collection of NewsStory items that we’ll bind to.  Notice that we create a spiral pattern by setting consecutive Dock properties to Bottom/Left/Top/Right.  We also use the Angle property to rotate every other element.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            Stories = new ObservableCollection<NewsStory>
            {
                new NewsStory("Diaper Market Bottoms Out", Colors.AliceBlue, Dock.Bottom, 0.0),
                new NewsStory("Antique Stripper to Display Wares", Colors.AntiqueWhite, Dock.Left, -90.0),
                new NewsStory("Cancer Society Honors Marlboro Man", Colors.Aqua, Dock.Top, 0.0),
                new NewsStory("War Dims Hope for Peace", Colors.Aquamarine, Dock.Right, -90.0)
                // more entries go here..
            };
            RaisePropertyChanged("Stories");

        }

        public ObservableCollection<NewsStory> Stories { get; protected set; }

        // INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private void RaisePropertyChanged(string propName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }

In XAML, we set the ItemContainerStyle to do the docking and specify a DockPanel as the ItemsPanel.

        <ListBox ItemsSource="{Binding Stories}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="DockPanel.Dock" Value="{Binding Dock}"/>
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <DockPanel IsItemsHost="True"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Story}"
                           Background="{Binding Color}">
                        <Label.LayoutTransform>
                            <RotateTransform Angle="{Binding Rotate}"/>
                        </Label.LayoutTransform>
                    </Label>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Here’s what the end result looks like:

1044-001