Wpf Listbox—满足最大行数之前的内容大小

Wpf Listbox—满足最大行数之前的内容大小,wpf,listbox,panel,Wpf,Listbox,Panel,我希望有一个列表框,其大小与其内容相符,直到满足某些MaxRow属性。因此,如果MaxRow值为3,则其行为如下 Items.Count==0->SizeToContent Items.Count==1->SizeToContent Items.Count==2->SizeToContent Items.Count==3->SizeToContent Items.Count==4->将高度限制为3行并启用滚动条 Items.Count==5->将高度限制为3行并启用滚动条 等等 我认为正确的方法

我希望有一个列表框,其大小与其内容相符,直到满足某些MaxRow属性。因此,如果MaxRow值为3,则其行为如下

Items.Count==0->SizeToContent

Items.Count==1->SizeToContent

Items.Count==2->SizeToContent

Items.Count==3->SizeToContent

Items.Count==4->将高度限制为3行并启用滚动条

Items.Count==5->将高度限制为3行并启用滚动条 等等

我认为正确的方法是使用自定义面板(如下所示),但这似乎不起作用

我怎样才能做到这一点

<ListBox ItemsSource="{Binding Items}"
         HorizontalContentAlignment="Stretch">
        <ListBox.Template>
            <ControlTemplate>
                <ScrollViewer VerticalScrollBarVisibility="Auto">
                    <l:LimitingStackPanel VerticalAlignment="Top"
                                          IsItemsHost="True"/>
                </ScrollViewer>
            </ControlTemplate>
        </ListBox.Template>
    </ListBox>


public class LimitingStackPanel : Panel
{
    protected override Size MeasureOverride(Size availableSize)
    {
        Size measuredSize = new Size(0, 0);
        int count = 0;
        foreach(UIElement item in InternalChildren)
        {
            item.Measure(availableSize);
            measuredSize.Width = Math.Max(measuredSize.Width, item.DesiredSize.Width);

            if(++count <= 4)
            {
                measuredSize.Height += item.DesiredSize.Height;
            }
        }

        return measuredSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        double y = 0;
        foreach (UIElement item in InternalChildren)
        {
            double height = item.DesiredSize.Height;
            item.Arrange(new Rect(0, y, finalSize.Width, height));
            y += height;
        }

        return new Size(finalSize.Width, y);
    }
}

公共类限制StackPanel:面板
{
受保护的覆盖尺寸测量覆盖(尺寸可用尺寸)
{
尺寸测量尺寸=新尺寸(0,0);
整数计数=0;
foreach(InternalChildren中的UIElement项)
{
项目.措施(可用性);
measuredSize.Width=Math.Max(measuredSize.Width,item.DesiredSize.Width);

如果(++count我试图通过附加属性来解决它,因此将其作为行为

样本xaml

<StackPanel xmlns:l="clr-namespace:CSharpWPF">
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
    </ListBox>
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
        <ListBoxItem>item 2</ListBoxItem>
    </ListBox>
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
        <ListBoxItem>item 2</ListBoxItem>
        <ListBoxItem>item 3</ListBoxItem>
    </ListBox>
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
        <ListBoxItem>item 2</ListBoxItem>
        <ListBoxItem>item 3</ListBoxItem>
        <ListBoxItem>item 4</ListBoxItem>
    </ListBox>
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
        <ListBoxItem>item 2</ListBoxItem>
        <ListBoxItem>item 3</ListBoxItem>
        <ListBoxItem>item 4</ListBoxItem>
        <ListBoxItem>item 5</ListBoxItem>
    </ListBox>
</StackPanel>
结果是一个自动高度列表框,最多可显示指定数量的项目

此解决方案假定所有项目的大小都相同。最后,对于这个问题,它不是一个完美的解决方案,我可以说它是一个解决方法。还值得注意的是,它只能在运行时重新调整大小,而在设计时它是无效的

尝试一下,看看这与您的需求有多接近


如果您事先知道项目高度,那么更好的解决方案是使用
MaxHeight
属性

我试图通过附加属性来解决它,因此将其作为行为

样本xaml

<StackPanel xmlns:l="clr-namespace:CSharpWPF">
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
    </ListBox>
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
        <ListBoxItem>item 2</ListBoxItem>
    </ListBox>
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
        <ListBoxItem>item 2</ListBoxItem>
        <ListBoxItem>item 3</ListBoxItem>
    </ListBox>
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
        <ListBoxItem>item 2</ListBoxItem>
        <ListBoxItem>item 3</ListBoxItem>
        <ListBoxItem>item 4</ListBoxItem>
    </ListBox>
    <ListBox l:ListBoxHelper.AutoSizeItemCount="3">
        <ListBoxItem>item 1</ListBoxItem>
        <ListBoxItem>item 2</ListBoxItem>
        <ListBoxItem>item 3</ListBoxItem>
        <ListBoxItem>item 4</ListBoxItem>
        <ListBoxItem>item 5</ListBoxItem>
    </ListBox>
</StackPanel>
结果是一个自动高度列表框,最多可显示指定数量的项目

此解决方案假定所有项目的大小都相同。最后,对于这个问题,它不是一个完美的解决方案,我可以说它是一个解决方法。还值得注意的是,它只能在运行时重新调整大小,而在设计时它是无效的

尝试一下,看看这与您的需求有多接近


如果您提前知道项目高度,那么更好的解决方案是使用
MaxHeight
属性

您希望限制ListBox或底层LimitingStackPanel的大小?无论哪一个解决问题的方法最好,让我们看看是否有合适的解决方案。在开始之前,请告诉我如果内容大小不同?它应该限制为3个完整的项目,对吗?滚动行为如何?逐项滚动?如果元素大小不同,是否允许列表框的大小在滚动时改变?我认为只有假设所有项目始终具有相同的高度,这个想法才会行得通。它还需要与可视化一起工作,所以我猜它是w我必须按项目滚动。我已经用一张图片更新了这个问题,显示了我正在努力实现的目标。您希望限制ListBox或底层LimitingStackPanel的大小?无论哪一个最好的解决方案似乎都非常棘手,让我们看看是否有一个合适的解决方案。在我开始之前,告诉我如果内容大小不同,该怎么办?这很简单你可以限制为3个完整的项目,对吗?滚动的行为如何?逐项滚动?如果元素大小不同,是否允许列表框的大小在滚动时发生变化?我认为只有假设所有项目始终具有相同的高度,这个想法才会行得通。它还需要与可视化一起工作,所以我猜它将不得不改变oll by item。我已经用一个图片更新了问题,显示了我试图实现的目标。我稍微修改了它,以便更好地处理大型集合(请参见我的编辑),谢谢!。我稍微修改了它,以便更好地处理大型集合(请参见我的编辑)
namespace CSharpWPF
{
    public class ListBoxHelper : DependencyObject
    {
        public static int GetAutoSizeItemCount(DependencyObject obj)
        {
            return (int)obj.GetValue(AutoSizeItemCountProperty);
        }

        public static void SetAutoSizeItemCount(DependencyObject obj, int value)
        {
            obj.SetValue(AutoSizeItemCountProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoSizeItemCount.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoSizeItemCountProperty =
            DependencyProperty.RegisterAttached("AutoSizeItemCount", typeof(int), typeof(ListBoxHelper), new PropertyMetadata(0, OnAutoSizeItemCountChanged));

        private static void OnAutoSizeItemCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ListBox listBox = d as ListBox;
            listBox.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler((lb, arg) => UpdateSize(listBox)));
            listBox.ItemContainerGenerator.ItemsChanged += (ig, arg) => UpdateSize(listBox);
        }

        static void UpdateSize(ListBox listBox)
        {
            ItemContainerGenerator gen = listBox.ItemContainerGenerator;
            FrameworkElement element = listBox.InputHitTest(new Point(listBox.Padding.Left + 5, listBox.Padding.Top + 5)) as FrameworkElement;
            if (element != null && gen != null)
            {
                object item = element.DataContext;
                if (item != null)
                {
                    FrameworkElement container = gen.ContainerFromItem(item) as FrameworkElement;
                    if (container == null)
                    {
                        container = element;
                    }
                    int maxCount = GetAutoSizeItemCount(listBox);
                    double newHeight = Math.Min(maxCount, gen.Items.Count) * container.ActualHeight;
                    newHeight += listBox.Padding.Top + listBox.Padding.Bottom + listBox.BorderThickness.Top + listBox.BorderThickness.Bottom + 2;
                    if (listBox.ActualHeight != newHeight)
                        listBox.Height = newHeight;
                }
            }
        }
    }
}