2

我会保持简洁。我有一个实现 ItemTemplate 的 ListBox。DataTemplate 包含一个复选框。我加载了大约 2000 个项目。我检查前 5 个项目,滚动到底部并选择最后 5 个项目。然后我向上滚动到顶部项目,并注意到我的前 5 个检查项目已被修改。

    <Window 
        x:Class="CheckItems.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CheckItems"
        Title="Window1" Height="300" Width="300"
        >
        <DockPanel>
            <StackPanel DockPanel.Dock="Bottom" >
                <Button Content="Test" Click="Button_Click"/>
            </StackPanel>
            <ListBox DockPanel.Dock="Left"
                x:Name="users"
                ItemsSource="{Binding Path=Users}"
                >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <CheckBox>
                            <TextBlock Text="{Binding Path=Name}"/>
                        </CheckBox>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </DockPanel>
    </Window>



    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Windows;
    namespace CheckItems
    {
        public partial class Window1 : Window
        {
            ViewModel controller;

            public Window1()
            {
                DataContext = controller = new ViewModel();
                InitializeComponent();
                controller.Users = LoadData();
            }

            private List<User> LoadData()
            {
                var newList = new List<User>();
                for (var i = 0; i < 2000; ++i)
                    newList.Add(new User { Name = "Name" + i, Age = 100 + i });
                return newList;
            }

            private void Button_Click(object sender, RoutedEventArgs e)
            { }
        }

        public class User
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }


        public class ViewModel : INotifyPropertyChanged
        {
            private List<User> users;
            public event PropertyChangedEventHandler PropertyChanged;

            public List<User> Users
            {
                get { return users; }
                set { users = value; NotifyChange("Users"); }
            }

            protected void NotifyChange(string propertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

希望对此有一个很好的解释 - 这是一个 MS 错误。这发生在 .NET 3.5 和 4.0 中。当 VirtualingStackPanel.IsVirtualizing 设置为 false 时,不会发生这种行为,但在现实世界中,没有虚拟化的加载是痛苦的。

一些见解会很好。

提前致谢,

安德烈斯·奥利瓦雷斯

4

1 回答 1

2

虚拟化面板会重复使用其中的控件,并DataContext在滚动时简单地替换控件后面的控件。这意味着当您滚动时,控件的状态(例如IsChecked)会被重置,除非该状态绑定到 DataContext 中的某些内容。

例如,如果您的 2000 中一次只有 10 个项目可见,那么 WPF 将只渲染其中的大约 14 个(滚动缓冲区的额外项目),并且在您滚动并替换DataContext控件后面时简单地重新使用这 14 个项目.

如果您禁用虚拟化,您将禁用此回收行为。这意味着 WPF 将渲染 2000 个项目而不是 14 个,这就是性能如此糟糕的原因。这也意味着 CheckBox 将保持选中状态,因为它们的状态未重置。

要解决此问题,我建议向IsSelected您的 User 对象添加一个属性并将其绑定CheckBox.IsChecked到它。

于 2012-01-05T14:21:01.710 回答