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

Time:11-17

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?

CodePudding user response:

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>

CodePudding user response:

There is a method on the WinUI 3 ListView called YourListView.SelectRange()

It isnt available on a ListView in xaml so I call it in code behind via a MVVM messenging call.

The Views Code Behind file

public CodeBehindConstructor()
{
    this.InitializeComponent();

    // Rx ListView Select items
    WeakReferenceMessenger.Default.Register<Msg_ListView_SelectItems>(this, (r, msg) =>
    {
        SelectParty(msg.Start, msg.Length);
    });
}


public void SelectParty(int start, uint length)
{
    Debug.WriteLine($"SelectParty  start {start}   len {length}");
    LV_Partys.SelectRange(new ItemIndexRange(start, length));
}

The ViewModel

    // Select 1st Party 
    WeakReferenceMessenger.Default.Send(new Msg_ListView_SelectItems { Start = 0, Length = 1});// Start is 0 based, Length isnt it starts at 1

    // Select 2nd Party 
    WeakReferenceMessenger.Default.Send(new Msg_ListView_SelectItems { Start = 1, Length = 1 });

Msg_ListView_SelectItems class

public class Msg_ListView_SelectItems
{
    public int Start { get; set; }
    public uint Length { get; set; }
}

If your ListViews selection mode is Multiple or Extended then .SelectRange() selects one consecutive lot of items, so you can call SelectRange more than once for items that are not in consecutive order.

  • Related