2

簡潔にしておきます。私はItemTemplateを実装するListBoxを持っています。 DataTemplateにはチェックボックスがあります。私は約2000アイテムを積みます。最初の5項目を確認し、最後までスクロールして最後の5項目を選択します。最初の項目までスクロールし、最初の5つのチェック項目が変更されていることがわかりました。ItemsControlのアイテムのレンダリングに関する問題

<Window 
     x:Class="CheckItems.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:CheckItems" 
     Title="Window1" Height="300" Width="300" 
     > 
     <DockPanel> 
      <StackPanel DockPanel.Dock="Bottom" > 
       <Button Content="Test" Click="Button_Click"/> 
      </StackPanel> 
      <ListBox DockPanel.Dock="Left" 
       x:Name="users" 
       ItemsSource="{Binding Path=Users}" 
       > 
       <ListBox.ItemTemplate> 
        <DataTemplate> 
         <CheckBox> 
          <TextBlock Text="{Binding Path=Name}"/> 
         </CheckBox> 
        </DataTemplate> 
       </ListBox.ItemTemplate> 
      </ListBox> 
     </DockPanel> 
    </Window> 



    using System.Collections.Generic; 
    using System.ComponentModel; 
    using System.Windows; 
    namespace CheckItems 
    { 
     public partial class Window1 : Window 
     { 
      ViewModel controller; 

      public Window1() 
      { 
       DataContext = controller = new ViewModel(); 
       InitializeComponent(); 
       controller.Users = LoadData(); 
      } 

      private List<User> LoadData() 
      { 
       var newList = new List<User>(); 
       for (var i = 0; i < 2000; ++i) 
        newList.Add(new User { Name = "Name" + i, Age = 100 + i }); 
       return newList; 
      } 

      private void Button_Click(object sender, RoutedEventArgs e) 
      { } 
     } 

     public class User 
     { 
      public string Name { get; set; } 
      public int Age { get; set; } 
     } 


     public class ViewModel : INotifyPropertyChanged 
     { 
      private List<User> users; 
      public event PropertyChangedEventHandler PropertyChanged; 

      public List<User> Users 
      { 
       get { return users; } 
       set { users = value; NotifyChange("Users"); } 
      } 

      protected void NotifyChange(string propertyName) 
      { 
       if (PropertyChanged != null) 
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 
    } 

この他にも良い説明があります。これはMSバグです。これは.NET 3.5および4.0で発生します。 VirtualingStackPanel.IsVirtualizingがfalseに設定されている場合、この動作は発生しませんが、実際の現場では、仮想化を行わずにロードするのは苦労します。

いくつかの洞察はいいと思います。事前に

おかげで、

アンドレス・オリバレス

答えて

2

それでコントロールを再使用しており、単純にスクロールコントロールの背後にDataContextを置き換える仮想化されたパネル。これは、スクロールすると、その状態がDataContext内の何かにバインドされていない限り、コントロールの状態(IsCheckedなど)がリセットされることを意味します。

たとえば、一度に表示できる2000個のアイテムが10個だけの場合、WPFはそのうち約14個(スクロールバッファ用の余分なアイテム)をレンダリングし、スクロールして置き換えるときにこれらの14個のアイテムを再利用しますコントロールの後ろにDataContext

仮想化を無効にすると、このリサイクル動作が無効になります。これは、WPFが14個ではなく2000個のアイテムをレンダリングすることを意味します。そのため、パフォーマンスが非常に悪いのです。また、状態がリセットされていないため、チェックボックスがチェックされたままになることを意味します。

この問題を解決するには、IsSelectedプロパティをUserオブジェクトに追加し、CheckBox.IsCheckedをバインドすることをおすすめします。

+0

レイチェルの説明をありがとう。限り、あなたの提案限り、私は実際にUIの動作を記述するモデルにプロパティを配置する受け入れられたパターンであることを気に入らない。私はこの主張が非常に人気があることを理解していますが、これは合理的な解決策ではないと思います。 あなたが提供した説明に基づいて、私はむしろ、スクロール中のDataContextの紛失が失われたり復元されなかったソリューションを好むでしょう。 私のジレンマについてもう少し詳しく説明しなければならない、この努力についてもっと詳しく説明しますが、あなたの注意をそんなにありがとうございます。 –

+0

@AndresOlivares 'IsSelected'と' User'オブジェクトのプロパティを含むクラスを作成し、ViewModelで 'List 'を使う代わりにそれらのオブジェクトのリストを作成することができます。過去に私が使っていたもう一つの方法は、 'IsChecked'を' ListBoxItem.IsSelected'プロパティにバインドし、ListBoxが複数の項目を選択できるようにし、ListBoxの選択機能を隠すことです。これは、あなたが他の何かのために選択機能を使用していない場合にのみ機能します。 – Rachel

関連する問題