Dot-Net
WPF:綁定到 ListBoxItem.IsSelected 不適用於螢幕外項目
在我的程序中,我有一組視圖模型對象來表示 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)); } }可以觀察到兩個不同的問題。
- 如果點擊第一個項目,然後按 Shift+End,則應選擇所有 200 個項目;但是,標題報告只選擇了 21 個項目。
- 如果點擊“全選”,則確實選擇了所有項目。如果您然後點擊 ListBox 中的一個項目,您會希望取消選擇其他 199 個項目,但這不會發生。相反,只有螢幕上的項目(以及其他一些項目)被取消選擇。除非您首先從頭到尾滾動列表,否則所有 199 個項目都不會被取消選擇(即使這樣,奇怪的是,如果您使用小滾動框執行滾動,它也不起作用)。
我的問題是:
- 有人可以準確解釋為什麼會發生這種情況嗎?
- 我可以避免或解決這個問題嗎?
ListBox預設情況下,UI 是虛擬化的。這意味著在任何給定時刻,ItemsSource實際上只會呈現其中的可見項目(以及“幾乎可見”項目的一小部分子集)。這就解釋了為什麼更新源會按預期工作(因為這些項目始終存在),但只是導航 UI 卻不能(因為這些項目的視覺表示是動態創建和銷毀的,並且永遠不會同時存在。)如果您想關閉此行為,一個選項是
ScrollViewer.CanContentScroll=False在您的ListBox. 這將啟用“平滑”滾動,並隱式關閉虛擬化。要明確禁用虛擬化,您可以設置VirtualizingStackPanel.IsVirtualizing=False.