WPF - 在ItemsSource更改时重置ListBox滚动位置

问题描述:

我目前有一个ListBox,其ItemsSource集合绑定到我的viewmodel上IEnumerable类型的属性。当preoprty的引用发生变化时,ListBox会按照预期进行更新,但是如果我有大量的项目集合并滚动到ListBox的底部,然后将引用更改为另一个包含比如1个项目的集合,ListBox视图是空白的并且不显示滚动条。我必须用鼠标滚轮向上滚动列表框,直到看到1个项目。WPF - 在ItemsSource更改时重置ListBox滚动位置

所以,我想我之后,是一种重置ListBox的滚动位置到顶部的方法,每当ItemsSource属性发生变化时,无论该集合有多大或多小,总是显示某些内容。 (改变ItemsSource的时候对我来说,ListBox滚动到最后一项最新系列中)

if (listBox.Items.Count > 0) { 
    listBox.ScrollIntoView(listBox.Items[0]); 
} 
+0

我理解你的问题是否正确或者你在找别的东西? – 2011-01-26 12:35:02

+0

@Meleak谢谢!该方法将会有用。我实际上已经将问题追踪到了我的ListBox使用的VirtualizingTilePanel,我从http://blogs.msdn.com/b/dancre/archive/2006/02/16/implementing-a-virtualizingpanel-part -4-,goods.aspx。通过滚动到底部,可以在示例下载中复制问题,然后用包含单个项目的集合替换绑定的集合。视图变为空白,直到用鼠标滚轮向上滚动。 – devdigital 2011-01-26 13:12:35

我无法重现您的问题:

试试这个。无论如何,要将ListBox滚动到顶端,每次更改ItemsSource时,都可以使用一些代码。先听在ItemsSourceProperty更改,然后滚动ListBox顶端一旦其项目已经产生

更新

做出了附加的行为,这是否来避免后面的代码。它可以像这样使用

<ListBox ... 
     behaviors:ScrollToTopBehavior.ScrollToTop="True"/> 

ScrollToTopBehavior

public static class ScrollToTopBehavior 
{ 
    public static readonly DependencyProperty ScrollToTopProperty = 
     DependencyProperty.RegisterAttached 
     (
      "ScrollToTop", 
      typeof(bool), 
      typeof(ScrollToTopBehavior), 
      new UIPropertyMetadata(false, OnScrollToTopPropertyChanged) 
     ); 
    public static bool GetScrollToTop(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(ScrollToTopProperty); 
    } 
    public static void SetScrollToTop(DependencyObject obj, bool value) 
    { 
     obj.SetValue(ScrollToTopProperty, value); 
    } 
    private static void OnScrollToTopPropertyChanged(DependencyObject dpo, 
                DependencyPropertyChangedEventArgs e) 
    { 
     ItemsControl itemsControl = dpo as ItemsControl; 
     if (itemsControl != null) 
     { 
      DependencyPropertyDescriptor dependencyPropertyDescriptor = 
        DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl)); 
      if (dependencyPropertyDescriptor != null) 
      { 
       if ((bool)e.NewValue == true) 
       { 
        dependencyPropertyDescriptor.AddValueChanged(itemsControl, ItemsSourceChanged); 
       } 
       else 
       { 
        dependencyPropertyDescriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged); 
       } 
      } 
     } 
    } 
    static void ItemsSourceChanged(object sender, EventArgs e) 
    { 
     ItemsControl itemsControl = sender as ItemsControl; 
     EventHandler eventHandler = null; 
     eventHandler = new EventHandler(delegate 
     { 
      if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
      { 
       ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(itemsControl) as ScrollViewer; 
       scrollViewer.ScrollToTop(); 
       itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler; 
      } 
     }); 
     itemsControl.ItemContainerGenerator.StatusChanged += eventHandler; 
    } 
} 

而且GetVisualChild的实现

private T GetVisualChild<T>(DependencyObject parent) where T : Visual 
{ 
    T child = default(T); 
    int numVisuals = VisualTreeHelper.GetChildrenCount(parent); 
    for (int i = 0; i < numVisuals; i++) 
    { 
     Visual v = (Visual)VisualTreeHelper.GetChild(parent, i); 
     child = v as T; 
     if (child == null) 
     { 
      child = GetVisualChild<T>(v); 
     } 
     if (child != null) 
     { 
      break; 
     } 
    } 
    return child; 
} 

迟到的回答

一个简单的解决方法是添加事件处理程序TargetUpdated事件,并设置在ItemsSourceNotifyOnTargetUpdated=True结合:

<ListBox x:Name="listBox" 
     ItemsSource="{Binding MySource, NotifyOnTargetUpdated=True}" 
     TargetUpdated="ListBox_TargetUpdated"/> 

,并在事件处理程序,滚动到顶部项目:

private void ListBox_TargetUpdated(object sender, DataTransferEventArgs e) 
{ 
    if (listBox.Items.Count > 0) 
    { 
     listBox.ScrollIntoView(listBox.Items[0]); 
    } 
} 

改进弗雷德里克Hedblad的回答工作与ObservableCollection:

public static class ItemsControlAttachedProperties 
{ 
    #region ScrollToTopOnItemsSourceChange Property 

    public static readonly DependencyProperty ScrollToTopOnItemsSourceChangeProperty = 
     DependencyProperty.RegisterAttached(
      "ScrollToTopOnItemsSourceChange", 
      typeof(bool), 
      typeof(ItemsControlAttachedProperties), 
      new UIPropertyMetadata(false, OnScrollToTopOnItemsSourceChangePropertyChanged)); 

    public static bool GetScrollToTopOnItemsSourceChange(DependencyObject obj) 
    { 
     return (bool) obj.GetValue(ScrollToTopOnItemsSourceChangeProperty); 
    } 

    public static void SetScrollToTopOnItemsSourceChange(DependencyObject obj, bool value) 
    { 
     obj.SetValue(ScrollToTopOnItemsSourceChangeProperty, value); 
    } 

    static void OnScrollToTopOnItemsSourceChangePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
    { 
     var itemsControl = obj as ItemsControl; 
     if (itemsControl == null) 
     { 
      throw new Exception("ScrollToTopOnItemsSourceChange Property must be attached to an ItemsControl based control."); 
     } 

     DependencyPropertyDescriptor descriptor = 
      DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl)); 
     if (descriptor != null) 
     { 
      if ((bool) e.NewValue) 
      { 
       descriptor.AddValueChanged(itemsControl, ItemsSourceChanged); 
      } 
      else 
      { 
       descriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged); 
      } 
     } 
    } 

    static void ItemsSourceChanged(object sender, EventArgs e) 
    { 
     var itemsControl = sender as ItemsControl; 
     DoScrollToTop(itemsControl); 

     var collection = itemsControl.ItemsSource as INotifyCollectionChanged; 
     if (collection != null) 
     { 
      collection.CollectionChanged += (o, args) => DoScrollToTop(itemsControl); 
     } 
    } 

    static void DoScrollToTop(ItemsControl itemsControl) 
    { 
     EventHandler eventHandler = null; 
     eventHandler = 
      delegate 
      { 
       if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
       { 
        var scrollViewer = GetVisualChild<ScrollViewer>(itemsControl); 
        scrollViewer.ScrollToTop(); 
        itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler; 
       } 
      }; 
     itemsControl.ItemContainerGenerator.StatusChanged += eventHandler; 
    } 

    static T GetVisualChild<T>(DependencyObject parent) where T : Visual 
    { 
     T child = default(T); 
     int numVisuals = VisualTreeHelper.GetChildrenCount(parent); 
     for (var i = 0; i < numVisuals; i++) 
     { 
      var v = (Visual) VisualTreeHelper.GetChild(parent, i); 
      child = v as T ?? GetVisualChild<T>(v); 
      if (child != null) 
      { 
       break; 
      } 
     } 
     return child; 
    } 

    #endregion 
} 

当您格式化控件时,您选择一个单元格的范围作为选择选择是t母鸡在列表框中列出。您还可以选择一个单元格作为所选选项的链接,其中将显示一个数字,具体取决于列表中选择的位置。列表中的第一个为1,第二个为2等该代码是很简单: -

范围( “A1”)选择

选择= 1名

更改( “A1”)你有联系 细胞,改变1中的位置你想要选择的列表。

作为链接的单元格引用可以双向使用 - 如果更改所选内容,单元格中的数字会发生变化,如果更改单元格中的数字,突出显示的选择内容会发生变化。