Dot-Net

WPF:綁定到 ListBoxItem.IsSelected 不適用於螢幕外項目

  • February 3, 2018

在我的程序中,我有一組視圖模型對象來表示 ListBox 中的項目(允許多選)。viewmodel 有一個 IsSelected 屬性,我想將其綁定到 ListBox,以便在 viewmodel 中而不是在列錶框本身中管理選擇狀態。

但是,顯然 ListBox 不維護大多數螢幕外項目的綁定,因此通常 IsSelected 屬性未正確同步。這是一些展示問題的程式碼。第一個 XAML:

<StackPanel>
   <StackPanel Orientation="Horizontal">
       <TextBlock>Number of selected items: </TextBlock>
       <TextBlock Text="{Binding NumItemsSelected}"/>
   </StackPanel>
   <ListBox ItemsSource="{Binding Items}" Height="200" SelectionMode="Extended">
       <ListBox.ItemContainerStyle>
           <Style TargetType="{x:Type ListBoxItem}">
               <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
           </Style>
       </ListBox.ItemContainerStyle>
   </ListBox>
   <Button Name="TestSelectAll" Click="TestSelectAll_Click">Select all</Button>
</StackPanel>

C# 全選處理程序:

private void TestSelectAll_Click(object sender, RoutedEventArgs e)
{
   foreach (var item in _dataContext.Items)
       item.IsSelected = true;
}

C# 視圖模型:

public class TestItem : NPCHelper
{
   TestDataContext _c;
   string _text;
   public TestItem(TestDataContext c, string text) { _c = c; _text = text; }

   public override string ToString() { return _text; }

   bool _isSelected;
   public bool IsSelected
   {
       get { return _isSelected; }
       set {
           _isSelected = value; 
           FirePropertyChanged("IsSelected");
           _c.FirePropertyChanged("NumItemsSelected");
       }
   }
}
public class TestDataContext : NPCHelper
{
   public TestDataContext()
   {
       for (int i = 0; i < 200; i++)
           _items.Add(new TestItem(this, i.ToString()));
   }
   ObservableCollection<TestItem> _items = new ObservableCollection<TestItem>();
   public ObservableCollection<TestItem> Items { get { return _items; } }

   public int NumItemsSelected { get { return _items.Where(it => it.IsSelected).Count(); } }
}
public class NPCHelper : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;
   public void FirePropertyChanged(string prop)
   {
       if (PropertyChanged != null)
           PropertyChanged(this, new PropertyChangedEventArgs(prop));
   }
}

可以觀察到兩個不同的問題。

  1. 如果點擊第一個項目,然後按 Shift+End,則應選擇所有 200 個項目;但是,標題報告只選擇了 21 個項目。
  2. 如果點擊“全選”,則確實選擇了所有項目。如果您然後點擊 ListBox 中的一個項目,您會希望取消選擇其他 199 個項目,但這不會發生。相反,只有螢幕上的項目(以及其他一些項目)被取消選擇。除非您首先從頭到尾滾動列表,否則所有 199 個項目都不會被取消選擇(即使這樣,奇怪的是,如果您使用小滾動框執行滾動,它也不起作用)。

我的問題是:

  1. 有人可以準確解釋為什麼會發生這種情況嗎?
  2. 我可以避免或解決這個問題嗎?

ListBox預設情況下,UI 是虛擬化的。這意味著在任何給定時刻,ItemsSource實際上只會呈現其中的可見項目(以及“幾乎可見”項目的一小部分子集)。這就解釋了為什麼更新會按預期工作(因為這些項目始終存在),但只是導航 UI 卻不能(因為這些項目的視覺表示是動態創建和銷毀的,並且永遠不會同時存在。)

如果您想關閉此行為,一個選項是ScrollViewer.CanContentScroll=False在您的ListBox. 這將啟用“平滑”滾動,並隱式關閉虛擬化。要明確禁用虛擬化,您可以設置VirtualizingStackPanel.IsVirtualizing=False.

引用自:https://stackoverflow.com/questions/7097999