Search code examples
listviewwinui-3

WinUI 3 Highlight listview item on ctrl - click


I use a listview in single mode and I would like to change its visual aspect when I control-click on a row to show that this row is selected. I can't use SelectionMode="Multiple" because it doesn't match the expected result (when I normally click on a row, an action is triggered and when I select several rows via ctrl - click, a button appears to perform an action on all selected rows).

I have set some attributes on my listview like

IsItemClickEnabled="True" ItemClick="Results_ItemClick"

In Results_ItemClick, I check if the control key is pressed and I would like it to stay highlighted if I move my mouse to another line. I tried with VisualStateManager but I have the impression that moving the mouse cancels the behavior.

VisualStateManager.GoToState(sender as Control, "Selected", true);

I thought of adding the item to the SelectedItems of the listview but SelectedItems doesn't allow the addition.

Is there a way to do this?

Thank you.


Solution

  • You can do it this way:

    MainPageViewModel.cs

    using CommunityToolkit.Mvvm.ComponentModel;
    using System.Collections.ObjectModel;
    
    namespace ListViewTests;
    
    public class Item
    {
        public int Id { get; set; }
    
        public string Text { get; set; } = string.Empty;
    }
    
    public partial class MainPageViewModel : ObservableObject
    {
        [ObservableProperty]
        private ObservableCollection<Item> items = new();
    
        public MainPageViewModel()
        {
            for (int i = 0; i < 10000; i++)
            {
                Items.Add(new Item { Id = Items.Count + 1, Text = $"Item {i + 1}" });
            }
        }
    }
    

    MainPage.xaml

    <Page
        x:Class="ListViewTests.MainPage"
        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:local="using:ListViewTests"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        mc:Ignorable="d">
    
        <Grid RowDefinitions="Auto,*">
            <StackPanel
                Grid.Row="0"
                Orientation="Horizontal">
                <TextBlock Text="Operations" />
                <Button
                    x:Name="ProcessSelectedItemsButton"
                    Click="ProcessSelectedItemsButton_Click"
                    Content="Process selected items"
                    Visibility="Collapsed" />
            </StackPanel>
            <ListView
                x:Name="ListViewControl"
                Grid.Row="1"
                IsItemClickEnabled="True"
                ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}"
                SelectionChanged="ListViewControl_SelectionChanged"
                SelectionMode="Extended">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="local:Item">
                        <Grid ColumnDefinitions="30,*,Auto">
                            <TextBlock
                                Grid.Column="0"
                                Text="{x:Bind Id, Mode=OneWay}" />
                            <TextBlock
                                Grid.Column="1"
                                Text="{x:Bind Text, Mode=OneWay}" />
                            <Button
                                Grid.Column="2"
                                Click="ListItemButton_Click"
                                Content="Ctrl + Click to select multiple items" />
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>
    </Page>
    

    MainPage.xaml.cs

    using Microsoft.UI.Input;
    using Microsoft.UI.Xaml;
    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Data;
    using System.Diagnostics;
    using System.Linq;
    using Windows.System;
    using Windows.UI.Core;
    
    namespace ListViewTests;
    
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }
    
        public MainPageViewModel ViewModel { get; } = new();
    
        public static bool IsControlKeyDown()
        {
            return InputKeyboardSource
                .GetKeyStateForCurrentThread(VirtualKey.Control)
                .HasFlag(CoreVirtualKeyStates.Down);
        }
    
        private void ProcessSelectedItemsButton_Click(object sender, RoutedEventArgs _)
        {
            foreach (Item item in this.ListViewControl.SelectedItems.OfType<Item>())
            {
                // Do your logic here...
                Debug.WriteLine($"Processed Item Id: {item.Id} Text: {item.Text}.");
            }
        }
    
        private void ListItemButton_Click(object sender, RoutedEventArgs e)
        {
            if (sender is Button button)
            {
                if (IsControlKeyDown() is false)
                {
                    ItemIndexRange allItemsRange = new(
                        firstIndex: 0,
                        length: (uint)this.ListViewControl.Items.Count);
                    this.ListViewControl.DeselectRange(allItemsRange);
                }
    
                int itemIndex = this.ListViewControl.Items.IndexOf(button.DataContext);
                ItemIndexRange selectedItemRange = new(
                    firstIndex: itemIndex,
                    length: 1);
                this.ListViewControl.SelectRange(selectedItemRange);
            }
        }
    
        private void ListViewControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            this.ProcessSelectedItemsButton.Visibility = (sender as ListView)?.SelectedItems.Count > 1
                ? Visibility.Visible
                : Visibility.Collapsed;
        }
    }