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

时间:2010-07-23 10:10:22

标签: c# wpf listview mvvm scroll

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

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

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

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

干杯

5 个答案:

答案 0 :(得分:15)

答案 1 :(得分:3)

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

<小时/>

C#

public class LoggingListBox : ListBox
{
    ///<summary>
    ///Define the AutoScroll property. If enabled, causes the ListBox to scroll to 
    ///the last item whenever a new item is added.
    ///</summary>
    public static readonly DependencyProperty AutoScrollProperty = 
        DependencyProperty.Register(
            "AutoScroll", 
            typeof(Boolean), 
            typeof(LoggingListBox), 
            new FrameworkPropertyMetadata(
                true, //Default value.
                FrameworkPropertyMetadataOptions.AffectsArrange | 
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
                AutoScroll_PropertyChanged));

    /// <summary>
    /// Gets or sets whether or not the list should scroll to the last item 
    /// when a new item is added.
    /// </summary>
    [Category("Common")] //Indicate where the property is located in VS designer.
    public bool AutoScroll
    {
        get { return (bool)GetValue(AutoScrollProperty); }
        set { SetValue(AutoScrollProperty, value); }
    }

    /// <summary>
    /// Event handler for when the AutoScroll property is changed.
    /// This delegates the call to SubscribeToAutoScroll_ItemsCollectionChanged().
    /// </summary>
    /// <param name="d">The DependencyObject whose property was changed.</param>
    /// <param name="e">Change event args.</param>
    private static void AutoScroll_PropertyChanged(
        DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        SubscribeToAutoScroll_ItemsCollectionChanged(
            (LoggingListBox)d,
            (bool)e.NewValue);
    }

    /// <summary>
    /// 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)
    /// </summary>
    /// <param name="listBox">The list box containing the items collection.</param>
    /// <param name="subscribe">Subscribe to the collection changed event?</param>
    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;
            }
        }
    }

    /// <summary>
    /// Event handler called only when the ItemCollection changes
    /// and if AutoScroll is enabled.
    /// </summary>
    /// <param name="sender">The ItemCollection.</param>
    /// <param name="e">Change event args.</param>
    private void AutoScroll_ItemsCollectionChanged(
        object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            int count = Items.Count;
            ScrollIntoView(Items[count - 1]);
        }
    }

    /// <summary>
    /// Constructor a new LoggingListBox.
    /// </summary>
    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中使用控件的方法:

<tools:LoggingListBox/> <!-- AutoScroll="true" by default. -->

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

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

如何运作

要创建自定义控件,您只需要C#代码。我们通过扩展ListBox并仅添加一个属性 AutoScroll 来实现此目的。因为它是一个依赖属性,它将参与WPF绑定系统,这也使它在Visual Studio设计器中可用。
覆盖依赖属性是一个相当大的主题,但是创建自定义控件是不可或缺的。您可以在Control Authoring OverviewDependency Properties Overview上了解详情。

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

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

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

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

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

    缺点

    这种方法只有一个缺点。基础项集合必须实现INotifyCollectionChanged。在大多数情况下,这不是问题。如果您正在使用MVVM,那么您可能已经将项目包含在ObservableCollection内,该{ "windows": { "cmd": ["g++", "$file_name","-o", "${file_base_name}.exe", "-lm", "-Wall", "&", "start", "${file_base_name}.exe"] }, "selector": "source.c++", "shell": true, "working_dir": "${file_path}" } 已经实现了我们所需的接口。

    享受! : - )

答案 2 :(得分:2)

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

答案 3 :(得分:-2)

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

答案 4 :(得分:-2)

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

对于listview来说只是打击:

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

对于Listbox而言,只需查看:

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

到列表视图项目添加(..etc)方法... ..或者在计时器上打它。

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