代码之家  ›  专栏  ›  技术社区  ›  Jobi Joy

如何控制MVVM WPF应用程序中列表框的滚动位置

  •  23
  • Jobi Joy  · 技术社区  · 15 年前

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

    如果在视图侧执行此操作,则可以调用ListBox.ScrollInToView(lstBoxItem)。 但从MVVM的角度来看,解决这个常见滚动问题的最佳方法是什么?

    4 回复  |  直到 8 年前
        1
  •  27
  •   Noctis    10 年前

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

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

    从我的虚拟机中,我可以获取默认的集合视图,并使用 MoveCurrent*() 方法以确保正在编辑的项是当前项。

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

    注:编辑后使用 ListBox.ScrollIntoView() 适应虚拟化

        2
  •  7
  •   LightBulb    8 年前

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

    using System.Windows.Controls;
    using System.Windows.Interactivity;
    
    namespace Jarloo.Sojurn.Behaviors
    {
        public sealed class ScrollIntoViewBehavior : Behavior<ListBox>
        {
            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"
    

    然后这样做:

    <ListBox ItemsSource="{Binding MyData}" SelectedItem="{Binding MySelectedItem}">
             <i:Interaction.Behaviors>
                 <behaviors:ScrollIntoViewBehavior />
             </i:Interaction.Behaviors>
    </ListBox>
    

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

        3
  •  3
  •   VahidN    10 年前

    这是已接受答案的随附属性表:

    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);
            }
        }
    }
    

    用途:

    <ListBox ItemsSource="{Binding}"
              IsSynchronizedWithCurrentItem="True"
              behaviors:ScrollCurrentItemIntoViewBehavior.AutoScrollToCurrentItem="True">
    
        4
  •  1
  •   D.Kempkes    11 年前

    如果上面的代码对您不起作用,请尝试一下

    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中的用法

    <ListBox IsSynchronizedWithCurrentItem="True" extenders:ListBoxExtenders.AutoScrollToCurrentItem="True" ..../>