Как связать IsSelected в ListView для невидимых (виртуализированных) элементов - PullRequest
8 голосов
/ 10 сентября 2011

Мне нужно иметь дело с большим количеством данных в приложении WPF.

Я привязал большую коллекцию к ListView, и я использую ItemContainerStyle, чтобы связать свойство IsSelected элемента списка ссвойство IsSelected моего объекта, поэтому при выборе элемента в ListView свойство IsSelected моих объектов также будет установлено в значение true.Делая это, я могу легко выводить команды только на объекты, которые были выбраны в списке.

Я использую виртуализацию пользовательского интерфейса в ListView, потому что в противном случае приложение будет работать медленно.Но поскольку в списке отображается только подмножество всей моей коллекции, когда я использую CTRL + A, чтобы выбрать все элементы в моем списке, только для загруженных элементов их свойство IsSelected имеет значение true.Для невидимых элементов (виртуализированных элементов) их свойство IsSelected имеет значение false.Это проблема, потому что, когда я выбираю все элементы в списке, я ожидаю, что для свойства IsSelected установлено значение true для всех элементов в коллекции.

Я создал некоторый пример кода для иллюстрациипроблема:

MainWindow.xaml

<Window x:Class="VirtualizationHelp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" x:Name="wnd">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Button Grid.Row="0" Content="Click me" Click="Button_Click" />
    <ListView Grid.Row="1" ItemsSource="{Binding Path=Persons, ElementName=wnd}">
        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
            </Style>
        </ListView.ItemContainerStyle>
    </ListView>
</Grid>
</Window>

MainWindow.xaml.cs

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace VirtualizationHelp
{
    public partial class MainWindow : Window
    {
        List<SelectablePerson> _persons = new List<SelectablePerson>(10000);
        public List<SelectablePerson> Persons { get { return _persons; } }
        public MainWindow()
        {
            for (int i = 0; i < 10000; i++)
            {
                SelectablePerson p = new SelectablePerson() { Name = "Person " + i, IsSelected = false };
                _persons.Add(p);
            }
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            int count = Persons.Where(p => p.IsSelected == true).Count();
            (sender as Button).Content = count.ToString();
        }
    }

    public class SelectablePerson
    {
        public string Name { get; set; }
        public bool IsSelected { get; set; }
        public override string ToString()
        {
            return Name;
        }
    }
}

Когда нажата кнопка в верхней части формы, она считает элементы вколлекция, для которой свойство IsSelected установлено в значение true.Вы можете видеть, что когда вы нажимаете CTRL + A , чтобы выбрать все элементы в списке, это показывает, что выбрано только 19 элементов.

Кто-нибудь знает способвокруг этой проблемы?Я не могу отключить виртуализацию, потому что получаю ужасную производительность.

1 Ответ

3 голосов
/ 28 марта 2012

Я думаю, это твоя привязка.По сути, вы обновляете / привязываете ListViewItem, который будет обновлять только видимые, а не весь список.Я поиграюсь с этим, а пока вы можете разобрать в привязке кода ..

<Window x:Class="VirtualizationHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="wnd">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock x:Name="txtSelectedItemsCount"/>
        <ListView Grid.Row="1" ItemsSource="{Binding Path=Persons, ElementName=wnd}" 
         SelectionChanged="ListView_SelectionChanged"/>
    </Grid>
</Window>

private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var lv = (ListView) sender;
    txtSelectedItemsCount.Text = lv.SelectedItems.Count.ToString();
}
...