1
votes

I have a viewmodel which contains an ObservableCollection of objects name MyLabel. These objects have 3 properties (content, rowNr, columnNr) which should be bound to the Content, Grid.Rowand Grid.Column attributes respectively.

The reason I defined ItemsControl is because it didnt work inside my grid, that is I couldnt bind rowNr and columnNr since the grids Grid.Column /Grid.Row properties kept overwriting my data.

How can I make this work so that my labels are inside of the grid?

<StackPanel>
    <ItemsControl ItemsSource="{Binding Path=MyLabelList}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding content}" Grid.Column="{Binding columnNr}" Grid.Row="{Binding rowNr}" Style="{StaticResource MyLabel}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
    </Grid>
</StackPanel>
4

4 Answers

2
votes

Try to use a Grid as the ItemsPanel for the ItemsControl:

<ItemsControl ItemsSource="{Binding Path=MyLabelList}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Grid.Column" Value="{Binding columnNr}" />
            <Setter Property="Grid.Row" Value="{Binding rowNr}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Label Content="{Binding content}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
1
votes

A Grid and ItemsControl don't play that well together but you could refer to the following blog post:

Using a Grid as the Panel for an ItemsControl: http://blog.scottlogic.com/2010/11/15/using-a-grid-as-the-panel-for-an-itemscontrol.html

Another option may be to "manually" add the <RowDefinition> and <ColumnDefinition> elements to the Grid dynamically in the view.

1
votes

You have to set the bindings to the ItemContainer programmaticly on GetContainerForItemOverride()

public class GridItemsControl : ItemsControl
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        FrameworkElement fwE = base.GetContainerForItemOverride() as FrameworkElement;
        fwE.SetBinding(Grid.ColumnProperty, "columnNr");
        fwE.SetBinding(Grid.RowProperty, "rowNr");

        return fwE;
    }
}

And in the View like this:

<custom:GridItemsControl>
    <custom:GridItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <!-- Add here -->
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <!-- Add here -->
                </Grid.RowDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </custom:GridItemsControl.ItemsPanel>
</custom:GridItemsControl>

You could also implement an own grid that generates the wanted amount of rows and columns.

This trick is pretty legit.

Additional Information Your ItemTemplate will not be directly added to your ItemsPanel (in your case a Grid) there is allways a ContentPresenter or something like this around, because you are using DataTemplate. So you have to go like this.

To make it more contrihensible you could imagine it like this:

                <Grid> <!-- This is the ItemsPanel -->
                    <ContentPresenter>
                        <!-- This needs the Binding on Grid.Row, but doesn't know shit without my solution -->
                        <!-- This is your ItemContainer that has the DataContext of your ViewModelItem -->
                        <ContentPresenter.ContentTemplate>
                            <DataTemplate>
                                <!-- Here is your ItemTemplate -->
                            </DataTemplate>
                        </ContentPresenter.ContentTemplate>
                    </ContentPresenter>
                </Grid>
1
votes

If you don't want to subclass ItemsControl like @Peter suggested in his answer, you could simply use ItemsControl.ItemContainerStyle to bind Grid.Column and Grid.Row properties:

<ItemsControl ItemsSource="{Binding Path=MyLabelList}">
    <ItemsPanelTemplate>
        <Grid>
            <Grid.ColumnDefinitions>
                <!-- Add here -->
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <!-- Add here -->
            </Grid.RowDefinitions>
        </Grid>
    </ItemsPanelTemplate>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Label Content="{Binding content}" Style="{StaticResource MyLabel}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Grid.Row" Value="{Binding rowNr}" />
            <Setter Property="Grid.Column" Value="{Binding columnNr}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

But, as others already mentioned, column and row definitions will not be added automatically and you'll either need to know it in design-time and add them statically, or you'll need to find some way to add them dynamically in run-time.