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.