2016-07-11 7 views
13

私はListView(仮想化はデフォルトでオン)です。ItemsSourceObservableCollection<Item>プロパティにバインドされています。仮想化を使用したScrollIntoViewとListView

データが入力されたとき(プロパティが設定され、通知が発生したとき)、プロファイラで2つのレイアウトスパイクが表示され、listView.ScrollIntoView()の後に2番目のものが発生します。

私の理解では、次のとおりです。の結合を介して

  1. ListViewデータをロードすると、その後、私はlistView.ScrollIntoView()呼び出すインデックス0
  2. から始めて、画面上の項目のためのListViewItemを作成します。
  3. そして、今度はListViewが作成されます(ListViewItem秒を作成します)。

この非仮想化が2回起こらないようにするにはどうすればよいですか(ScrollIntoViewが発生する前には、1つは望ましくありません)。


私はListBoxを使用してREPROを作ってみました。

XAML:

<Grid> 
    <ListBox x:Name="listBox" ItemsSource="{Binding Items}"> 
     <ListBox.ItemContainerStyle> 
      <Style TargetType="ListBoxItem"> 
       <Setter Property="IsSelected" Value="{Binding IsSelected}" /> 
      </Style> 
     </ListBox.ItemContainerStyle> 
    </ListBox> 
    <Button Content="Fill" VerticalAlignment="Top" HorizontalAlignment="Center" Click="Button_Click" /> 
</Grid> 

CS:

public class NotifyPropertyChanged : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    public void OnPropertyChanged([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); 
} 

public class ViewModel : NotifyPropertyChanged 
{ 
    public class Item : NotifyPropertyChanged 
    { 
     bool _isSelected; 
     public bool IsSelected 
     { 
      get { return _isSelected; } 
      set 
      { 
       _isSelected = value; 
       OnPropertyChanged(); 
      } 
     } 
    } 

    ObservableCollection<Item> _items = new ObservableCollection<Item>(); 
    public ObservableCollection<Item> Items 
    { 
     get { return _items; } 
     set 
     { 
      _items = value; 
      OnPropertyChanged(); 
     } 
    } 
} 

public partial class MainWindow : Window 
{ 
    ViewModel _vm = new ViewModel(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = _vm; 
    } 

    void Button_Click(object sender, RoutedEventArgs e) 
    { 
     var list = new List<ViewModel.Item>(1234567); 
     for (int i = 0; i < 1234567; i++) 
      list.Add(new ViewModel.Item()); 
     list.Last().IsSelected = true; 
     _vm.Items = new ObservableCollection<ViewModel.Item>(list); 
     listBox.ScrollIntoView(list.Last()); 
    } 
} 

デバッグ - パフォーマンス・プロファイラ - アプリケーションのタイムライン...少し待って、ボタンをクリックし、ウィンドウを閉じるを少し待ちます。 VirtualizingStackPanelで2つのレイアウトパスが表示されます。私の目的はただ一つ持っていることで、私はどのように知りません。

reproの問題は、負荷をシミュレートすることです( is expensiveを作成するとき)。しかし、これで問題がより明確に示されることを願っています。

+0

アイテムのコレクションがUIに追加されたら、最後のアイテムを常に表示したいですか? 2つのレイアウトパスの問題は解決しませんが、そのような場合は、UIにアイテムを追加する前にアイテムのソート方法を変更して、表示するアイテムをコレクションの最初のアイテムにすることができます – Bijington

+0

@Bijington、コレクションはすでにソートされています(日付別、mcveには表示されていません)、注文を変更しないでください。どの項目でもかまいません(実際のプロジェクトでは、最後に選択した項目にスクロールして、複数選択の 'ListView'を実行します)。理想的には、MVVMアプリケーション(選択が処理されますが、スクロール位置は、何とか* ScrollIntoViewで処理されます)で 'ListView'状態を保存/復元する方法を模索しています。問題(ただし、 'ScrollIntoView'では大丈夫ですが、仮想化が2回だけ問題になります)。 – Sinatr

+0

私はそれほど多くを前提としましたが、尋ねる価値があると思いました。これは確かに解決する興味深い問題のように聞こえる。 'ListView'にスクロール位置を伝えてから、内容をロードする必要があります。ロード・パス(おそらくIsLoadingContentフラグ)を終えるまで、何らかの形で 'ListView'をサブクラス化してレンダリングしないようにして、アイテムを割り当て、選択して表示する必要があるマークを付けることができますか? – Bijington

答えて

0

VirtualizingStackPanelでは、Scrollメソッドがうまく機能しません。その問題を回避するために、私は以下のソリューションを使用します。

  1. ディッチVirtualizingStackPanel。パネルテンプレート用の通常のStackPanelを使用します。
  2. DataTemplateの外側のレイヤーをここからのLazyControlにします。http://blog.angeloflogic.com/2014/08/lazycontrol-in-junglecontrols.html
  3. LazyControlに高さを設定してください。

私は一般的にそのアプローチから優れたパフォーマンスを得ています。あなたが求めているとおりにするために、(スクロールメソッドを呼び出した後に)いくつかのフラグがセットされるのを待つために、LazyControlに追加のロジックを追加する必要があります。

関連する問題