WPF ScrollViewer с ListBox - PullRequest
       0

WPF ScrollViewer с ListBox

4 голосов
/ 25 сентября 2010

нужна ваша помощь. У меня есть ListBox (с virtualization ), который отображает ScrollViewer. Мои элементы ListBox можно расширять, и при увеличении их высота может превышать видимую область прокрутки.

Проблема, с которой я сталкиваюсь, заключается в том, что когда элемент списка выходит за пределы видимой области прокрутки - прокрутка переходит к следующему элементу ListBox вместо простой прокрутки представления.

Проверьте этот код:

    <ListBox Grid.Row="1" Grid.Column="0" DataContext="{Binding SpecPackageSpecGroupListViewModel}" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"
                     ItemContainerStyle="{StaticResource SpecPackageSpecGroupListBoxStyle}" ScrollViewer.IsDeferredScrollingEnabled="True" 
                     ItemsSource="{Binding SortedChildren}" ScrollViewer.CanContentScroll="True"
                     Background="Transparent"
                     BorderThickness="0" SelectionMode="Extended"
                     Margin="5,5,5,5">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Controls:SpecPackageSpecGroupControl/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

Конечно, я не могу обернуть свой ListBox другим скроллером, так как он отключит визуализацию (что очень бессильно для меня).

Если я установил для CanContentScroll значение False, все работает как положено, но виртуализация перестает работать.

HELP !!!

Гиль

Ответы [ 3 ]

3 голосов
/ 26 сентября 2010

Хорошо, так что перед тем, как я собирался сдаться и каким-то образом научиться жить с этой ошибкой, я наткнулся на пост (который я не могу найти сейчас), который предполагает, что TreeView поддерживает прокрутку на основе пикселей (AKA Physical Scrolling) без отключения визуализации.

Итак, я попробовал это и действительно - это работает!Убедитесь, что виртуализация работает, протестировано с ~ 1000 элементов, а также установите точку останова на конструкторе элемента управления и убедитесь, что она вызывается при прокрутке моего представления.

Единственный недостаток использования TreeView вместо ListBoxв том, что TreeView, кажется, не поддерживает выбор нескольких элементов (что мне было нужно), но реализовать это намного проще, чем реализовать интеллектуальную прокрутку для ListBox.

Я создал стиль для TreeViewItem, который заставляет TreeViewItem выглядетьи ведут себя так же, как ListBoxItem, это на самом деле не является обязательным, но я предпочел это так (помимо того, что в базовом стиле есть проблемы растяжения, которые я должен был исправить с помощью стилей).По сути, я удалил ItemsPresenter и остался только с ContentPresenter, поскольку мои данные не являются иерархическими:

    <Style x:Key="MyTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="VerticalContentAlignment" Value="Stretch"/>
        <Setter Property="OverridesDefaultStyle" Value="true"/>    
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TreeViewItem}">
                    <Border Name="myBorder" 
                        SnapsToDevicePixels="true" 
                        CornerRadius="0,0,0,0" 
                        VerticalAlignment="Stretch" 
                        HorizontalAlignment="Stretch"
                        BorderThickness="0"
                        BorderBrush="Transparent"
                        Height="Auto"
                        Margin="1,1,1,3" 
                        Background="Transparent">
                        <ContentPresenter Grid.Column="1" x:Name="PART_Header" HorizontalAlignment="Stretch" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Теперь - единственное, что мне осталось сделать, - это реализовать представление дерева множественного выбора.Для реализации такого поведения могут быть разные подходы, я выбрал подход ViewModel.

Полученный из TreeView я создал новый MultiSelectionTreeView:

public class MultiSelectionTreeView : TreeView
{
    private static bool CtrlPressed
    {
        get
        {
            return Keyboard.IsKeyDown(Key.LeftCtrl);
        }
    }

    protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
    {
        base.OnSelectedItemChanged(e);

        var previouseItemViewModel = e.OldValue as IMultiSelectionTreeViewItemViewModel;
        if (previouseItemViewModel != null)
        {
            if (!CtrlPressed)
                previouseItemViewModel.IsSelected = false;
        }                        

        var newItemViewModel = e.NewValue as IMultiSelectionTreeViewItemViewModel;
        if (newItemViewModel != null)
        {
            if (!CtrlPressed)
                newItemViewModel.ClearSelectedSiblings();
            newItemViewModel.IsSelected = true;
        }                
    }
}

Где IMultiSelectionTreeViewItemViewModel выглядит следующим образом:

public interface IMultiSelectionTreeViewItemViewModel
{
    bool IsSelected { get; set; }
    void ClearSelectedSiblings();
}

Конечно, теперь я отвечаю за то, как представлены выбранные элементы, - в моем случае это было дано, поскольку у элементов моего древовидного представления был свой собственный DataTemplate, который имел указание для его выбора.Если это не ваш случай, и он вам нужен, просто расширите шаблон данных элемента представления в виде дерева, чтобы указать его состояние выбора в соответствии со свойством IsSelected модели представления.

Надеюсь, это когда-нибудь кому-нибудь поможет :-) Удачи!

Гили

2 голосов
/ 28 августа 2012

Этот вопрос все еще поднимается в поисковых системах, поэтому я отвечу на него через 2 года.

WPF 4.5 теперь поддерживает пиксельные виртуализирующие панели.

Если вы можете настроить таргетинг на 4.5, просто добавьте это в свой список:

<Setter Property="VirtualizingPanel.ScrollUnit" Value="Pixel"/>

Что нового в .NET 4.5 (который включает в себя новые вещи в WPF 4.5), см. http://msdn.microsoft.com/en-us/library/ms171868.aspx

1 голос
/ 25 сентября 2010

Взгляните здесь (Bea Stollnitz) и здесь (Dan Crevier) ; в основном вам необходимо реализовать собственный контейнер, поддерживающий виртуализацию и прокрутку на основе пикселей. Вы также можете посмотреть этот аналогичный вопрос SO для получения более подробной информации или возможной альтернативы. Производная от VirtualizingStackPanel и изменение поведения прокрутки, кажется, лучшая ставка.

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