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 が 14 ではなく 2000 のアイテムをレンダリングすることを意味します。これが、パフォーマンスが非常に悪い理由です。また、状態がリセットされないため、CheckBox がチェックされたままになることも意味します。

この問題を解決するにはIsSelected、User オブジェクトにプロパティを追加し、それにバインドすることをお勧めしますCheckBox.IsChecked

于 2012-01-05T14:21:01.710 に答える