#578 – Grid Row and Column Size Use GridLength Object

When you create a Grid in XAML, you typically specify the rows and columns at design-time, along with the height of each row and width of each column.  Recall that there are three ways to set the height of a row or width of a column–explicit size, automatic or evenly distributed using remaining space in the Grid.

    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="140"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

Within the RowDefinition and ColumnDefinition objects, the row height and column width are stored within a GridLength object.  GridLength has the three boolean properties IsAbsolute, IsAuto and IsStar indicating how the height or width is specified and then a Value property that contains the actual value.

 

#470 – Elements that Support RightToLeft Flow

Every FrameworkElement has a FlowDirection property that can be either LeftToRight (the default), or RightToLeft.  For panel elements that lay out a series of child elements, this property indicates in which direction the layout should be done.

The FlowDirection property is typically used for cultures where text flows from right to left, e.g. Arabic.  However, you can use this property whenever a right-to-left layout would make sense.

Below are some examples of elements that can use RightToLeft flow.

A Calendar control.

A DatePicker

A Menu, with MenuItem elements

A ListBox

A TreeView

A ProgressBar

A Grid

A StackPanel with Horizontal orientation

#457 – Use a GridSplitter to Let a User Change Row or Column Size in a Grid

Rows and columns in a Grid don’t normally change their size at runtime, unless the content of the Grid changes.  But you can allow a user to change the size of rows or columns by using a GridSplitter element.

You typically place a GridSplitter in its own row or column.  In the example below, the middle column contains a blue GridSplitter.  At runtime, a user can click and drag on this element to change the relative sizes of columns 0 and 2.

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

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

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

Layout at startup:

User clicks and drags to the left:

Or drags to the right:

#456 – An Element in a Grid Can Span Multiple Rows or Columns

You can have child elements in a grid span multiple rows and/or multiple columns using the the Grid.RowSpan and Grid.ColumnSpan properties.

In the example below, the Image spans two rows and two columns and the TextBlock is located in a single row, but spans two columns.

    <Grid DataContext="{Binding GoodMovie}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Image Source="{Binding Image}" Grid.RowSpan="2" Grid.ColumnSpan="2"
               Width="142" Height="216" Margin="5"
               HorizontalAlignment="Center" VerticalAlignment="Center"/>

        <Label Content="{Binding Title}" FontWeight="Bold" Grid.Row="0" Grid.Column="2"/>
        <Label Content="{Binding Year}" Grid.Row="0" Grid.Column="3"/>

        <TextBlock Text="{Binding Summary}" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2"
                   TextWrapping="Wrap" Margin="8"/>

        <Label Content="Director:" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right"/>
        <Label Content="{Binding Director}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left"/>

        <Label Content="Actor:" Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right"/>
        <Label Content="{Binding ActorLead}" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Left"/>

        <Label Content="Actress:" Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right"/>
        <Label Content="{Binding ActressLead}" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Left"/>
    </Grid>


You can see things a little better by turning on the grid lines.

#452 – Data Binding Elements in a Collection to a Grid, Part V

We saw earlier that to bind Grid.Row and Grid.Column properties on child items in an ItemsControl,  we need to set up the binding on the ContentPresenter elements that contain the individual items.

We can do this by overriding the ItemsControl.PrepareContainerForItemOverride method.  This method is called for each item in the collection and receives both the containing element (a ContentPresenter in our case) and the contained item (a ChessPiece object in our example).  We bind the Grid.Row and Grid.Column properties on the ContentPresenter to the same properties on the contained item.

public class GridBasedItemsControl : ItemsControl
    {
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);

            ContentPresenter cp = element as ContentPresenter;

            if ((cp != null) && (item != null))
            {
                BindingOperations.SetBinding(cp, Grid.RowProperty, new Binding { Source = item, Path = new PropertyPath("Row")});
                BindingOperations.SetBinding(cp, Grid.ColumnProperty, new Binding { Source = item, Path = new PropertyPath("Column") });
            }

        }
    }

Everything now works as expected.

NOTE: Reader Bruno pointed out a much better way to do this, using the ItemContainerStyle property.

#451 – Data Binding Elements in a Collection to a Grid, Part IV

Our first attempt to bind elements in a collection to a Grid didn’t work as expected.  We set the Grid.Row and Grid.Column properties in the data template, but the items did not show up in the correct row and column.

The problem is that when we’re setting the Grid.Row and Grid.Column properties on the Label control in the data template, these Label controls are not direct children of the Grid, so the properties have no effect.

We can look at the visual tree of the ItemsControl to see this.  The direct child elements of the Grid are actually instances of a ContentPresenter.

What we really want to do is set the value of the Grid.Row and Grid.Column properties on the ContentPresenter, rather than the Label.  I’ll show how to do that in the next post.

 

#450 – Data Binding Elements in a Collection to a Grid, part III

We’ve done part of the work required in order to bind a collection of elements to a Grid, with each object’s Row and Column properties indicating where in the Grid it should be located.

  • Set the ItemsPanel property of an ItemsControl to represent a Grid
  • Set the ItemTemplate property to represent the item, setting the Grid.Row and Grid.Column property for each

At this point, the binding doesn’t work as expected.  Each element does get inserted into the grid, but every element is added to Row 0 and Column 0.  The Grid.Row and Grid.Column attached properties are being ignored.

#449 – Data Binding Elements in a Collection to a Grid, part II

This post continues with the goal of binding a collection of elements to a Grid, with each object’s Row and Column properties indicating where in the Grid it should be located.  Last time we set the ItemsPanel property of an ItemsControl to order things in an 8×8 grid.  We also set the ItemTemplate property to include a Label with attached Grid.Row and Grid.Column properties.

In this post, we create a series of ChessPiece elements at runtime, representing a series of chess pieces.

            ChessPieces = new ObservableCollection<ChessPiece>();
            ChessPieces.Add(new ChessPiece("QR-Blk", 1, 1));
            ChessPieces.Add(new ChessPiece("QN-Blk", 1, 2));
            ChessPieces.Add(new ChessPiece("QB-Blk", 1, 3));
            ChessPieces.Add(new ChessPiece("Q-Blk", 1, 4));
            ChessPieces.Add(new ChessPiece("K-Blk", 1, 5));
            ChessPieces.Add(new ChessPiece("KB-Blk", 1, 6));
            ChessPieces.Add(new ChessPiece("KN-Blk", 1, 7));
            ChessPieces.Add(new ChessPiece("KR-Blk", 1, 8));

            ChessPieces.Add(new ChessPiece("P1-Blk", 2, 1));
            ChessPieces.Add(new ChessPiece("P2-Blk", 2, 2));
            ChessPieces.Add(new ChessPiece("P3-Blk", 2, 3));
            ChessPieces.Add(new ChessPiece("P4-Blk", 2, 4));
            ChessPieces.Add(new ChessPiece("P5-Blk", 2, 5));
            ChessPieces.Add(new ChessPiece("P6-Blk", 2, 6));
            ChessPieces.Add(new ChessPiece("P7-Blk", 2, 7));
            ChessPieces.Add(new ChessPiece("P8-Blk", 2, 8));
            OnPropertyChanged("ChessPieces");

#448 – Data Binding Elements in a Collection to a Grid, part I

Assume that you have a collection of objects that you want to place in a grid, with their position only known at runtime.  You might have properties on the objects indicating what row and column they should be placed in and then use data binding to locate the objects.

You can use a Grid panel as the ItemsPanel of an ItemsControl.  This dictates the layout of the items in the collection.  You can then specify the ItemTemplate to indicate how each item should be displayed.

Your first try might look something like this:

    <ItemsControl ItemsSource="{Binding ChessPieces}" Height="500" Width="500">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid ShowGridLines="True">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <!-- 7 more rows -->
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <!-- 7 more columns -->
                    </Grid.ColumnDefinitions>
                </Grid>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding Text}" Grid.Row="{Binding Row}" Grid.Column="{Binding Column}"
                        HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

Next time–create a collection of ChessPiece objects at runtime.

#434 – Canvas Layout Mode in Blend

By default, when you edit a Grid in Blend, margins are preserved when you change the Grid layout, but the visual appearance of controls may change.  This is know as grid layout mode and is indicated by the small icon in the upper left corner of a Grid on the design surface.

You can also switch to canvas layout mode, which allows editing the Grid in a mode that behaves a little more like the Canvas panel.  Controls retain their position and size and their margins are adjusted as Grid elements are resized.

You can click on the icon shown above to switch to canvas layout mode.  In the example below, I switch to canvas layout mode and then change the width of the second column.  The Button’s position and size stay the same and its margins are adjusted.

Margins before the change:

Margins after the change:

Follow

Get every new post delivered to your Inbox.

Join 240 other followers