扩展选择模式、虚拟化和 IsSelected 绑定

Extended selection mode, virtualization and IsSelected binding

似乎在扩展选择模式下 IsSelected 绑定有问题。看起来只有最后一项 超出范围 的选择得到了正确处理。

示范:

项目 012989796 已通过 Control 选择。选择94时(没有控制!)选择计数器应为1,但是您看到3。向上滚动显示只有一项(最后一项)超出范围的选择项未被选中。

下面是mcve:

xaml:

<ListBox ItemsSource="{Binding Items}" SelectionMode="Extended" SelectionChanged="ListBox_SelectionChanged">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Text}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="IsSelected" Value="{Binding IsSelected}" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

cs:

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

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

    public string Text { get; set; }
}

public class ViewModel : NotifyPropertyChanged
{
    public ObservableCollection<Item> Items { get; }

    public ViewModel()
    {
        var list = new List<Item>();
        for (int i = 0; i < 100; i++)
            list.Add(new Item() { Text = i.ToString() });
        Items = new ObservableCollection<Item>(list);
    }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }

    void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        Title = ((ViewModel)DataContext).Items.Count(item => item.IsSelected).ToString();
    }
}

快速修复是禁用列表控制(ListBoxListView)虚拟化:

VirtualizingStackPanel.IsVirtualizing="False"

问题:知道如何在不禁用虚拟化的情况下修复它吗?

好吧,这是预期的行为。虚拟化仅为可见项目创建视觉容器 (ListBoxItem)。为了使绑定起作用,容器必须首先存在,因此只有可见的项目会受到影响。

有两个明显的解决方案:

  1. 禁用虚拟化。
  2. 改用 SelectionChanged 事件。您可以从 SelectionChangedEventArgs 获取添加和删除的项目。然后您需要做的就是执行转换并相应地设置 IsSelected 属性(您不需要迭代 Items)。 Ctrl+A 也可以,你只需要处理添加的项目(并完全删除绑定):

    void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        foreach (var removedItem in e.RemovedItems.Cast<Item>())
        {
            removedItem.IsSelected = false;
        }
        foreach (var addedItem in e.AddedItems.Cast<Item>())
        {
            addedItem.IsSelected = true;
        }
        Title = ((ViewModel) DataContext).Items.Count(item => item.IsSelected).ToString();
    }