Можно ли задать высоту WPF ListBox, кратную его высоте элемента? - PullRequest
1 голос
/ 28 февраля 2012

Есть ли способ установить атрибут Height для множественного выбора WPF ListBox кратным высоте элемента, аналогично установке атрибута size для элемента html select?

У меня есть бизнес-требование, чтобы не иметь половину элемента, отображаемого внизу списка (если это длинный список с полосой прокрутки), и не иметь лишних пробелов внизу (еслиэто короткий список со всеми пунктами, отображаемыми), но единственный способ, который я могу найти, - это просто настроить Height до тех пор, пока он не будет выглядеть правильно.

(Что еще я пробовал? I 'Мы спросили коллег, провели поиск в MSDN и StackOverflow, провели общий поиск в Google и посмотрели на то, что VS Intellisense предлагал, когда я редактировал код. Существует множество советов о том, как установить высоту в соответствии с контейнером ListBox, но это противоположночто я пытаюсь сделать.)

Ответы [ 2 ]

7 голосов
/ 28 февраля 2012

Да, можно предположить, что был бы более простой способ сделать это (одно свойство snapToWholeElement). Я тоже не смог найти эту недвижимость.

Чтобы выполнить ваше требование, я написал небольшую логику. По сути, в моем объекте 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>

Обратите внимание, что ItemTemplate несколько не тривиален. Важно отметить, что я дал этому предмету Имя, чтобы найти его позже.

В конструкторе с выделенным кодом я поместил некоторые данные в список:

public MainWindow( )
{
    InitializeComponent( );
    this.x_list.ItemsSource = Enumerable.Range( 0, 100 );
}

Далее я реализую 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;
}

Я буду использовать следующий предикат, который возвращает true, если искомый элемент является границей, а его имя - "x". Вам следует изменить этот предикат, чтобы он соответствовал корневому элементу вашего ItemTemplate.

findVisualItem(
    x_list,
    el => { return ( el is Border ) ? ( (FrameworkElement)el ).Name == "x" : false; }
    );

Наконец, свойство 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, и когда элементы списка были загружены (событие Loaded в ListBox), я вызвал событие PropertyChanged для свойства 'lbHeight'. В какой-то момент это было необходимо, но в конце WPF извлек свойство lbHeight, когда у меня уже есть обработанный элемент.

Возможно, ваши Предметы не одинаковы по высоте, и в этом случае вам нужно будет суммировать все Предметы в VirtualizedStackPanel. Если у вас есть горизонтальная полоса прокрутки, вы должны учитывать ее для общей высоты курса. Но это общая идея. Вы опубликовали свой вопрос всего через 3 часа - я надеюсь, что кто-то придет с более простым ответом.

1 голос
/ 13 апреля 2013

Это делается путем установки свойства родительского элемента управления Height на Auto, без установки какого-либо размера для самого списка (или также установки для Auto).Чтобы ограничить размер списка, вы также должны указать MaxHeight Свойство

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...