【问题标题】:Extended selection mode, virtualization and IsSelected binding扩展选择模式、虚拟化和 IsSelected 绑定
【发布时间】:2023-03-15 07:00:01
【问题描述】:

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

演示:

项目012989796 使用Control 选择。选择94(没有 Control KBD>!)选择计数器应该是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"

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

【问题讨论】:

  • 我怀疑项目的“选定”方面是启用虚拟化时您需要自己做的事情。虚拟化(至少从原生 Windows 而言)只是为了提供关于应该显示哪些项目的提示,而不是为您显示它。它与滚动条一起工作。这允许您的应用显示 1000 多个项目。可见的项目数通常保持不变(最后一页或项目数少于屏幕正常显示的项目数除外)。
  • 在我看来可视化模式设置为回收,所以它会重复使用选定的项目,您是否尝试过更改虚拟化模式,例如:VirtualizingPanel.VirtualizationMode="Standard"
  • @XAMlMAX,你可以复制mcve自己试试,说不定会找到解决办法。 VirtualizingMode="Standard" 没有任何改进(尝试使用 VirtualizingPanelVirtualizingStackPanel)。

标签: c# wpf mvvm selection virtualization


【解决方案1】:

嗯,这是预期的行为。虚拟化只为可见项目创建视觉容器 (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();
    }
    

【讨论】:

  • 从范围之外的选择中取消选择最后一项是否也是预期行为?我无法解释,你可以吗?
  • @Sinatr,预计无法正常工作。为什么它不能以这种特殊的方式工作我不知道,我只能推测。 :) 它可能与如何以及何时清理 ListViewItems 有关。如果我猜的话,我会说最后一个选定的项目容器以某种方式被SelectedItem 属性“持有”,因此当您离开它时它不会被破坏(与当前选择中的所有其他项目不同)。跨度>
  • 我明白了。我忽略了您最初使用添加/删除项目的建议,这很有用,目前正试图使其适用于 CTR+A 而不会获得 StackOverflowException 添加项目。
  • @Sinatr 我发布的代码在我的机器上正常工作,我不知道什么会导致堆栈溢出,没有递归。确保从 xaml 中删除 IsSelected 绑定。
  • 我不想移除绑定(ViewModel 需要它来恢复选择)。而且您的解决方案比我的更好(删除它)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-08
  • 1970-01-01
相关资源
最近更新 更多