使用 MVVM 将列表框滚动到视图中 [英] ListBox Scroll Into View with MVVM

查看:61
本文介绍了使用 MVVM 将列表框滚动到视图中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个非常简单的问题,但我不知道如何使用 MVVM 破解它.

I have what is a pretty simple problem, but I can't figure out how to crack it using MVVM.

我有一个 ListBox 绑定到一个 ObservableCollection.

I have a ListBox that is bound to an ObservableCollection<string>.

我运行了一个过程,将向集合中添加一大堆项目,因此它们会显示在 ListBox 中.

I run a process that will add a whole bunch of items to the collection and they are therefore shown in the ListBox.

问题是,当项目被添加到列表框时......滚动条只会增长,但我似乎无法弄清楚如何为每个添加的项目制作 ScrollIntoView到集合.

The problem is that as the items are added to the list box... the scroll bar just grows, but I can't seem to figure out how to make it ScrollIntoView for each item added to the collection.

此示例代码完美地说明了问题.

This sample code illustrates the problem perfectly.

XAML

<Window x:Class="Stack.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:Stack"
    Title="MainWindow"
    Height="350"
    Width="525">
<Window.DataContext>
    <vm:MainWindowViewModel />
</Window.DataContext>
<StackPanel>
    <ListBox Margin="10" Height="150"
             ItemsSource="{Binding Path=MyValue}" />
    <Button Margin="10"
            Height="25"
            Content="Generate"
            Command="{Binding Path=CommandName}" />
</StackPanel>
</Window>

查看模型

namespace Stack
{
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;

/// <summary>
/// TODO: Update summary.
/// </summary>
public class MainWindowViewModel : INotifyPropertyChanged
{
    private readonly BackgroundWorker _worker;

    private ICommand _commandName;

    private ObservableCollection<string> _myValue = new ObservableCollection<string>();

    /// <summary>
    /// Initializes a new instance of the <see cref="MainWindowViewModel" /> class.
    /// </summary>
    public MainWindowViewModel()
    {
        this._worker = new BackgroundWorker();
        this._worker.DoWork += new DoWorkEventHandler(DoWork);
        this._worker.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
        this._worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
        {
            CommandManager.InvalidateRequerySuggested();
        };
    }

    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    public ICommand CommandName
    {
        get
        {
            if (this._commandName == null)
            {
                this._commandName = new RelayCommand(() => this.CommandMethod());
            }
            return this._commandName;
        }
    }

    /// <summary>
    /// Gets or sets my value.
    /// </summary>
    /// <value>My value.</value>
    public ObservableCollection<string> MyValue
    {
        get
        {
            return this._myValue;
        }
        set
        {
            this._myValue = value;
            this.NotifyPropertyChange("MyValue");
        }
    }

    /// <summary>
    /// Notifies the property change.
    /// </summary>
    /// <param name="propName">Name of the prop.</param>
    internal void NotifyPropertyChange(string propName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }

    /// <summary>
    /// Commands the method.
    /// </summary>
    private void CommandMethod()
    {
        this.MyValue.Clear();
        this._worker.RunWorkerAsync();
        this._worker.WorkerReportsProgress = true;
    }

    /// <summary>
    /// Does the work.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs" /> instance containing the event data.</param>
    private void DoWork(object sender, DoWorkEventArgs e)
    {
        this.Populate();
    }

    /// <summary>
    /// Populates this instance.
    /// </summary>
    private void Populate()
    {
        for (int index = 0; index < 100; index++)
        {
            System.Threading.Thread.Sleep(10);
            this._worker.ReportProgress(index);
        }
    }

    /// <summary>
    /// Progresses the changed.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs" /> instance containing the event data.</param>
    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.MyValue.Add(e.ProgressPercentage.ToString());
    }
}

}

推荐答案

您可以创建一个 DependencyProperty 或简单地扩展 ListBox 控件并改用您的新控件.

You could create a DependencyProperty or simply extend the ListBox control and use your new control instead.

public class ScrollingListBox : ListBox
{
    protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        int newItemCount = e.NewItems.Count; 

        if(newItemCount > 0) 
            this.ScrollIntoView(e.NewItems[newItemCount - 1]);

        base.OnItemsChanged(e);
    } 
}

在您的 XAML 中,添加类的命名空间:

In your XAML, add the class's namespace:

xmlns:custom="clr-namespace:ScrollingListBoxNamespace"

并用自定义的替换标准的 ListBox:

and swap out your standard ListBox with your custom one:

<custom:ScrollingListBox Margin="10" Height="150"
                         ItemsSource="{Binding Path=MyValue}" />

这篇关于使用 MVVM 将列表框滚动到视图中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆