网格和堆栈面板之间的 WPF 组合

WPF combination between grid and stackpanel

我目前正在尝试 find/implement WPF 控件,它是 GridStackPanel 的组合。这意味着,我喜欢有两个包含多个项目的列。一个项目是另一个控件(例如,面板中带有标签的单个 LabelTextBox,...)。

如果一个项目被折叠,空的 space 应该被下一个项目填充,这意味着我总是尽可能少地在控件中使用 space (单个项目之间没有间隙).

我附上了两张图片应该是什么样子。

初始:

Item4 已折叠(注意后续项目的移动):

有没有人有想法或经验如何做这样的事情?

您可以使用将 StackPanel 或 DockPanel 用作 ItemTemplate 的 ListView。 这是我使用 Grid 和 DockPannel 在列表中显示对象的可观察集合的示例(简化):

                   <ListView x:Name="lbxDroppedDatas" SelectionMode="Single" AllowDrop="True" BorderThickness="0" Padding="0" Margin="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionChanged="lbxDroppedDatas_SelectionChanged" PreviewKeyDown="lbxDroppedDatas_PreviewKeyDown" PreviewMouseRightButtonDown="lbxDroppedDatas_PreviewMouseRightButtonDown" >
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <DockPanel Grid.Row="0" Width="{Binding Path=ActualWidth, ElementName=Container}" Height="40" Margin="1" LastChildFill="True">
                                        <CheckBox DockPanel.Dock="Left" IsChecked="{Binding IsChecked}" VerticalAlignment="Center" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
                                        <Label Content="{Binding Name}" Foreground="{Binding LineBrush}" FontWeight="Bold" FontSize="12" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="0" MouseEnter="Label_MouseEnter" MouseLeave="Label_MouseLeave"/>
                                        <TextBox DockPanel.Dock="Right" Width="60" MaxLength="14" Text="{Binding CurrentValue, StringFormat=N}" Margin="0,0,33,0" Background="Transparent" Foreground="{Binding LineBrush}" BorderBrush="{Binding LineBrush}" BorderThickness="1" VerticalAlignment="Center" HorizontalAlignment="Right" IsEnabled="False"/>
                                    </DockPanel>
                             </DataTemplate>
                        </ListView.ItemTemplate>
                        <ListView.ItemsPanel>
                            <ItemsPanelTemplate>
                                <WrapPanel Orientation="Vertical" VerticalAlignment="Stretch" Margin="0"/>
                            </ItemsPanelTemplate>
                        </ListView.ItemsPanel>
                    </ListView>

您可以将列表元素的可见性绑定到折叠的或可见的 属性。

我会通过以下方式解决这个问题:

  • 首先创建一个带有两个堆栈面板的网格(网格位于 UserControl 或任何其他占位符中):

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <StackPanel x:Name="LeftPanel" x:FieldModifier="private" Orientation="Vertical" />
        <StackPanel x:Name="RightPanel" x:FieldModifier="private" Grid.Column="1" Orientation="Vertical" />
    </Grid>
    
  • 要控制您的项目,请创建一个项目列表,最好在您的 UserControl 代码中:
    private List<FrameworkElement> m_items = new List<FrameworkElement>();

  • 对于您不想显示的项目,将 Visibility 设置为 HiddenCollapsed

  • 您需要一些方法来 select 可见项目并将它们交替放置在左右面板上。我想出了一些您需要在每次列表更改时调用的快速方法(添加或删除项目,更改项目的可见性):

    public void SortItems()
    {
        this.LeftPanel.Children.RemoveRange(0, this.LeftPanel.Children.Count);
        this.RightPanel.Children.RemoveRange(0, this.RightPanel.Children.Count);
        this.m_items.Where(i => i.Visibility == Visibility.Visible).ToList().ForEach((i) => { (this.LeftPanel.Children.Count == this.RightPanel.Children.Count ? this.LeftPanel : this.RightPanel).Children.Add(i); } );
    }
    

该方法简单地删除堆栈面板的所有子项,然后遍历项目列表,仅选择 Visible 的项目,如果两个面板中的项目数量相同,则向左侧面板添加一个他们和右面板否则。

如果您需要其他方式 select 将以下项目添加到哪个面板(例如 ActualHeight),只需更改 select 面板的条件即可。

如果你想让这个方法更优雅,你可以添加一些事件或依赖属性来自动调用 SortItems。无论如何,这是一个好的开始。

非常感谢您提供所有不同的解决方案。最后我在Sinatr的建议下解决了它。所以我创建了自己的面板并覆盖了 ArrangeOverride:

protected override Size ArrangeOverride(Size finalSize)
    {
        List<UIElement> visibleItems = new List<UIElement>();
        double xColumn1 = 0;
        double xColumn2 = (finalSize.Width / 2) + (WIDTH_COLUMN_SEPERATOR / 2);
        double y = 0;

        double columnWidth = (finalSize.Width - WIDTH_COLUMN_SEPERATOR) / 2;

        for (int i = 0; i < InternalChildren.Count; i++)
        {
            UIElement child = InternalChildren[i];
            if (child.Visibility != Visibility.Collapsed)
            {
                visibleItems.Add(child);
            }
        }

        for (int i = 0; i < visibleItems.Count; i++)
        {
            if (i >= (visibleItems.Count - 1))
            {
                visibleItems[i].Arrange(new Rect(xColumn1, y, columnWidth, visibleItems[i].DesiredSize.Height));
            }
            else
            {
                UIElement leftItem = visibleItems[i];
                UIElement rightItem = visibleItems[i + 1];

                double rowHeight = leftItem.DesiredSize.Height > rightItem.DesiredSize.Height ? leftItem.DesiredSize.Height : rightItem.DesiredSize.Height;

                leftItem.Arrange(new Rect(xColumn1, y, columnWidth, rowHeight));
                rightItem.Arrange(new Rect(xColumn2, y, columnWidth, rowHeight));

                y += rowHeight;

                i++;
            }
        }

        return finalSize;
    }

我还为项目创建了自己的用户控件:

<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Label x:Name="captionLabel" Grid.Row="0" Content="{Binding Caption}"/>
    <ContentPresenter x:Name="inputMask" Grid.Row="1" Content="{Binding InputMask, ElementName=userControlBRAIN2AttributePanelItem}" />
</Grid>

所以我得到以下结果(来自我的测试应用程序):

enter image description here

第 3 项已折叠:

enter image description here

我很惊讶没有人建议使用 UniformGrid

您所要做的就是声明一个 UniformGrid,将 Columns 设置为 2 并添加您的项目。折叠的项目应将其 Visibility 设置为 Collapsed (duh)。

这是一个例子:

<UniformGrid Columns="2"
             VerticalAlignment="Center"
             HorizontalAlignment="Center">

    <TextBlock Text="Item 1 " />
    <TextBlock Text="Item 2 " />
    <TextBlock Text="Item 3 " />
    <TextBlock Text="Item 4 " />
</UniformGrid>

将产生以下结果:

当第二个 TextBlockVisibility 设置为 Collapsed 时,如下所示: