question

JMDeb-5957 avatar image
0 Votes"
JMDeb-5957 asked ·

Selecting, deselecting ListView items by code...and manually

Hi,

Working with a list of about 50,000 items, I must be able to select/deselect items code behind.

I first tried to achieve this through UI. For instance, to deselect a selection:

 foreach (ListViewItem item in ListView_DB_Edit.Items)
             item.IsSelected = !item.IsSelected;

Since I was naturally using virtualization, I had to pass by the ItemContainerGenerator, so the code was more:

 foreach (DatabaseItems item in ListView_DB_Edit.Items) {
     ListViewItem listViewItem = TryCast(ListView_DB_Edit.ItemContainerGenerator.ContainerFromItem(item), ListViewItem);
     if (listViewItem != null) {
         listViewItem.IsSelected = !listViewItem.IsSelected;
     }   
 }

The problem is that when UI virtualization is enabled for the ListView, the ItemContainerGenerator methods will return null for items that are not currently visible. This happens because the corresponding ListViewItem has not yet been created.
(That said, I wonder how the Listview SelectAll() and UnselectAll() methods do! Because they do work perfectly with virtualization!!)

Anyway! I then decided to rather handle the selection by adding a IsSelected property to my object...then bind the IsSelect from the ListViewItem to that property.
...and this just worked fine!

The problem:
The problem arises when I want to 'mix' code behind selection with 'manual' selection.
In the following clip, one can see how, after having done a selection code behind, then adding a selection manually (using the Ctrl/Shift combination from Windows) then attempting to invert the selection, the elements selected manually...stay selected!
MrrqB2P.gif

Here I am..I can't see what is to be done now...could you please help?
Here is the whole code used for this example

 <Window x:Class="SelectionBinding.MainWindow"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:SelectionBinding"
         mc:Ignorable="d"
         Title="MainWindow" Height="450" Width="800">
     <Grid>
         <ListView  VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" IsSynchronizedWithCurrentItem="True" IsTextSearchEnabled="True" TextSearch.TextPath="Name" x:Name="ListView_DB_Edit" Margin="10,10,0,58" HorizontalAlignment="Left" SelectionChanged="ListView_DB_Edit_OnSelectionChanged">
             <ListView.ItemContainerStyle>
                 <Style TargetType="{x:Type ListViewItem}">
                     <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                     <EventSetter Event="UIElement.PreviewMouseLeftButtonDown" Handler="ListviewItem_Click" />
                 </Style>
             </ListView.ItemContainerStyle>
             <ListView.View>
                 <GridView x:Name="GridView1">
                     <GridViewColumn DisplayMemberBinding="{Binding Id}" Width="100" >
                         <GridViewColumnHeader Content="Id"/>
                     </GridViewColumn>
                     <GridViewColumn DisplayMemberBinding="{Binding Number}" Width="100" >
                         <GridViewColumnHeader Content="Number"/>
                     </GridViewColumn>
                 </GridView>
             </ListView.View>
         </ListView>
         <Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="140" Height="30" Margin="250,10,0,0" Content="Select All" Click="SelectAll_Click"></Button>
         <Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="140" Height="30" Margin="250,50,0,0" Content="Unselect All" Click="UnselectAll_Click"></Button>
         <Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="140" Height="30" Margin="250,90,0,0" Content="Invert Selection" Click="InvertSel_Click"></Button>
         <Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="140" Height="30" Margin="250,130,0,0" Content="Select Range 2-9" Click="SelRange_Click"></Button>
         <Label x:Name="StatsLabel" Margin="10,0,10,30" FontSize="18" VerticalAlignment="Bottom" Padding="0"/>
         <Label x:Name="StatsLabel2" Margin="10,0,10,5" FontSize="18" VerticalAlignment="Bottom" Padding="0" />
     </Grid>
 </Window>

C#

 using System;
 using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Data;
 using System.Windows.Input;
    
    
 namespace SelectionBinding
 {
     public partial class MainWindow : Window
     {
         private ObservableCollection<DBitem> db;
         Random rand = new Random();
    
         public MainWindow()
         {
             InitializeComponent();
    
             db = new ObservableCollection<DBitem>();
             for (int i = 0; i < 100; i++)
             {
                 db.Add(new DBitem() { Id = i.ToString(), Number = rand.Next(101).ToString() });
             }
    
             ICollectionView icv = CollectionViewSource.GetDefaultView(db);
             //icv.SortDescriptions.Add(new SortDescription("Number", ListSortDirection.Ascending));
    
             ListView_DB_Edit.ItemsSource = icv;
             ListView_DB_Edit.SelectedItems.Clear();
         }
    
         public class DBitem : INotifyPropertyChanged
         {
             private string _id;
             public string Id
             {
                 get { return _id; }
                 set { _id = value; OnPropertyChanged("Id");}
             }
             public string _number;
             public string Number
             {
                 get { return _number; }
                 set { _number = value; OnPropertyChanged("Number");}
             }
             public bool _isselected;
             public bool IsSelected
             {
                 get { return _isselected; }
                 set { _isselected = value; OnPropertyChanged("IsSelected"); }
             }
    
             public event PropertyChangedEventHandler PropertyChanged;
             private void OnPropertyChanged(string nameProp)
             {
                 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameProp));
             }
         }
    
         private void SelectAll_Click(object sender, RoutedEventArgs e)
         {
             foreach (DBitem dbi in db)
             {
                 dbi.IsSelected = true;
             }
             RefreshStats();
         }
    
         private void UnselectAll_Click(object sender, RoutedEventArgs e)
         {
             foreach (DBitem dbi in db)
             {
                 dbi.IsSelected = false;
             }
             RefreshStats();
         }
    
         private void InvertSel_Click(object sender, RoutedEventArgs e)
         {
             foreach (DBitem dbi in db)
             {
                 dbi.IsSelected = !dbi.IsSelected;
             }
             RefreshStats();
         }
    
         private void SelRange_Click(object sender, RoutedEventArgs e)
         {
             for (int i = 2; i < 10; i++)
             {
                 db[i].IsSelected = true;
             }
             RefreshStats();
         }
    
         private void ListviewItem_Click(object sender, MouseButtonEventArgs e)
         {
             RefreshStats();
         }
    
         private void ListView_DB_Edit_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
         {
             RefreshStats();
         }
    
         private void RefreshStats()
         {
             string total = db.Count.ToString();
             int nbr = 0;
             foreach (DBitem item in db)
             {
                 if (item.IsSelected) nbr += 1;
             }
             string selected = nbr.ToString();
             StatsLabel.Content = "Obects IsSelected: "+ selected + "|" + total;
             StatsLabel2.Content = "Listview selecteditems: " + ListView_DB_Edit.SelectedItems.Count.ToString();
         }
     }
 }





windows-wpf
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

gekka avatar image
0 Votes"
gekka answered ·

Hi JMDeb,

The cause is the reuse of ListViewItems.
Set ListView's VirtualizingStackPanel.VirtualizationMode to Standard to improve the problem.

· 1 ·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thanks for the reply! At first it seems to work better...but as soon as you scroll the view down...the same problem occurs
dmSIXIy.gif


0 Votes 0 ·
AlexLi-MSFT avatar image
0 Votes"
AlexLi-MSFT answered ·

Welcome to our Microsoft Q&A platform!

You can customize a VirtualizingStackPanel to prohibit unloading any UI element which was selected.

  public class MyVirtualizingStackPanel : VirtualizingStackPanel
     {
         protected override void OnCleanUpVirtualizedItem(CleanUpVirtualizedItemEventArgs e)
         {
             var item = e.UIElement as ListBoxItem;
             if (item != null && item.IsSelected)
             {
                 e.Cancel = true;
                 e.Handled = true;
                 return;
             }
    
             var item2 = e.UIElement as TreeViewItem;
             if (item2 != null && item2.IsSelected)
             {
                 e.Cancel = true;
                 e.Handled = true;
                 return;
             }
    
             base.OnCleanUpVirtualizedItem(e);
         }
     }

  <ListView  VirtualizingStackPanel.VirtualizationMode="Recycling" IsSynchronizedWithCurrentItem="True" IsTextSearchEnabled="True" TextSearch.TextPath="Name" x:Name="ListView_DB_Edit" Margin="10,10,0,58" HorizontalAlignment="Left" SelectionChanged="ListView_DB_Edit_OnSelectionChanged">
             <ListView.ItemsPanel>
                 <ItemsPanelTemplate>
                     <local:MyVirtualizingStackPanel/>
                 </ItemsPanelTemplate>
             </ListView.ItemsPanel>
 ...


Thanks.

· 2 ·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi Alex, this approach doesn't work properly.7801-28-04-2020-05-51-46.gif


0 Votes 0 ·

@AlexLi-MSFT Thanks for the reply! Yes, in some occasion that will work better, thanks!
But what about this particular problem, could you please help?

  1. Go to the end of the list

  2. Select programmatically somme items at the top of the list

  3. Select manually (with the help of ctrl & shift, to add to the selection) some more items

  4. Do some actions like "invert"

  5. Then click (a single click, no ctrl or shift this time) on another item

This should unselect every selected items..and it does in the 'visible' view...but not in the 'hidden' part!
What could be done to address this please? Any idea?
ne4oJMb.gif




0 Votes 0 ·