扩展选择模式、虚拟化和 IsSelected 绑定
Extended selection mode, virtualization and IsSelected binding
似乎在扩展选择模式下 IsSelected
绑定有问题。看起来只有最后一项 超出范围 的选择得到了正确处理。
示范:
项目 0
、1
、2
和 98
、97
、96
已通过 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();
}
}
快速修复是禁用列表控制(ListBox
或 ListView
)虚拟化:
VirtualizingStackPanel.IsVirtualizing="False"
问题:知道如何在不禁用虚拟化的情况下修复它吗?
好吧,这是预期的行为。虚拟化仅为可见项目创建视觉容器 (ListBoxItem
)。为了使绑定起作用,容器必须首先存在,因此只有可见的项目会受到影响。
有两个明显的解决方案:
- 禁用虚拟化。
改用 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();
}
似乎在扩展选择模式下 IsSelected
绑定有问题。看起来只有最后一项 超出范围 的选择得到了正确处理。
示范:
项目 0
、1
、2
和 98
、97
、96
已通过 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();
}
}
快速修复是禁用列表控制(ListBox
或 ListView
)虚拟化:
VirtualizingStackPanel.IsVirtualizing="False"
问题:知道如何在不禁用虚拟化的情况下修复它吗?
好吧,这是预期的行为。虚拟化仅为可见项目创建视觉容器 (ListBoxItem
)。为了使绑定起作用,容器必须首先存在,因此只有可见的项目会受到影响。
有两个明显的解决方案:
- 禁用虚拟化。
改用
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(); }