2009-05-13 14 views
11

WPF ListBoxObservableCollectionにバインドされています。コレクションにアイテムが追加されると、ListBoxスクロール位置は追加されたエントリのサイズだけシフトします。スクロール位置を保持できるようにしたいので、リストに項目が追加されても、現在表示されている項目は移動しません。これを達成する方法はありますか?コレクションが変更されたときにバインドされたWPFリストボックスのスクロール位置を保持する

+0

私はちょうどリストボックスにデータバインディングを本当に簡単にしました。アイテムを追加すると、表示されていたアイテムが表示されました...ほかに何をしていますか? – TheCodeMonk

+0

新しいアイテムをコレクションのビニングに追加してみてください。新しいアイテムが追加されると、表示しているアイテムがビューポートから移動し始めます。 1つのアイテム - ビューポートステーを同じ位置(インデックスベース)に追加すると、1つのアイテムがビューポートに入力され、1つのアイテムがそのアイテムを残したことを意味します。 – EvAlex

答えて

0

オーバーライドがあるかどうかはわかりませんが、疑問です。

マニュアルバインディングを使用している疑いがある場合は、 : - }真剣に、デフォルトのバインディングの振る舞いは - よく設定されているので、特別な振る舞いが必要な場合は手作業によるバインディングがより良い選択です。手動バインディングを使用すると、スクロール位置を保存し、アイテムを追加した後にスクロール位置をリセットすることができます。

+0

これは明白な疑問につながります。現在のスクロール位置をどのように決定すればよいですか? –

1

CollectionViewを作成します。 これを試してみてください:

private ObservableCollection<int> m_Values; 
    private CollectionView m_View; 

    private void Bind() 
    { 
     m_Values = new ObservableCollection<int>(); 
     m_View = new CollectionView(m_Values); 
     MyListBox.ItemsSource = m_View; 
    } 

    private void MyListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     Debug.WriteLine(m_View.CurrentItem); 
     Debug.WriteLine(m_View.CurrentPosition); 
    } 
+1

さらに説明してください。この回答は問題を解決するようではありません - 単に現在のアイテムと位置を報告します。 ListBoxを使ってItemsPanelをVirtualizingStackPanelに設定して同じ問題を抱えています。私が見ている問題は、Observableコレクションに新しい項目が追加されても、リストボックスの項目が移動し続けることです。新しいアイテムが追加された後(たとえば、SelectedIndexを使用して)、アイテムを元の位置に戻すことは可能ですが、これによって動きがぎくしゃくします。新しいアイテムが追加されるときにスクロール位置が最初から変更されないようにする方法が必要です。 – CyberMonk

+0

私は同じ問題を抱えていますが、あなたの解決策はsomethigが選択されていることを前提としています。しかし、ユーザーが何かを選択したくない場合はどうなりますか?どこかにスクロールして放置するだけです。 – EvAlex

0

ものが追加されたとき、あなたがコントロールしますか?つまり、データが変更されるポイントが1つまたは2つ定義されていますか?もしそうなら、簡単な解決法(おそらくエレガントではない)は、現行のListBox.SelectedIndexを保持するローカルint変数を持つでしょう。データが変更される直前にselectedIndexをこの変数に保存し、データが追加されたらlistBoxのSelectedIndexをこの変数の値に設定します。

1

まずListBox.SelectedIndexで現在の位置を見つけ、ListBox.ScrollIntoView(/ * Current Index * /)を使用します。新しい項目がリストに追加されても、現在の位置とビューは同じになります。

3

私は同じ問題に直面しました。もう1つの制限がありました。ユーザーはアイテムを選択せず​​、スクロールするだけです。だからこそ、ScrollIntoView()メソッドは役に立ちません。ここに私の解決策があります。

まず、私はbool型の添付依存関係プロパティPreserveScrollとDependencyObjectからそれを派生クラスScrollPreserverを、作成した:

public class ScrollPreserver : DependencyObject 
{ 
    public static readonly DependencyProperty PreserveScrollProperty = 
     DependencyProperty.RegisterAttached("PreserveScroll", 
      typeof(bool), 
      typeof(ScrollPreserver), 
      new PropertyMetadata(new PropertyChangedCallback(OnScrollGroupChanged))); 

    public static bool GetPreserveScroll(DependencyObject invoker) 
    { 
     return (bool)invoker.GetValue(PreserveScrollProperty); 
    } 

    public static void SetPreserveScroll(DependencyObject invoker, bool value) 
    { 
     invoker.SetValue(PreserveScrollProperty, value); 
    } 

    ... 
} 

プロパティがコールバックを変更するには、プロパティがScrollViewerので設定されている前提としています。コールバックメソッドは、プライベート辞書にこのScrollViewerのを追加し、それにScrollChangedイベントハンドラを追加します。

private static Dictionary<ScrollViewer, bool> scrollViewers_States = 
    new Dictionary<ScrollViewer, bool>(); 

private static void OnScrollGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    ScrollViewer scrollViewer = d as ScrollViewer; 
    if (scrollViewer != null && (bool)e.NewValue == true) 
    { 
     if (!scrollViewers_States.ContainsKey(scrollViewer)) 
     { 
      scrollViewer.ScrollChanged += new ScrollChangedEventHandler(scrollViewer_ScrollChanged); 
      scrollViewers_States.Add(scrollViewer, false); 
     } 
    } 
} 

この辞書は、このクラスを使用するアプリケーション内のすべてのScrollViewersへの参照を保持します。私の場合、アイテムはコレクションの最初に追加されます。問題は、ビューポート位置が変わらないことです。位置が0のときは問題ありません。ビューポートの最初の要素は常に最初の項目になります。しかし、ビューポートの最初の要素に0以外のインデックスがあり、新しい項目が追加されると、その要素のインデックスが増加してスクロールダウンします。だから、何もする必要がない0の場合はbool値は、垂直ScrollViewerの者がオフセットするかどうかを0にされていないことを示し、それが0でない場合には、ビューポート内のアイテムとの相対位置を維持するために必要である:

static void scrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) 
{ 
    if (scrollViewers_States[sender as ScrollViewer]) 
     (sender as ScrollViewer).ScrollToVerticalOffset(e.VerticalOffset + e.ExtentHeightChange); 

    scrollViewers_States[sender as ScrollViewer] = e.VerticalOffset != 0; 
} 

ExtentHeightChangeは、追加されたアイテムの量を示すためにここで使用されます。私の場合、アイテムはコレクションの先頭にのみ追加されるので、ScrollViewerのVerticalOffsetをこの値だけ大きくするだけです。 最後に、使用法です。 ListBoxの例は次のとおりです。

<Listbox ...> 
    <ListBox.Resourses> 
     <Style TargetType="ScrollViewer"> 
      <Setter Property="local:ScrollPreserver.PreserveScroll" Value="True" /> 
     </Style> 
    </ListBox.Resourses> 
</ListBox> 

Ta-da!仕事が素敵:)

+0

静的なフィールドの辞書のためにメモリリークがありませんか? – TcKs

+0

DependencyPropertiesはすべて静的です。たとえば、次のように読むことができます:http://stackoverflow.com/questions/2989431/why-are-dependency-properties-static – EvAlex

+0

私は知っています。しかし、 "scrollViewers_States"フィールドは、依存関係プロパティのショートカットではありません。 ScrollViewerのインスタンスを格納します。古いインスタンスを辞書から削除するコードがありますか? – TcKs

関連する問題