如何设置垂直 WPF WrapPanel 以使用尽可能多的列

问题描述 投票:0回答:3

我有一个 ItemsControl,其中 WrapPanel 作为 ItemsPanel。 ItemsControl 位于 ScrollViewer 内部。

根据窗口的宽度,ItemsControl/WrapPanel 应具有更多列以更多地利用屏幕(并且用户必须更少滚动)。

如果我将 WrapPanel 设置为 Orientation="Horizontal",它会按预期工作。但如果我将方向设置为“垂直”,则 ItemsControl/WrapPanel 仅显示一列。

不同的是,我喜欢像报纸专栏一样的专栏,所以像:

A   E   I
B   F   J
C   G
D   H

但是如果 WrapPanel 设置为水平,则列如下:

A   B   C
D   E   F
G   H   I
J

如何设置 WrapPanel 使其具有这样的行为?

我当然可以限制高度。当然,我可以通过一些巧妙的逻辑来实现这一点,即考虑源中的项目数量以及窗口的宽度。但这会很复杂,而且可能一团糟,没有人会理解(甚至我自己,如果我几年后回顾这一点)。

我希望 WPF 中已经存在这样的东西。


这是伪 XAML 代码

<ScrollViewer>
    <StackPanel>

        <!-- Some other stuff -->

        <ItemsControl ItemsSource="{Binding … }">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Width="300"><!-- … --></Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <!-- Some more other stuff -->

    </StackPanel>
</ScrollViewer>

它适用于 Orientation="Horizontal"(但单元格顺序错误),但当 Orientation="Vertical" 时仅显示一列。

wpf orientation wrappanel
3个回答
0
投票

我相信

Orientation="Vertical"
是您正在寻找的选择。它将在换行到新列之前尝试填充列。

A __D __G
| | | | | 
B | E | .
| | | | .
C_| F_| .->

您遇到的麻烦是

WrapPanel
StackPanel
允许无限量的垂直布局空间,因此不必换行。

要解决此问题,请将

StackPanel
更改为
WrapPanel
或其他不允许无限布局空间的控件。

示例

<WrapPanel>
    <ItemsControl>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Vertical"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <Rectangle Height="50" Width="50" Fill="#F000"/>
        <Rectangle Height="50" Width="50" Fill="#F111"/>
        <Rectangle Height="50" Width="50" Fill="#F222"/>
        <!- ...  ->
        <Rectangle Height="50" Width="50" Fill="#F111"/>
        <Rectangle Height="50" Width="50" Fill="#F000"/>
    </ItemsControl>    
</WrapPanel>

结果


0
投票

好吧,我想出了一个解决方案,那就是纯 XAML。

所以基本上我有一个网格。有两排。网格内部有两个几乎相同的 ItemsControl,带有绑定和 WrapPanel。 第一个有一个 Orientation=“Horizontal”的 WrapPanel。它仅用于计算第二个 ItemsControl 的边界。因此它的可见性=“隐藏”。而且它只跨越第一行。

第二个 ItemsControl 具有 Orientation=“Vertical”的 WrapPanel。它的高度绑定到第一个 ItemsControl 的 ActualHeight。因此,它被迫使用多个列,因为高度不能高于 ItemsControl #1 的高度。 第二个 ItemsControl 跨越两行(这很重要,否则高度可能会卡住,调整窗口大小*)。

这不是一个很好的解决方案。更像是蛮力。但它当然有效。第一个 ItemsControl 计算外部尺寸。我将此尺寸强制为第二个,这样可以以良好的模式分布项目。

<ScrollViewer>
    <StackPanel>

        <!-- Some other stuff -->

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <!-- first ItemsControl, to calculate the outer boundaries with --> 
            <ItemsControl ItemsSource="{Binding … }" 
                          Name="Calculating_Size"
                          Visibility="Hidden">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Horizontal" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Width="300"><!-- … --></Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

            <!-- Second ItemsControl with fixed size for Vertical WrapPanel --> 
            <ItemsControl ItemsSource="{Binding … }" 
                          Height="{Binding ActualHeight, ElementName=Calculating_Size}"
                          Grid.RowSpan="2">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Vertical" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Width="300"><!-- … --></Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

        <!-- Some more other stuff -->

    </StackPanel>
</ScrollViewer>



* 第二行的原因是,如果第一个 WrapPanel 重新组织,因为宽度增加,它仍然与高度固定,因为行不能变小,第二个 ItemsControl.但是,如果第二个 ItemsControl 跨越两行,则第一行可能会变得更小,而与第二个 ItemsControl 无关。第二个之后立即变小。但这似乎是一个额外的步骤。


0
投票

对我来说,答案是通过将 WrapPanel 绑定到父 ScrollContentPresenter 来限制其高度

    <ListBox>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Vertical"
                       Height="{Binding (FrameworkElement.ActualHeight), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}">
                </WrapPanel>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
© www.soinside.com 2019 - 2024. All rights reserved.