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的面板。這意味着當您滾動時,控件的狀態(例如IsChecked)會被重置,除非該狀態綁定到DataContext中的某些內容。

例如,如果一次只能看到10個2000的項目,那麼WPF將只渲染其中的14個(用於滾動緩衝區的額外項目),並且在滾動和替換時簡單地重用這14個項目控件後面的DataContext

如果禁用虛擬化,則禁用此回收行爲。這意味着WPF將呈現2000個項目而不是14個,這就是爲什麼表現如此糟糕。這也意味着CheckBoxes將保持被檢查狀態,因爲它們的狀態沒有被重置。

要解決這個問題,我建議添加一個IsSelected屬性到您的用戶對象並綁定CheckBox.IsChecked它。

+0

謝謝你的解釋Rachel。至於你的建議,我真的不喜歡它是一種可接受的模式,將屬性放在描述UI上的行爲的模型上。我明白這個建議非常受歡迎,但我不認爲這是一個合理的解決方案。 根據你提供的解釋,我寧願更喜歡一個解決方案,在滾動期間丟失DataContext不會丟失或恢復。 我會發布更多關於這種努力,這應該更多地解釋我的困境,但非常感謝您的關注。 – 2012-01-05 19:42:44

+0

@AndresOlivares你可以創建一個包含IsSelected和User對象屬性的類,然後在ViewModel中創建這些對象的List,而不是使用List List 。我過去使用的另一種選擇是將'IsChecked'綁定到'ListBoxItem.IsSelected'屬性,允許ListBox選擇多個項目,並隱藏ListBox的選擇功能。這隻會在你不使用選擇功能的時候起作用。 – Rachel 2012-01-05 20:04:31