2010-02-18 5 views
23

세로 스크롤이 가능한 큰 ListBox가 있고 MVVM에 새로 만들기 및 ICommand 편집이 있습니다. 컬렉션의 끝에 새 항목을 추가하고 있지만 내 MVVM-AddCommand를 호출하면 스크롤바가 끝까지 자동으로 위치되기를 바랍니다. 또한 응용 프로그램의 다른 부분에서 EditCommand를 호출하여 항목을 편집 할 수 있도록하여 DataTrigger를 사용하여 모드를 편집하는 ListBoxItem을 가져 오지만 특정 행 (ListBoxItem)을 스크롤 위치를 조정하여보기.MVVM WPF 응용 프로그램에서 ListBox의 스크롤 위치를 제어하는 ​​방법

내가보기 측면에서하고 있다면 listBox.ScrollInToView (lstBoxItem)를 호출 할 수 있습니다. 그러나 MVVM 관점에서 일반적인 스크롤 문제를 해결하는 가장 좋은 방법은 무엇입니까?

+0

ListBox SelectionChanged 이벤트 및 ScrollIntoView 메서드를 사용하면 MVVM이 중단되지 않습니다. 이는 전적으로보기 기능이므로보기에서 처리해야합니다. 뷰 모델은 ListBox가 있는지 또는 뷰에서 개체가있는 위치를 제어하지 않아야합니다. 뷰 모델이 수행해야하는 유일한 작업은 SelectedItem을 변경하는 것입니다. SelectedItem은 ListBox의 뷰 모델에 대한 바운드 프로퍼티 여야합니다. – Tim

답변

27

나는 일반적으로 에 IsSynchronizedWithCurrentItem="True"을 설정합니다. 그리고 나는 SelectionChanged 처리기를 추가하고 항상보기에 선택한 항목을 가지고, 코드를 다음과 같이 :

private void BringSelectionIntoView(object sender, SelectionChangedEventArgs e) 
    { 
     Selector selector = sender as Selector; 
     if (selector is ListBox) 
     { 
      (selector as ListBox).ScrollIntoView(selector.SelectedItem); 
     } 
    } 

을 내 VM에서 나는 기본 모음보기를 얻을 수있는 아이템이되고 있음을 보장하기 위해 MoveCurrent*() 방법 중 하나를 사용 편집 된 항목이 현재 항목입니다.

CollectionViewSource.GetDefaultView(_myCollection).MoveCurrentTo(thisItem); 

참고 : MVVM이 사용 가상화에게

+0

예 내 ListView가 가상화되었습니다. 답변을 주셔서 감사합니다. –

7

을 수용 할 ListBox.ScrollIntoView()를 사용하여 편집 쉽게과 같이 첨부 된 행동을 통해 수행 할 수 있습니다

using System.Windows.Controls; 
using System.Windows.Interactivity; 

namespace Jarloo.Sojurn.Behaviors 
{ 
    public sealed class ScrollIntoViewBehavior : Behavior<ListBox> 
    { 
     protected override void OnAttached() 
     { 
      base.OnAttached(); 
      AssociatedObject.SelectionChanged += ScrollIntoView; 
     } 

     protected override void OnDetaching() 
     { 
      AssociatedObject.SelectionChanged -= ScrollIntoView; 
      base.OnDetaching(); 
     } 

     private void ScrollIntoView(object o, SelectionChangedEventArgs e) 
     { 
      ListBox b = (ListBox) o; 
      if (b == null) 
       return; 
      if (b.SelectedItem == null) 
       return; 

      ListBoxItem item = (ListBoxItem) ((ListBox) o).ItemContainerGenerator.ContainerFromItem(((ListBox) o).SelectedItem); 
      if (item != null) item.BringIntoView(); 
     } 
    } 
} 

를 다음보기 광고이 참조에에 상단 :

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

그리고 :

<ListBox ItemsSource="{Binding MyData}" SelectedItem="{Binding MySelectedItem}"> 
     <i:Interaction.Behaviors> 
      <behaviors:ScrollIntoViewBehavior /> 
     </i:Interaction.Behaviors> 
</ListBox> 

이제 SelectedItem이 변경되면 동작이 BringIntoView() 호출을 대신 수행합니다. 위의 코드는 당신을 위해 작동하지 않는 경우

1

는 XAML

<ListBox IsSynchronizedWithCurrentItem="True" extenders:ListBoxExtenders.AutoScrollToCurrentItem="True" ..../> 
3

에서 시도이

public class ListBoxExtenders : DependencyObject 
{ 
    public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged)); 

    public static bool GetAutoScrollToCurrentItem(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(AutoScrollToSelectedItemProperty); 
    } 

    public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value) 
    { 
     obj.SetValue(AutoScrollToSelectedItemProperty, value); 
    } 

    public static void OnAutoScrollToCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) 
    { 
     var listBox = s as ListBox; 
     if (listBox != null) 
     { 
      var listBoxItems = listBox.Items; 
      if (listBoxItems != null) 
      { 
       var newValue = (bool)e.NewValue; 

       var autoScrollToCurrentItemWorker = new EventHandler((s1, e2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition)); 

       if (newValue) 
        listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker; 
       else 
        listBoxItems.CurrentChanged -= autoScrollToCurrentItemWorker; 
      } 
     } 
    } 

    public static void OnAutoScrollToCurrentItem(ListBox listBox, int index) 
    { 
     if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0) 
      listBox.ScrollIntoView(listBox.Items[index]); 
    } 

} 

사용을 줄이있는 허용 대답의 연결된 속성 양식 :

using System.Windows; 
using System.Windows.Controls; 

namespace CommonBehaviors 
{ 
    public static class ScrollCurrentItemIntoViewBehavior 
    { 
     public static readonly DependencyProperty AutoScrollToCurrentItemProperty = 
      DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", 
       typeof(bool), typeof(ScrollCurrentItemIntoViewBehavior), 
       new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged)); 

     public static bool GetAutoScrollToCurrentItem(DependencyObject obj) 
     { 
      return (bool)obj.GetValue(AutoScrollToCurrentItemProperty); 
     } 

     public static void OnAutoScrollToCurrentItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
     { 
      var listBox = obj as ListBox; 
      if (listBox == null) return; 

      var newValue = (bool)e.NewValue; 
      if (newValue) 
       listBox.SelectionChanged += listBoxSelectionChanged; 
      else 
       listBox.SelectionChanged -= listBoxSelectionChanged; 
     } 

     static void listBoxSelectionChanged(object sender, SelectionChangedEventArgs e) 
     { 
      var listBox = sender as ListBox; 
      if (listBox == null || listBox.SelectedItem == null || listBox.Items == null) return; 

      listBox.Items.MoveCurrentTo(listBox.SelectedItem); 
      listBox.ScrollIntoView(listBox.SelectedItem); 
     } 

     public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value) 
     { 
      obj.SetValue(AutoScrollToCurrentItemProperty, value); 
     } 
    } 
} 
관련 문제