WPF网格作为ItemsControl的ItemsPanel,动态绑定到列表

22
我正在使用网格(Grid)作为一个列表(ItemsControl)的项(ItemPanel), 并通过数据绑定进行动态绑定。以下代码可以工作 - 但是还存在一个问题: 我找不到一种方法来动态初始化网格(Grid)的列(ColumnDefinitions)和行(RowDefinitions). 因此,所有值都被放置在彼此之上。
<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>

请注意,我正在根据MVVM模式搜索答案。因此,子类化和代码后置仅是解决方法,而不是解决方案。


你需要什么布局?UniformGrid 布局可能适合你吗? - jeffora
@jeffora:我需要至少有不同的列宽。我认为UniformGrid不是一个选项。 - Dirk Brockhaus
你觉得不看太久,UniformGrid可能是更好的选择吗? - user1228
@Will:不是。看看我对jeffora评论的回答。 - Dirk Brockhaus
3个回答

31
您需要一种告诉网格有多少行/列的方法。也许在每个项目加载时,您可以检查 RowIndex ColumnIndex 的值,并在需要时向网格添加行/列。另一个替代方案是,在ViewModel中公开返回最大 RowIndex ColumnIndex RowCount ColumnCount 属性,并在Grid的Loaded事件中添加所需的列/行。
我认为,在MVVM中只要代码与UI相关,使用Code-Behind就很合理。
另一个想法是在Code-Behind中将项目排列成2D网格,然后将其绑定到DataGrid,并使用 AutoGenerateColumns=True 和删除标题。
更新: 我当前解决此问题的方法是使用一组 AttachedProperties 用于 Grid ,允许您将 RowCount ColumnCount 属性绑定到ViewModel中的属性。您可以在我的博客上找到附加属性版本的代码,并且可以像这样使用它们:
<ItemsPanelTemplate>
    <Grid local:GridHelpers.RowCount="{Binding RowCount}"
          local:GridHelpers.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>

非常感谢您的回答。根据您的第二个建议(Grid的Loaded事件),我已经解决了这个问题。我认为您在MVVM代码后台方面的观点是正确的。最初,我搜索了类似于“网格定义的数据绑定”的内容,但是现在WPF似乎不太友好于MVVM。 - Dirk Brockhaus
我希望做同样的事情,只是我想在第0行放置一个TextBlock和在第1行放置另一个TextBlock,你有什么办法让它工作吗? - Didier A.
1
@didibus 如果您想在每个记录中向ItemsPanel(在本例中为Grid)添加两个单独的项,并且每个记录都包装在单个ContentPresenter中,则无法轻松使用ItemsControl完成此操作。 您应该将ItemTemplate制作成自己的Grid,其中有2个单元格(如果需要,可以使用SharedSizeScope使它们大小相同),或者您应该使用不同于ItemsControl的控件来显示您的项,例如DataGrid。 如果您遇到困难,请在SO上发布一个带有相关代码片段的问题,我很乐意帮助您 :) - Rachel

15

你的网格没有行和列,因此所有内容都会叠在一起显示。请按照以下方式进行操作即可正常显示。

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

4
我知道这一点,但是我的网格具有动态增长的列数和行数。我不能使用像上面那样的静态方案。 - Dirk Brockhaus
@华尔街程序员: <ItemsControl.ItemContainerStyle...> 正是我一直在寻找的。非常感谢! - Tobias Koller
ItemContainerStyle 来拯救! - Justin Grahn

3
我从这里找到了这个 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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接