Search code examples
c#listviewmvvmwinui-3

WinUI 3 ListView - how to select item programmatically in mvvm?


We have an observable collection SelectedPartys if user interacts with the listview we add/remove in code behind.

<ListView
    x:Name="LV_Partys"
    IsMultiSelectCheckBoxEnabled="True"
    ItemsSource="{x:Bind ViewModel.PartysOC, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
    SelectionChanged="LV_Partys_SelectionChanged"
    SelectionMode="Extended">

    <ListView.ItemTemplate>
        <DataTemplate>

            <Grid Margin="0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <StackPanel Grid.Row="0" Orientation="Horizontal">
                    <TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" TextWrapping="NoWrap" />
                </StackPanel>

            </Grid>

        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
private ObservableCollection<Party> partysOC;
public ObservableCollection<Party> PartysOC
{
    get => partysOC;
    set => Set(ref partysOC, value);
}

private void LV_Partys_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var added_items = e.AddedItems.Cast<Party>().ToList();
    foreach (var item in added_items)
    {
        ViewModel.SelectedPartys.Add(item);
    }

    var removed_items = e.RemovedItems.Cast<Party>().ToList();
    foreach (var item in removed_items)
    {
        ViewModel.SelectedPartys.Remove(item);
    }

    ViewModel.SelectedPartyChanged();
}

We need to save the ListViews selected items in Db and then restore them pre-selected in the ListView, to do this I believe we need to select an item programatically, how can we do this?


Solution

  • Since you can't bind to SelectedItems, you might need to create a control derived from ListView:

    CustomListView.cs

    using Microsoft.UI.Xaml;
    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Data;
    using Windows.Foundation.Collections;
    
    namespace ListViews;
    
    public class CustomListView : ListView
    {
        public static readonly DependencyProperty SelectedItemsSourceProperty = DependencyProperty.Register(
            nameof(SelectedItemsSource),
            typeof(object),
            typeof(CustomListView),
            new PropertyMetadata(default, (d, e) =>
            {
                (d as CustomListView)?.UpdateSelectedItemsSource();
            }));
    
        public object SelectedItemsSource
        {
            get => (object)GetValue(SelectedItemsSourceProperty);
            set => SetValue(SelectedItemsSourceProperty, value);
        }
    
        private CollectionViewSource? SelectedItemsSourceViewSource { get; set; }
    
        private ICollectionView? SelectedItemsSourceView { get; set; }
    
        private void UpdateSelectedItemsSource()
        {
            if (SelectedItemsSourceView is not null)
            {
                SelectedItemsSourceView.VectorChanged -= SelectedItemsSourceView_VectorChanged;
            }
    
            SelectedItemsSourceViewSource = new()
            {
                Source = SelectedItemsSource
            };
    
            SelectedItemsSourceView = SelectedItemsSourceViewSource.View;
            SelectedItemsSourceView.VectorChanged += SelectedItemsSourceView_VectorChanged;
        }
    
        private void SelectedItemsSourceView_VectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs @event)
        {
            switch (@event.CollectionChange)
            {
                case CollectionChange.Reset:
                    SelectedItems.Clear();
                    break;
    
                case CollectionChange.ItemInserted:
                    if (SelectedItemsSourceView?.Count >= @event.Index)
                    {
                        SelectedItems.Add(SelectedItemsSourceView[(int)@event.Index]);
                    }
                    break;
    
                case CollectionChange.ItemRemoved:
                    if (SelectedItemsSourceView?.Count >= @event.Index)
                    {
                        SelectedItems.Remove(SelectedItemsSourceView[(int)@event.Index]);
                    }
                    break;
    
                case CollectionChange.ItemChanged:
                    break;
    
                default:
                    break;
            }
        }
    }
    

    and use it like this:

    MainPage.xaml

    <local:CustomListView
        ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}"
        SelectedItemsSource="{x:Bind ViewModel.SelectedItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
        SelectionMode="Multiple">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:Item">
                <TextBlock Text="{x:Bind Id}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </local:CustomListView>