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

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

问题描述

在我的节目,我有一组视图模型对象重新在ListBox present项目(多选是允许的)。该视图模型有一个IsSelected属性,我想绑定到列表框,以便选择状态的视图模型,而不是在列表框中本身的管理。

然而,显然列表框不维护绑定大多数离屏项的,所以一般IsSelected属性不正确同步。下面是一些code演示该问题。首先XAML:

 < StackPanel的>
    < StackPanel的方向=横向>
        <的TextBlock>入选项目数:< / TextBlock的>
        < TextBlock的文本={结合NumItemsSelected}/>
    < / StackPanel的>
    <列表框的ItemsSource ={绑定表项}高度=200的SelectionMode =扩展>
        < ListBox.ItemContainerStyle>
            <风格的TargetType ={X:输入一个ListBoxItem}>
                < setter属性=IsSelectedVALUE ={结合IsSelected}/>
            < /样式和GT;
        < /ListBox.ItemContainerStyle>
    < /列表框>
    <按钮名称=TestSelectAll点击=TestSelectAll_Click>选择全部< /按钮>
< / StackPanel的>
 

C#全选处理程序:

 私人无效TestSelectAll_Click(对象发件人,RoutedEventArgs E)
{
    的foreach(在_dataContext.Items VAR项)
        item.IsSelected = TRUE;
}
 

C#视图模型:

 公共类TestItem:NPCHelper
{
    TestDataContext _c;
    串_text;
    公共TestItem(TestDataContext C,文本字符串){_c = C; _text =文本; }

    公共重写字符串的ToString(){返回_text; }

    布尔_isSelected;
    公共BOOL IsSelected
    {
        {返回_isSelected; }
        组 {
            _isSelected =价值;
            FirePropertyChanged(IsSelected);
            _c.FirePropertyChanged(NumItemsSelected);
        }
    }
}
公共类TestDataContext:NPCHelper
{
    公共TestDataContext()
    {
        的for(int i = 0; I< 200;我++)
            _items.Add(新TestItem(此,i.ToString()));
    }
    的ObservableCollection< TestItem> _items =新的ObservableCollection< TestItem>();
    公众的ObservableCollection< TestItem>项目{{返回_items; }}

    公众诠释NumItemsSelected {{返回_items.Where(IT => it.IsSelected).Count之间的(); }}
}
公共类NPCHelper:INotifyPropertyChanged的
{
    公共事件PropertyChangedEventHandler的PropertyChanged;
    公共无效FirePropertyChanged(字符串道具)
    {
        如果(的PropertyChanged!= NULL)
            的PropertyChanged(这一点,新PropertyChangedEventArgs(丙));
    }
}
 

两个单独的问题都可以被观察到。

  1. 如果您单击第一个项目,然后preSS SHIFT + END,所有的200个项目应选择;然而,标题报道,只有21个项目被选中。
  2. 如果您单击全选,则所有的项目确实选择。如果您单击ListBox中的一个项目,你会期望其他199项目被取消,但这种情况不会发生。相反,只有在屏幕(和其他一些)上的项目取消。全部199项将不会被取消,除非您完成从开始到结束(即使这样,奇怪的是,它并没有,如果你进行滚动的小滚动框工作)列表中第一个卷。

我的问题是:

  1. 有人能解释一下precisely为什么出现这种情况?
  2. 在我能避免或解决这个问题?
解决方案

列表框,默认情况下,用户界面​​虚拟化。这意味着,在任何时候,只有可见项(连同几乎已到项目的一小部分)的的ItemsSource 将真正呈现。这就解释了为什么更新的来源的工作正常(因为这些项目始终存在,),但只是导航的用户界面不会(因为创建和销毁对这些项目的视觉重新presentations飞行,一次不能同时存在。)

如果你想关闭这一行为,一种选择是设置 ScrollViewer.CanContentScroll =假列表框。这将使平稳滚动,并含蓄关闭虚拟化。要明确禁用虚拟化,你可以设置 VirtualizingStackPanel.IsVirtualizing =假

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.

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# Select All handler:

private void TestSelectAll_Click(object sender, RoutedEventArgs e)
{
    foreach (var item in _dataContext.Items)
        item.IsSelected = true;
}

C# viewmodel:

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. If you click the first item and then press Shift+End, all 200 items should be selected; however, the heading reports that only 21 items are selected.
  2. If you click "Select all" then all items are indeed selected. If you then click an item in the ListBox you would expect the other 199 items to be deselected, but this does not happen. Instead, only the items that are on the screen (and a few others) are deselected. All 199 items will not be deselected unless you first scroll through the list from beginning to end (and even then, oddly enough, it doesn't work if you perform scrolling with the little scroll box).

My questions are:

  1. Can someone explain precisely why this occurs?
  2. Can I avoid or work around the problem?

解决方案

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.)

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天全站免登陆