如何在MVVM WPF应用程序中控制ListBox的滚动位置

我有一个大的ListBox启用了垂直滚动,我的MVVM有New和Edit ICommands。 我在集合的末尾添加了新项目,但是当我调用MVVM-AddCommand时,我还希望滚动条自动定位到End。 我还从应用程序的其他部分创建一个可编辑的项目(通过调用带有特定行项的EditCommand),以便我的ListBoxItem使用DataTrigger进入编辑模式,但是如何将该特定行(ListBoxItem)带到视图中通过调整滚动位置。

如果我在View端进行,我可以调用listBox.ScrollInToView(lstBoxItem)。 但是从MVVM角度解决这个常见Scroll问题的最佳方法是什么。

我通常在ListBox上设置IsSynchronizedWithCurrentItem="True" 。 然后我添加了一个SelectionChanged处理程序,并始终将所选项目置于视图中,代码如下:

  private void BringSelectionIntoView(object sender, SelectionChangedEventArgs e) { Selector selector = sender as Selector; if (selector is ListBox) { (selector as ListBox).ScrollIntoView(selector.SelectedItem); } } 

从我的VM我可以获取默认的集合视图并使用其中一个MoveCurrent*()方法来确保正在编辑的项目是当前项目。

 CollectionViewSource.GetDefaultView(_myCollection).MoveCurrentTo(thisItem); 

注意:编辑使用ListBox.ScrollIntoView()来容纳虚拟化

在MVVM中使用它可以通过附加行为轻松完成,如下所示:

 using System.Windows.Controls; using System.Windows.Interactivity; namespace Jarloo.Sojurn.Behaviors { public sealed class ScrollIntoViewBehavior : Behavior { protected override void OnAttached() { base.OnAttached(); AssociatedObject.SelectionChanged += ScrollIntoView; } protected override void OnDetaching() { AssociatedObject.SelectionChanged -= ScrollIntoView; base.OnDetaching(); } private void ScrollIntoView(object o, SelectionChangedEventArgs e) { ListBox b = (ListBox) o; if (b == null) return; if (b.SelectedItem == null) return; ListBoxItem item = (ListBoxItem) ((ListBox) o).ItemContainerGenerator.ContainerFromItem(((ListBox) o).SelectedItem); if (item != null) item.BringIntoView(); } } } 

然后在查看广告中,此引用位于顶部:

 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

这样做:

      

现在,当SelectedItem更改时,行为将为您执行BringIntoView()调用。

这是接受的答案的附加财产forms:

 using System.Windows; using System.Windows.Controls; namespace CommonBehaviors { public static class ScrollCurrentItemIntoViewBehavior { public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ScrollCurrentItemIntoViewBehavior), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged)); public static bool GetAutoScrollToCurrentItem(DependencyObject obj) { return (bool)obj.GetValue(AutoScrollToCurrentItemProperty); } public static void OnAutoScrollToCurrentItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { var listBox = obj as ListBox; if (listBox == null) return; var newValue = (bool)e.NewValue; if (newValue) listBox.SelectionChanged += listBoxSelectionChanged; else listBox.SelectionChanged -= listBoxSelectionChanged; } static void listBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { var listBox = sender as ListBox; if (listBox == null || listBox.SelectedItem == null || listBox.Items == null) return; listBox.Items.MoveCurrentTo(listBox.SelectedItem); listBox.ScrollIntoView(listBox.SelectedItem); } public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value) { obj.SetValue(AutoScrollToCurrentItemProperty, value); } } } 

用法:

  

如果上述代码不适合您,请尝试一下

 public class ListBoxExtenders : DependencyObject { public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged)); public static bool GetAutoScrollToCurrentItem(DependencyObject obj) { return (bool)obj.GetValue(AutoScrollToSelectedItemProperty); } public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value) { obj.SetValue(AutoScrollToSelectedItemProperty, value); } public static void OnAutoScrollToCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) { var listBox = s as ListBox; if (listBox != null) { var listBoxItems = listBox.Items; if (listBoxItems != null) { var newValue = (bool)e.NewValue; var autoScrollToCurrentItemWorker = new EventHandler((s1, e2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition)); if (newValue) listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker; else listBoxItems.CurrentChanged -= autoScrollToCurrentItemWorker; } } } public static void OnAutoScrollToCurrentItem(ListBox listBox, int index) { if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0) listBox.ScrollIntoView(listBox.Items[index]); } } 

在XAML中的用法