WPF Dynamic Layout with ItemsControl and Grid

20,175

You're on the right track. You just need to create a Style and apply it to the ItemsControl.ItemContainerStyle. Then, in the style, specify a setter for the Grid.Row and Grid.Column. The ItemContainerStyle will get applied to the containers generated for each item.

Share:
20,175
Jason Williams
Author by

Jason Williams

A developer specializing in .NET technologies.

Updated on July 28, 2020

Comments

  • Jason Williams
    Jason Williams almost 4 years

    I am creating a WPF form. One of the requirements is that it have a sector-based layout so that a control can be explicitly placed in one of the sectors/cells.

    I have created a tic-tac-toe example below to convey my problem:

    There are two types and one base type:

    public class XMoveViewModel : MoveViewModel
    {
    }
    public class OMoveViewModel : MoveViewModel
    {
    }
    public class MoveViewModel
    {
        public int Row { get; set; }
        public int Column { get; set; }
    }
    

    The DataContext of the form is set to an instance of:

    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            Moves = new ObservableCollection<MoveViewModel>()
            {
                new XMoveViewModel() { Row = 0, Column = 0 },
                new OMoveViewModel() { Row = 1, Column = 0 },
                new XMoveViewModel() { Row = 1, Column = 1 },
                new OMoveViewModel() { Row = 0, Column = 2 },
                new XMoveViewModel() { Row = 2, Column = 2}
            };
        }
        public ObservableCollection<MoveViewModel> Moves
        {
            get;
            set;
        }
    }
    

    And finally, the XAML looks like this:

     <Window.Resources>
        <DataTemplate DataType="{x:Type vm:XMoveViewModel}">
            <Image Source="XMove.png" Grid.Row="{Binding Path=Row}" Grid.Column="{Binding Path=Column}" Stretch="None" />
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:OMoveViewModel}">
            <Image Source="OMove.png" Grid.Row="{Binding Path=Row}" Grid.Column="{Binding Path=Column}" Stretch="None" />
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding Path=Moves}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid ShowGridLines="True">
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
    

    What was not so obvious to me when I started was that the ItemsControl element actually wraps each item in a container, so my Grid.Row and Grid.Column bindings are ignored since the images are not directly contained within the grid. Thus, all of the images are placed in the default Row and Column (0, 0).

    What is happening:

    What is happening

    The desired result:

    The desired result

    So, my question is this: how can I achieve the dynamic placement of my controls in a grid? I would prefer a XAML/Data Binding/MVVM-friendly solution.

    Thanks.

    I have put the final code here: http://www.centrolutions.com/downloads/TicTacToe.zip

  • Jason Williams
    Jason Williams about 14 years
    Aha! I knew I was missing something. Thank you for your help. That worked great.
  • Emil Badh
    Emil Badh over 13 years
    I'm trying to do the exact same thing in Silverlight, does anyone know a way to do that? (ItemContainerStyle is not available in Silverlight...)