WPF Grid as ItemsPanel for a list dynamically bound to an ItemsControl

  Kiến thức lập trình

I am using a Grid as ItemsPanel for a list dynamically bound to an ItemsControl. The code below is working – with a remaining problem: I can’t find a way to dynamically initialize the ColumnDefinitions and RowDefinitions of the grid. As consequence all values are placed on top of each other.

<ItemsControl ItemsSource="{Binding Cells}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Grid.Row" Value="{Binding RowIndex}"/>
            <Setter Property="Grid.Column" Value="{Binding ColumnIndex}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Value}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Please be aware, that I am searching an answer according the MVVM pattern. Therefore sub classing and code behind are just workarounds, but no solutions.

4

You’ll need some way to tell the Grid how many Rows/Columns it has. Perhaps as each Item loads you could check the value of RowIndex and ColumnIndex and add Rows/Columns to the Grid if needed.

As another alternative, perhaps you can expose RowCount and ColumnCount properties in your ViewModel that return the max RowIndex and ColumnIndex, and in the Grid’s Loaded event add however many Columns/Rows you need.

I find it perfectly acceptable to use code-behind in MVVM IF the code is related to the UI only.

Another idea would be to arrange your items in your code-behind into a 2D grid before returning it to the View, and then bind that Grid to a DataGrid with AutoGenerateColumns=True and the headers removed

Update

My current solution to this problem is to use a set of AttachedProperties for a Grid that allow you to bind RowCount and ColumnCount properties to a property on the ViewModel

You can find the code for my version of the attached properties on my blog here, and they can be used like this:

<ItemsPanelTemplate>
    <Grid local:GridHelpers.RowCount="{Binding RowCount}"
          local:GridHelpers.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>

3

Your grid has zero rows and columns so everything will be displayed on top of each other. Do something like below and it will work.

<ItemsControl ItemsSource="{Binding Cells}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Grid.Row" Value="{Binding RowIndex}" />
            <Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Value}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

3

i found this from here The ItemsControl

            <ItemsControl  ItemsSource="{Binding VehicleMakes}" >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Columns="3" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Width="300" Height="100"  Click="ButtonOption_Click" Tag="{Binding Name}">
                            <StackPanel Orientation="Vertical">
                                <Image
                                    Initialized="Image_Initialized"
                                    Tag="{Binding ResourseImageName}"
                               Width="116"
                               Height="30"
                               Margin="0,0,0,10" >
                                </Image>
                                <TextBlock Text="{Binding Name}" VerticalAlignment="Bottom"  HorizontalAlignment="Center"/>
                            </StackPanel>
                        </Button>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

LEAVE A COMMENT