wpf 使用 MVVM 将列表框滚动到视图中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16866309/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ListBox Scroll Into View with MVVM
提问by imdandman
I have what is a pretty simple problem, but I can't figure out how to crack it using MVVM.
我有一个非常简单的问题,但我不知道如何使用 MVVM 破解它。
I have a ListBoxthat is bound to an ObservableCollection<string>.
我有一个ListBox绑定到ObservableCollection<string>.
I run a process that will add a whole bunch of items to the collection and they are therefore shown in the ListBox.
我运行了一个过程,将向集合中添加一大堆项目,因此它们显示在ListBox.
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 ScrollIntoViewfor each item added to the collection.
问题是,当项目添加到列表框时......滚动条只会增长,但我似乎无法弄清楚如何为ScrollIntoView添加到集合中的每个项目制作它。
This sample code illustrates the problem perfectly.
此示例代码完美地说明了该问题。
XAML
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>
View Model
查看模型
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());
}
}
}
}
回答by keyboardP
You could create a DependencyPropertyor simply extend the ListBoxcontrol and use your new control instead.
您可以创建一个DependencyProperty或简单地扩展ListBox控件并改用新控件。
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);
}
}
In your XAML, add the class's namespace:
在您的 XAML 中,添加类的命名空间:
xmlns:custom="clr-namespace:ScrollingListBoxNamespace"
and swap out your standard ListBoxwith your custom one:
并ListBox用您的自定义标准换掉您的标准:
<custom:ScrollingListBox Margin="10" Height="150"
ItemsSource="{Binding Path=MyValue}" />
回答by Tal Segal
You can also add a Behavior:
您还可以添加行为:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
.
.
.
<ListBox Margin="10" Height="150" ItemsSource="{Binding Path=MyValue}" >
<i:Interaction.Behaviors>
<bhv:ScrollIntoViewBehavior/>
</i:Interaction.Behaviors>
</ListBox>
And implement the behavior:
并实现行为:
using System.Windows.Interactivity;
public class ScrollIntoViewBehavior : Behavior<ListBox>
{
protected override void OnAttached()
{
ListBox listBox = AssociatedObject;
((INotifyCollectionChanged)listBox.Items).CollectionChanged += OnListBox_CollectionChanged;
}
protected override void OnDetaching()
{
ListBox listBox = AssociatedObject;
((INotifyCollectionChanged)listBox.Items).CollectionChanged -= OnListBox_CollectionChanged;
}
private void OnListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ListBox listBox = AssociatedObject;
if (e.Action == NotifyCollectionChangedAction.Add)
{
// scroll the new item into view
listBox.ScrollIntoView(e.NewItems[0]);
}
}
}

