WPF:绑定到 ListBoxItem.IsSelected 不适用于屏幕外项目 [英] WPF: Binding to ListBoxItem.IsSelected doesn't work for off-screen items

查看:23
本文介绍了WPF:绑定到 ListBoxItem.IsSelected 不适用于屏幕外项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的程序中,我有一组视图模型对象来表示 ListBox 中的项目(允许多选).viewmodel 有一个 IsSelected 属性,我想将其绑定到 ListBox,以便在 viewmodel 中而不是在列表框本身中管理选择状态.

In my program I have a set of view-model objects to represent items in a ListBox (multi-select is allowed). The viewmodel has an IsSelected property that I would like to bind to the ListBox so that selection state is managed in the viewmodel rather than in the listbox itself.

但是,显然 ListBox 不维护大多数屏幕外项目的绑定,因此通常 IsSelected 属性未正确同步.这是一些演示问题的代码.第一个 XAML:

However, apparently the ListBox doesn't maintain bindings for most of the off-screen items, so in general the IsSelected property is not synchronized correctly. Here is some code that demonstrates the problem. First 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));
    }
}

可以观察到两个不同的问题.

Two separate problems can be observed.

  1. 如果单击第一个项目,然后按 Shift+End,则应选择所有 200 个项目;但是,标题报告只选择了 21 个项目.
  2. 如果您单击全选",则确实选择了所有项目.如果您然后单击 ListBox 中的一个项目,您会希望取消选择其他 199 个项目,但这不会发生.相反,只有屏幕上的项目(以及其他一些项目)被取消选择.除非您首先从头到尾滚动列表,否则所有 199 个项目都不会被取消选择(即使这样,奇怪的是,如果您使用小滚动框执行滚动,它也不起作用).

我的问题是:

  1. 有人能准确解释为什么会这样吗?
  2. 我可以避免或解决这个问题吗?

推荐答案

ListBox 默认情况下是 UI 虚拟化的.这意味着在任何给定时刻,只有 ItemsSource 中的可见项目(以及几乎可见"项目的一小部分)将实际呈现.这就解释了为什么更新 source 可以按预期工作(因为这些项目始终存在),但只是导航 UI 却不能(因为这些项目的视觉表示是动态创建和销毁的,并且永远不会同时存在.)

ListBox is, by default, UI virtualized. That means that at any given moment, only the visible items (along with a small subset of "almost visible" items) in the ItemsSource will actually be rendered. That explains why updating the source works as expected (since those items always exist,) but just navigating the UI doesn't (since the visual representations of those items are created and destroyed on the fly, and never exist together at once.)

如果您想关闭此行为,一种选择是在您的 ListBox 上设置 ScrollViewer.CanContentScroll=False.这将启用平滑"滚动,并隐式关闭虚拟化.要显式禁用虚拟化,您可以设置 VirtualizingStackPanel.IsVirtualizing=False.

If you want to turn off this behaviour, one option is to set ScrollViewer.CanContentScroll=False on your ListBox. This will enable "smooth" scrolling, and implicitly turn off virtualization. To disable virtualization explicitly, you can set VirtualizingStackPanel.IsVirtualizing=False.

这篇关于WPF:绑定到 ListBoxItem.IsSelected 不适用于屏幕外项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆