将对象绑定到ListBox,Items状态与预期不符

最近看discussion,有个话题比较有意思:
定义了一个类

    class DisplayedItem

    {

 

        public DisplayedItem(string name, bool isChecked)

        {

            Name = name;

            IsChecked = isChecked;

        }

        public string Name { get; set; }

        public bool IsChecked { get; set; }

    }

这个类有一个IsChecked的bool类型,想把这个对象集合绑到ListBox,每个ListBoxItem显示为CheckBox。 

    ObservableCollection<DisplayedItem> col = new ObservableCollection<DisplayedItem>();

    private void Window_Loaded(object sender, RoutedEventArgs e)

    {

        col.Add(new DisplayedItem("item1", false));

        col.Add(new DisplayedItem("item2", false));
        col.Add(new DisplayedItem("item3", false));
        col.Add(new DisplayedItem("item4", false));
        col.Add(new DisplayedItem("item5", false));
        col.Add(new DisplayedItem("item6", false));
        col.Add(new DisplayedItem("item7", false));
        col.Add(new DisplayedItem("item8", false));

        col.Add(new DisplayedItem("item9", false));

        col.Add(new DisplayedItem("item10", false));

        col.Add(new DisplayedItem("item11", false));

        col.Add(new DisplayedItem("item12", false));

        col.Add(new DisplayedItem("item13", false));

        col.Add(new DisplayedItem("item14", false));

        col.Add(new DisplayedItem("item15", false));

        col.Add(new DisplayedItem("item16", false));

 

        listBox1.ItemsSource = col;

    }

<ListBox Name="listBox1" Margin="198,12,0,48" HorizontalAlignment="Left" Width="146" Height="120">

        <ListBox.ItemTemplate>

           <DataTemplate>

               <CheckBox Content="{Binding Path=Name}" 

                         IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"/>

           </DataTemplate>

        </ListBox.ItemTemplate>

</ListBox>

 

界面上有个Button,点击时把所有CheckBox都勾上

private void button1_Click(object sender, RoutedEventArgs e)

{

    foreach (var item in col)

    {

       item.IsChecked = true;

    }

}

运行后的实际结果却很奇怪

初始:
image

                                                     图1

点击Button后,把滚动条拉到最下,会发现item10到item16都勾上了,而item1到item9却没勾上。

clip_image002

                                                    图2

调试时可发现,实际绑定的对象的IsChecked都是true。

研究一下代码,点Button让所有item都勾上,这个很好实现,只要DisplayedItem实现INotifyPropertyChanged。可以参见这里的绑定到.net属性/对象这节。

 

但是我不禁要问为什么不实现INotifyPropertyChanged的情况下为什么有的是勾上的,有的却不勾上,而且怪异的是,在点Button前,先把滚动条拉到最下,再点Button,会发现所有items都没有勾上:
clip_image002[5]

                                                     图3

这么怪异的不可思议的匪夷所思的事情是怎么发生的,是WPF有bug吗?

幸好有高人回复,经过我的理解大意如下:

因为没有实现INotifyPropertyChanged,绑定到ListBox时使用了OneTime这种模式。可以看到ListBox中,有些Item(Item1到Item7)是在初始状态就显示的,有些(Item8到Item16)是不显示的。对于已经显示的,在显示时使用OneTime去计算值,都是false,对于未显示的,为了性能上的考虑,在没显示时不作绑定的计算。在点击Button时,源中已经都是true了,把滚动条拉到最下,有个显示的过程,这时经过计算得到true,所以UI上会勾上。如果先把滚动条拉到最下,所有的Item都经过OneTime的计算,值都是false,点击button,虽然源已经被设置为true,但是没有实现INotifyPropertyChanged所以源的改变不会显示在UI上。

确实是这样的,对于使用了OneTime这点可以通过把Item6,Item7的初始状态设为true,可以观察的到。
image

                           图4 ListBox加高后,初始Item6,Item7设为true

但是细心的同学会发现初始情况下Item8,Item9也是不显示的,为什么点击button后,还是没勾上呢?

这个确实奇怪,又有高人路过,出招了:
为了使得显示的流畅感十足,WPF为对某几个未显示的(接近已显示的)也作了绑定计算,这里是Item8和Item9。

我作了实验,把ListBox的高度加大,确实原先勾上的就不勾上了:

image

                        图5 加高后,Item12,Item13处于未勾中状态

 

 

真相只有一个。

你可能感兴趣的:(listbox)