mvvm如何使列表视图自动滚动到列表视图中的新项目

我正在使用MVVM模式,我有一个创建新ViewModel ,在用户单击保存后,此视图将关闭,并打开一个单独的视图,在ListView显示一组视图模型。

ListView按字母顺序排序,因此新的ViewModel可能出现在ListBox的底部,用户无法立即看到它。

我的问题是如何让视图自动滚动到新添加的项目?

我想它将使用附加的行为,以及ListView上的ScrollIntoView事件,但是我需要从GridView中捕获哪个事件,我不确定..

干杯

此解决方案适用于ListBox,但可以针对ListView进行修改…当您从ViewModel更改所选项目时,这将滚动所选项目进入视图。

类:

 ///  /// ListBoxItem Behavior class ///  public static class ListBoxItemBehavior { #region IsBroughtIntoViewWhenSelected ///  /// Gets the IsBroughtIntoViewWhenSelected value ///  ///  ///  public static bool GetIsBroughtIntoViewWhenSelected(ListBoxItem listBoxItem) { return (bool)listBoxItem.GetValue(IsBroughtIntoViewWhenSelectedProperty); } ///  /// Sets the IsBroughtIntoViewWhenSelected value ///  ///  ///  public static void SetIsBroughtIntoViewWhenSelected( ListBoxItem listBoxItem, bool value) { listBoxItem.SetValue(IsBroughtIntoViewWhenSelectedProperty, value); } ///  /// Determins if the ListBoxItem is bought into view when enabled ///  public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty = DependencyProperty.RegisterAttached( "IsBroughtIntoViewWhenSelected", typeof(bool), typeof(ListBoxItemBehavior), new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged)); ///  /// Action to take when item is brought into view ///  ///  ///  static void OnIsBroughtIntoViewWhenSelectedChanged( DependencyObject depObj, DependencyPropertyChangedEventArgs e) { ListBoxItem item = depObj as ListBoxItem; if (item == null) return; if (e.NewValue is bool == false) return; if ((bool)e.NewValue) item.Selected += OnListBoxItemSelected; else item.Selected -= OnListBoxItemSelected; } static void OnListBoxItemSelected(object sender, RoutedEventArgs e) { // Only react to the Selected event raised by the ListBoxItem // whose IsSelected property was modified. Ignore all ancestors // who are merely reporting that a descendant's Selected fired. if (!Object.ReferenceEquals(sender, e.OriginalSource)) return; ListBoxItem item = e.OriginalSource as ListBoxItem; if (item != null) item.BringIntoView(); } #endregion // IsBroughtIntoViewWhenSelected } 

将xmlns添加到视图中:

 xmlns:util="clr-namespace:YourNamespaceHere.Classes" 

将样式添加到Window / UserControl的资源:

    

实现列表框:

  

将所选项DependecyProperty添加到包含该集合的类中。 将listview的SelectedItem绑定到它。 将新模型添加到集合后,设置所选项DependencyProperty。

使用ListBox另一种解决方案 要实现自动滚动,您可以创建自定义控件!


C#

 public class LoggingListBox : ListBox { /// ///Define the AutoScroll property. If enabled, causes the ListBox to scroll to ///the last item whenever a new item is added. /// public static readonly DependencyProperty AutoScrollProperty = DependencyProperty.Register( "AutoScroll", typeof(Boolean), typeof(LoggingListBox), new FrameworkPropertyMetadata( true, //Default value. FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AutoScroll_PropertyChanged)); ///  /// Gets or sets whether or not the list should scroll to the last item /// when a new item is added. ///  [Category("Common")] //Indicate where the property is located in VS designer. public bool AutoScroll { get { return (bool)GetValue(AutoScrollProperty); } set { SetValue(AutoScrollProperty, value); } } ///  /// Event handler for when the AutoScroll property is changed. /// This delegates the call to SubscribeToAutoScroll_ItemsCollectionChanged(). ///  /// The DependencyObject whose property was changed. /// Change event args. private static void AutoScroll_PropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e) { SubscribeToAutoScroll_ItemsCollectionChanged( (LoggingListBox)d, (bool)e.NewValue); } ///  /// Subscribes to the list items' collection changed event if AutoScroll is enabled. /// Otherwise, it unsubscribes from that event. /// For this to work, the underlying list must implement INotifyCollectionChanged. /// /// (This function was only creative for brevity) ///  /// The list box containing the items collection. /// Subscribe to the collection changed event? private static void SubscribeToAutoScroll_ItemsCollectionChanged( LoggingListBox listBox, bool subscribe) { INotifyCollectionChanged notifyCollection = listBox.Items.SourceCollection as INotifyCollectionChanged; if (notifyCollection != null) { if (subscribe) { //AutoScroll is turned on, subscribe to collection changed events. notifyCollection.CollectionChanged += listBox.AutoScroll_ItemsCollectionChanged; } else { //AutoScroll is turned off, unsubscribe from collection changed events. notifyCollection.CollectionChanged -= listBox.AutoScroll_ItemsCollectionChanged; } } } ///  /// Event handler called only when the ItemCollection changes /// and if AutoScroll is enabled. ///  /// The ItemCollection. /// Change event args. private void AutoScroll_ItemsCollectionChanged( object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { int count = Items.Count; ScrollIntoView(Items[count - 1]); } } ///  /// Constructor a new LoggingListBox. ///  public LoggingListBox() { //Subscribe to the AutoScroll property's items collection //changed handler by default if AutoScroll is enabled by default. SubscribeToAutoScroll_ItemsCollectionChanged( this, (bool)AutoScrollProperty.DefaultMetadata.DefaultValue); } } 

XAML

以下是在XAML中使用控件的方法:

   

您需要在某处指定访问此控件的方式。 这完全取决于您的项目设置。

 xmlns:tools="clr-namespace:MyCustomControls;assembly=MyCustomControls" 

这个怎么运作

要创建自定义控件,您只需要C#代码。 我们通过扩展ListBox并仅添加一个属性AutoScroll来实现 。 因为它是依赖属性,所以它将参与WPF绑定系统,这也使它在Visual Studio设计器中可用。
覆盖依赖属性是一个相当大的主题,但是创建自定义控件是不可或缺的。 您可以在“ 控制创作概述”或“ 依赖关系属性概述”中了解更多信息。

目标是订阅基础项目集合的集合更改事件,以便我们可以在添加新项目时通过滚动到底部进行响应。 我们必须在两个地方订阅这个活动。

  1. 每当AutoScroll设置为true时 ,我们需要订阅。 AutoScroll的值可能随时发生变化,我们应该能够做出相应的响应。 如果设置为false ,我们应该通过取消订阅指示控件停止滚动到底部。
  2. 假设AutoScroll只需要在编译时设置,我们需要一种在启动时订阅的方法。 这是通过使用控件的构造函数完成的。

为什么要创建自定义控件

首先,我们尽可能简化了XAML。 我们只需要访问控件并可选择指定或绑定到AutoScroll属性。

它符合MVVM标准。 我们的视图模型不需要担心AutoScrollfunction,因为它在控件中是自包含的。 同时,视图模型可以提供AutoScroll属性绑定的属性,从而为视图和视图模型提供所需的解耦。

此外,我们避免使用行为。 这意味着我们已经从项目中删除了两个依赖项(授予这是这些依赖项首先被包含在内的唯一原因)。 我们可以安全地从项目引用中省略System.Windows.InteractivityMicrosoft.Expressions.Interactions

缺点

这种方法只有一个缺点。 基础项集合必须实现INotifyCollectionChanged 。 在大多数情况下,这不是问题。 如果您正在使用MVVM,您可能已经将您的项目包含在ObservableCollection ,该ObservableCollection已经实现了我们所需的接口。

请享用! 🙂

这可能不适用于WPF,但在WinForms中,代码类似于lstData.EnsureVisible(itemIndex);

嗯谈论矫枉过正,对于一个更简单的方法和我想象中最常用的方法….

对于listview简单地说:

 listView1.EnsureVisible(listView1.Items.Count - 1); 

而对于Listbox来说,简单地说:

 listBox1.SelectedIndex = listBox1.Items.Count - 1; listBox1.SelectedIndex = -1; 

在listview项目中添加(..etc)方法……或者在计时器勾选中敲击它。

OP下面的上述方式对我来说似乎有很大帮助我很懒…所有代码都解释了它的自我。