WPF列表框的高度是否可以设置为其项目高度的倍数?

WPF列表框的高度是否可以设置为其项目高度的倍数?,wpf,listbox,Wpf,Listbox,是否有某种方法可以将WPF multi-select列表框的Height属性设置为项目高度的倍数,类似于设置html select元素的size属性 我有一个业务要求,如果是带有滚动条的长列表,则列表底部不显示半个项目,如果是显示所有项目的短列表,则列表底部不显示额外的空白,但我能找到的唯一方法是不断调整高度,直到看起来合适为止 我还试过什么?我询问了同事们,搜索了MSDN和StackOverflow,做了一些谷歌搜索,并在编辑代码时查看了VS Intellisense提供的功能。关于如何设置高

是否有某种方法可以将WPF multi-select列表框的Height属性设置为项目高度的倍数,类似于设置html select元素的size属性

我有一个业务要求,如果是带有滚动条的长列表,则列表底部不显示半个项目,如果是显示所有项目的短列表,则列表底部不显示额外的空白,但我能找到的唯一方法是不断调整高度,直到看起来合适为止


我还试过什么?我询问了同事们,搜索了MSDN和StackOverflow,做了一些谷歌搜索,并在编辑代码时查看了VS Intellisense提供的功能。关于如何设置高度以适合ListBox的容器,有很多建议,但这与我试图做的恰恰相反。

是的,可以想象有一种更简单的方法来实现这一点,即使用单个SnapToHoleElement属性。我也找不到这个地方

为了达到你的要求,我写了一点逻辑。基本上,在我的Windows对象中有一个公共属性lbHeight,它通过计算每个项目的高度来计算列表框的高度

首先,让我们来看看XAML:

<Window
    x:Class="SO.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="120" SizeToContent="Height"
    Title="SO Sample"    
    >
    <StackPanel>
        <ListBox x:Name="x_list" Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=lbHeight}" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border x:Name="x" Background="Gray" Margin="4" Padding="3">
                        <TextBlock Text="{Binding}" />
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>        
</Window>
接下来,我将实现一个findVisualItem——查找数据模板的根元素。我把这个函数做了一点泛型,所以它得到了一个谓词p,它标识这是否是我想要找到的元素:

private DependencyObject findVisualItem( DependencyObject el, Predicate<DependencyObject> p )
{
    DependencyObject found = null;

    if( p(el) ) {
        found = el;
    }
    else {
        int count = VisualTreeHelper.GetChildrenCount( el );
        for( int i=0; i<count; ++i ) {
            DependencyObject c = VisualTreeHelper.GetChild( el, i );
            found = findVisualItem( c, p );
            if( found != null )
                break;
        }
    }
    return found;
}
最后,lbHeight属性:

public double lbHeight
{
    get {
        FrameworkElement item = findVisualItem( 
            x_list,
            el => { return ( el is Border ) ? ( (FrameworkElement)el ).Name == "x" : false; }
            ) as FrameworkElement;
        if( item != null ) {
            double h = item.ActualHeight + item.Margin.Top + item.Margin.Bottom;
            return h * 12;
        }
        else {
            return 120;
        }
    }
}
我还使窗口实现INotifyPropertyChanged,当列表框的项被加载到ListBox的loaded事件时,我为“lbHeight”属性触发了PropertyChanged事件。在某些情况下,这是必要的,但在结束时,WPF获取了lbHeight属性,而我已经有了一个渲染项


您的项目可能高度不同,在这种情况下,您必须将VirtualizedStackPanel中的所有项目相加。如果你有一个水平滚动条,你必须考虑它的总高度当然。但这是总体思路。您发布问题才3个小时,我希望有人能给出一个更简单的答案。

是的,可以想象有一种更简单的方法来实现这一点—一个SnapToHoleElement属性。我也找不到这个地方

为了达到你的要求,我写了一点逻辑。基本上,在我的Windows对象中有一个公共属性lbHeight,它通过计算每个项目的高度来计算列表框的高度

首先,让我们来看看XAML:

<Window
    x:Class="SO.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="120" SizeToContent="Height"
    Title="SO Sample"    
    >
    <StackPanel>
        <ListBox x:Name="x_list" Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=lbHeight}" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border x:Name="x" Background="Gray" Margin="4" Padding="3">
                        <TextBlock Text="{Binding}" />
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>        
</Window>
接下来,我将实现一个findVisualItem——查找数据模板的根元素。我把这个函数做了一点泛型,所以它得到了一个谓词p,它标识这是否是我想要找到的元素:

private DependencyObject findVisualItem( DependencyObject el, Predicate<DependencyObject> p )
{
    DependencyObject found = null;

    if( p(el) ) {
        found = el;
    }
    else {
        int count = VisualTreeHelper.GetChildrenCount( el );
        for( int i=0; i<count; ++i ) {
            DependencyObject c = VisualTreeHelper.GetChild( el, i );
            found = findVisualItem( c, p );
            if( found != null )
                break;
        }
    }
    return found;
}
最后,lbHeight属性:

public double lbHeight
{
    get {
        FrameworkElement item = findVisualItem( 
            x_list,
            el => { return ( el is Border ) ? ( (FrameworkElement)el ).Name == "x" : false; }
            ) as FrameworkElement;
        if( item != null ) {
            double h = item.ActualHeight + item.Margin.Top + item.Margin.Bottom;
            return h * 12;
        }
        else {
            return 120;
        }
    }
}
我还使窗口实现INotifyPropertyChanged,当列表框的项被加载到ListBox的loaded事件时,我为“lbHeight”属性触发了PropertyChanged事件。在某些情况下,这是必要的,但在结束时,WPF获取了lbHeight属性,而我已经有了一个渲染项


您的项目可能高度不同,在这种情况下,您必须将VirtualizedStackPanel中的所有项目相加。如果你有一个水平滚动条,你必须考虑它的总高度当然。但这是总体思路。您发布问题才3个小时-我希望有人能给出更简单的答案。

这是通过将父控件的“高度”属性设置为“自动”来完成的,而无需将列表框本身的任何大小设置为“自动”。
要限制列表大小,还应指定MaxHeight属性,方法是将父控件的“高度”属性设置为“自动”,而不将列表框本身的大小设置为“自动”。
要限制列表大小,还应指定MaxHeight属性

谢谢您的回答。不幸的是,我已经离开了使用WPF的项目很久了,我不再有工具来测试这个解决方案是否有效,所以我觉得我不能让它成为公认的答案。谢谢你的回答。不幸的是,我已经离开了使用WPF的项目很久了,我不再有工具来测试这个解决方案是否有效,所以我觉得我不能让它成为公认的答案。