2013-05-31 27 views
9

Tôi có một vấn đề khá đơn giản, nhưng tôi không thể tìm ra cách để crack nó bằng MVVM.ListBox Scroll Into View với MVVM

Tôi có một số ListBox bị ràng buộc với số ObservableCollection<string>.

Tôi chạy một quy trình sẽ thêm toàn bộ các mục vào bộ sưu tập và do đó chúng được hiển thị trong ListBox.

Vấn đề là khi các mục được thêm vào hộp danh sách ... thanh cuộn chỉ phát triển, nhưng tôi không thể tìm ra cách làm cho nó ScrollIntoView cho mỗi mục được thêm vào bộ sưu tập.

Mã mẫu này minh họa sự cố một cách hoàn hảo.

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> 

Xem Mẫu

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

}

Trả lời

17

Bạn có thể tạo một DependencyProperty hay đơn giản là mở rộng ListBox kiểm soát và sử dụng điều khiển mới của bạn để thay thế.

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

Trong XAML của bạn, thêm namespace của lớp:

xmlns:custom="clr-namespace:ScrollingListBoxNamespace" 

và trao đổi trên tiêu chuẩn ListBox của bạn với tùy chỉnh của bạn một:

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

Rất cảm ơn ...! –

+0

@BOBINJOSEPH Bạn được chào đón :) – keyboardP

+0

@keyboard Tôi biết đã 3 năm kể từ khi bạn trả lời câu hỏi này, nhưng bất kỳ cơ hội nào bạn sẽ mở rộng câu trả lời và giải thích cách tùy chọn thuộc tính phụ thuộc sẽ hoạt động như thế nào? Cảm ơn. – Thierry

9

Bạn cũng có thể thêm một hành vi:

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> 

Và thực hiện hành vi:

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]); 
     } 
    } 
} 
Các vấn đề liên quan