2013-05-31 3 views
9

Ich habe was ist ein ziemlich einfaches Problem, aber ich kann nicht herausfinden, wie man es mit MVVM knacken.ListBox Scroll in Sicht mit MVVM

Ich habe eine ListBox, die an eine ObservableCollection<string> gebunden ist.

Ich führe einen Prozess, der eine ganze Reihe von Elementen zur Sammlung hinzufügen wird und sie daher in der ListBox angezeigt werden.

Das Problem ist, dass wenn die Elemente zum Listenfeld hinzugefügt werden ... die Bildlaufleiste nur wächst, aber ich kann nicht herausfinden, wie es ScrollIntoView für jedes Element der Sammlung hinzugefügt wird.

Dieser Beispielcode veranschaulicht das Problem perfekt.

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> 

Ansicht Modell

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

}

Antwort

17

Sie eine DependencyProperty schaffen könnte oder einfach die ListBox Steuerung erweitern und Ihre neue Steuerung stattdessen verwenden.

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 XAML, fügen Sie die Namespace-Klasse:

xmlns:custom="clr-namespace:ScrollingListBoxNamespace" 

und tauschen Sie Ihren Standard ListBox mit Ihrer Gewohnheit ein aus:

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

Sehr vielen Dank ...! –

+0

@BOBINJOSEPH Gern geschehen :) – keyboardP

+0

@keyboard Ich weiß, dass es 3 Jahre her ist, seit du diese Frage beantwortet hast, aber jede Chance, deine Antwort zu erweitern und zu erklären, wie die Abhängigkeitseigenschaft funktionieren würde? Vielen Dank. – Thierry

9

Sie auch ein Verhalten hinzufügen:

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> 

Und implementieren Sie das Verhalten:

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]); 
     } 
    } 
} 
Verwandte Themen