#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:

#433 – Preservation of Margins While Editing Grid in Blend

Assume that you have a simple 2×2 Grid and you use the design surface in Blend to place a Button in the Grid.  If you place the Button by dragging it and dropping it, its Grid.Row and Grid.Column properties will be set accordingly and its Margin will be set to reflect the specific spot where you dropped it.

	<Grid Height="300" Width="300">
		<Grid.RowDefinitions>
			<RowDefinition Height="0.5*"/>
			<RowDefinition Height="0.5*"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="0.5*"/>
			<ColumnDefinition Width="0.5*"/>
		</Grid.ColumnDefinitions>
		<Button Content="Button" Grid.Column="1" Margin="59,48,16,0" Grid.Row="1" VerticalAlignment="Top"/>
	</Grid>

If you now change the width of the 2nd column, making it wider, you’ll notice that the button’s Margin is preserved, which means that the size of the button changes.

		<Button Content="Button" Grid.Column="1" Margin="59,48,16,0" Grid.Row="1" VerticalAlignment="Top"/>

This is normally what you want.  As you change things on the design surface, the XAML does not change but the visual appearance of the controls may change.

#426 – Layout Panels Can Also Have Margins

You typically set margins on elements contained within a layout panel to create space between different elements and to create space between an element and the edge of the containing panel.

Here’s a Window containing a Grid (beige background), which contains some controls.  No margins have been set.

We can then specify a Margin value for the child elements within the Grid.  (Here, we set all margins to 5).  This creates space between each control and the cell in the Grid that it’s contained in.

We can also specify a Margin value for the Grid itself.  Doing so will create space between the Grid and the edges of its container, the Window.  In the example below, we specify a Margin of 10 for the Grid.  The background of the Window is set to blue, so that we can see the edges of the Grid.

 

 

#423 – Setting Maximum Height and Width for Rows and Columns in a Grid

In the same way that you can set minimum height and width of rows and columns in a Grid, you can also set the maximum height or width.

You set the maximum height of a row in a Grid using the MaxHeight property of a RowDefinition element.  You set the maximum width of a column by using the MaxWidth property of a ColumnDefinition element.

    <Grid ShowGridLines="True">
    	<Grid.RowDefinitions>
    		<RowDefinition Height="Auto" />
    		<RowDefinition Height="Auto"/>
    		<RowDefinition Height="Auto"/>
    		<RowDefinition Height="Auto"/>
    	</Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" MaxWidth="20"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*" MaxWidth="80"/>
        </Grid.ColumnDefinitions>


#422 – Setting Minimum Height and Width on Rows and Columns in a Grid

You can set a minimum height for a row in a Grid using the MinHeight property.  You can also set the minimum width of a column using the MinWidth property.

In the example below, we set the minimum height of the first column, even though its Width is also set to Auto.  We also set the minimum of the 4th column, whose width is set to 1* (the same width as the 3rd column).

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

When the application starts, the first column is already wider than its elements, because of its minimum width.  The 4th column is set to the same width as the 3rd, both of which are larger than the 4th column’s minimum width.

As we make the window narrower, the 4th column eventually reaches its minimum width and will not become any narrower.