xamlwinrt-xamlwinui-3

WinUI3 ListView Selected Items


I'm using a WinUI3 ListView like this to load a list of files:

<ListView ItemTemplate="{StaticResource Template2}"
 ItemsSource="{x:Bind Files,Mode=OneWay}"  
IsItemClickEnabled="True" x:Name="List2" SelectionMode="Multiple" />
 <DataTemplate x:Key="Template2" x:DataType="local:FileItem">
        <Grid>
         .... TextBlocks that bind to properties in FileItem
         </Grid>
</DataTemplate>

Now, how do I configure my DataTemplate in order for some (or all) of the items to be selected by default?

Is there a special value in the data template I should use?


Solution

  • You can use the ListViewItem in your ItemTemplate.

    Let's say your FileItem looks like this:

    public partial class FileItem : ObservableObject
    {
        [ObservableProperty]
        private string _name = string.Empty;
    
        [ObservableProperty]
        private bool _isSelected;
    }
    

    and your ViewModel:

    public partial class  MainPageViewModel : ObservableObject
    {
        [ObservableProperty]
        private ObservableCollection<FileItem> _files = new()
        {
            new FileItem { Name = "File1" },
            new FileItem { Name = "File2", IsSelected = true, },
            new FileItem { Name = "File3"},
        };
    }
    

    and in XAML:

    <Page.Resources>
        <DataTemplate
            x:Key="Template2"
            x:DataType="local:FileItem">
            <ListViewItem IsSelected="{x:Bind IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <TextBlock Text="{x:Bind Name, Mode=OneWay}" />
            </ListViewItem>
        </DataTemplate>
    </Page.Resources>
    <ListView
        Grid.Row="1"
        IsItemClickEnabled="True"
        ItemTemplate="{StaticResource Template2}"
        ItemsSource="{x:Bind ViewModel.Files, Mode=OneWay}"
        SelectionMode="Multiple" />
    

    But unfortunately, this won't work because the ListViewItem resets its IsSelected property to false when it's loaded. I'm not sure if it's a bug or it's by design.

    As a workaround, you can do the binding after each ListViewItem is loaded:

    <Page.Resources>
        <DataTemplate
            x:Key="Template2"
            x:DataType="local:FileItem">
            <ListViewItem Loaded="ListViewItem_Loaded">
                <TextBlock Text="{x:Bind Name, Mode=OneWay}" />
            </ListViewItem>
        </DataTemplate>
    </Page.Resources>
    

    then in code-behind:

    private void ListViewItem_Loaded(object sender, RoutedEventArgs e)
    {
        if (sender is not ListViewItem listViewItem)
        {
            return;
        }
    
        listViewItem.SetBinding(
            ListViewItem.IsSelectedProperty,
            new Binding
            {
                Path = new PropertyPath("IsSelected"),
                Mode = BindingMode.TwoWay,
                Source = listViewItem.DataContext,
                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
            });
    }
    

    BTW, I'm using the CommunityToolkit.Mvvm NuGet package for the ViewModels but I hope this gives you an idea how to fix your issue.