35

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

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Jason Williams
  • 1,283
  • 2
  • 11
  • 31
  • Hey Jason, Any chance to get the full code of your tic-tac-toe? I'd love to take a look. Thanks! – VitalyB Sep 01 '10 at 19:05
  • 2
    Unfortunately, the project is just an example and not a fully functioning game. This was a prototype for a larger (non-tic-tac-toe) project. If you're still interested, I've put a link to the final code below the original question. – Jason Williams Sep 02 '10 at 13:37
  • 2
    This is a great question, and great answer -- and thanks so much for posting the link to the final code! It really helped me a lot. – Guy Starbuck Sep 03 '10 at 00:56
  • The source code is a great example of MVVM. Really pointed me in the right direction in a hurry! – eselk Aug 30 '13 at 22:27
  • Really appreciate, you pointed out the ItemsControl.ItemContainerStyle which allow to configure row & column that solved my problem as well! I create a UserControl with a 10x10 circle to indicate the table layout for a cafe application. – yancyn Nov 07 '13 at 14:54

1 Answers1

29

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.

Charlie
  • 15,069
  • 3
  • 64
  • 70