2012-08-25 51 views
7

我有一個ItemsControl,它在ScrollViewer中顯示它的項目,並進行虛擬化。我試圖將該ScrollViewer滾動到它包含的(屏幕外,因此虛擬化)項目。但是,由於該項目是虛擬化的,它並不存在於屏幕上並且沒有位置(IIUC)。我試過BringIntoView對子元素,但它不滾動到視圖。我也嘗試過使用TransformToAncestor,TransformBoundsScrollToVerticalOffset,但是TransformToAncestor永遠不會返回(我猜也是因爲虛擬化,因爲它沒有位置,但我沒有證據)以及它從不執行的代碼。滾動到虛擬化的元素ItemsControl

是否可以通過虛擬化ItemsControl滾動到某個項目?如果是這樣,怎麼樣?

回答

9

在.NET源代碼中查找引導我推薦使用ListBox及其ScrollIntoView方法。這種方法的實現依賴於幾個internal方法,如VirtualizingPanel.BringIndexIntoView,它強制在該索引處創建項目並滾動到該項目。許多這些機制都是內部的這一事實意味着如果你試圖自己做這個,你將會遇到一個不好的時機

(爲了使這帶來了無形的選擇,你可以retemplate的ListBoxItems

+0

我想避免這樣做,因爲我不需要ListBox的「選擇項目」功能。任何想法爲什麼'ItemsControl'沒有'ScrollIntoView'? –

+0

@Seth:正如我所說,你可以隱藏選擇,誰在乎它是否存在?它沒有滾動,因爲它是按照這種方式設計的,'ItemsControl'是項目控件的最基本的東西,這種基類不需要滾動功能。 –

+0

現在要了解如何使ListBox退出時單擊完全滾動項目... –

11

我一直在尋找得到一個ItemsControl的一個VirtualizingStackPanel現在滾動到了一陣項目,並保持找到「使用列表框」答案。我不想,所以我找到了一個辦法。首先,您需要爲您的ItemsControl設置一個控件模板,該控件模板中包含一個ScrollViewer(如果您使用的是項目控件,則可能已有此模板)。我的基本模板如下所示(包含在一個方便的樣式爲ItemsControl的)

<Style x:Key="TheItemsControlStyle" TargetType="{x:Type ItemsControl}"> 
    <Setter Property="Template"> 
    <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ItemsControl}"> 
       <Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True"> 
        <ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False" HorizontalScrollBarVisibility="Auto"> 
         <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" /> 
        </ScrollViewer> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

所以我基本上是有一個邊界與滾動觀衆多數民衆贊成將包含我的內容。現在

<ItemsControl x:Name="myItemsControl" [..snip..] Style="{DynamicResource TheItemsControlStyle}" ScrollViewer.CanContentScroll="True" VirtualizingStackPanel.IsVirtualizing="True"> 

好有趣的部分:
我的ItemsControl與定義。我創建了一個擴展方法來連接到任何ItemsControl中得到它滾動到給定商品:

public static void VirtualizedScrollIntoView(this ItemsControl control, object item) { 
     try { 
      // this is basically getting a reference to the ScrollViewer defined in the ItemsControl's style (identified above). 
      // you *could* enumerate over the ItemsControl's children until you hit a scroll viewer, but this is quick and 
      // dirty! 
      // First 0 in the GetChild returns the Border from the ControlTemplate, and the second 0 gets the ScrollViewer from 
      // the Border. 
      ScrollViewer sv = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild((DependencyObject)control, 0), 0) as ScrollViewer; 
      // now get the index of the item your passing in 
      int index = control.Items.IndexOf(item); 
      if(index != -1) { 
       // since the scroll viewer is using content scrolling not pixel based scrolling we just tell it to scroll to the index of the item 
       // and viola! we scroll there! 
       sv.ScrollToVerticalOffset(index); 
      } 
     } catch(Exception ex) { 
      Debug.WriteLine("What the..." + ex.Message); 
     } 
    } 

所以在地方推廣方法,你能使用它,就像ListBox的同伴方法:

myItemsControl.VirtualizedScrollIntoView(someItemInTheList); 

很好用!

請注意,您也可以調用sv.ScrollToEnd()和其他常用的滾動方法來解決您的項目。

+0

不幸的是我使用基於像素的滾動,所以這對我不起作用,但我確信這會在未來幫助其他人+1。 –

+0

如果您使用的是基於像素的滾動,那麼您可以在ItemsControl中獲得單個項目的大小(如果其固定大小則很容易,但也可以在ItemControls ItemTemplate上進行枚舉以獲取個體的大小item),然後只乘以單個項目大小返回的索引,然後用該數字調用ScrollToVerticalOffset。即sv.ScrollToVerticalOffset((double)index * sizeOfAnItemInTheList); –

0

我知道我敢遲到了,但希望這可以幫助別人一起尋找解決方案來了...

int index = myItemsControl.Items.IndexOf(*your item*).FirstOrDefault(); 
int rowHeight = *height of your rows*; 
myScrollView.ScrollToVerticalOffset(index*rowHeight); 
//this will bring the given item to the top of the scrollViewer window 

...和我的XAML是設置這樣的...

<ScrollViewer x:Name="myScrollView"> 
    <ItemsControl x:Name="myItemsControl"> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <Grid> 
        <!-- data here --> 
       </Grid> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</ScrollViewer>