2010-09-24 69 views
4

需要你的幫助。我有一個ListBox(帶有虛擬化),它顯示一個ScrollViewer。 我的列表框項目是可擴展的,雖然展開它們的高度可能會超過可見滾動區域。帶列表框的WPF ScrollViewer

我遇到的問題是,當列表框項目超出可見滾動區域時 - 滾動跳轉到下一個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> 

中 - 當然,我不能換我與另一個滾動列表框,因爲它將可視關閉(這是非常虛弱無力的我)。

如果我將CanContentScroll設置爲False,一切都按預期工作 - 但虛擬化停止工作。

幫助!!!

吉利

回答

1

看看here (Bea Stollnitz)here(Dan Crevier);基本上你需要實現你自己的容器,它支持虛擬化和基於像素的滾動。您也可以查看this similar SO question瞭解更多詳情或其他可能的選擇。從VirtualizingStackPanel派生並修改滾動行爲似乎是最好的選擇。

+0

這將是荒謬的因爲我花了這麼多時間,讓我的系統面臨這樣的風險。我們正在開發系統而不是控制。簡單地將樹視圖調整爲這一點更容易。我永遠不會說永遠不會,如果沒有一個簡單的解決方案 - 我們作爲開發人員需要做必要的事情,但因爲這裏有更好的選擇 - 我不明白爲什麼要冒險。 – Gilad 2012-04-16 11:28:02

+1

BTW:這是一個錯誤。微軟應該照顧這一點。在基於像素的滾動和虛擬化之間不存在任何衝突。證明這一點的是,它在TreeView中工作得很好,由於異端結構他們沒有選擇。有人在那裏懶惰...... :-) – Gilad 2012-04-16 11:31:03

+0

實際上,TreeView根本沒有虛擬化,這是另一個令人頭痛的問題。恰當地實施它是一個適當的痛苦。當然,這很困難,因爲每個父項的項目都是由另一個ItemsControl表示的,並且使每個嵌入的ItemsControl知道最外層容器的大小和屬性,以便虛擬化能夠正常工作。這就是TreeView的性能真的吸引超過一小部分項目的原因。 – 2012-04-18 08:22:42

3

好的,所以在我即將放棄並以某種方式學習如何忍受這個bug後,我碰到了一個帖子(我現在似乎找不到),這表明TreeView確實支持基於像素的滾動(AKA物理滾動)而不關閉可視化。

所以我試了這個,確實 - 它的工作原理!確保驗證虛擬化是否有效,並測試了大約1000個項目,並在我的控件構造函數中設置了一個斷點,並確保在我的視圖滾動時調用它。

使用TreeView而不是ListBox的唯一缺點是TreeView似乎不支持多項選擇(我需要) - 但實現此方法比爲ListBox實現智能滾動要容易得多。

我創建了樹型視圖樣式,使樹型視圖的外觀和行爲就像一個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

這個問題仍然是即將在搜索引擎,所以兩年後我會回答它。

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