xamllistviewdata-bindingwinui-3selected

WinUI 3 Show selected items from ListView


I have a WinUI 3 project scaffolded using Template Studio. I have a list view populated with an Enum. I want to show my selected items in another list, but the binding does not work.
Populated with Enum meaning I take <key, value> pairs with enum value and enum description and use as ItemsSource. Selection Mode Multiple active.

public IEnumerable<KeyValuePair<string, string>> ValidationFlagsList => EnumExtensions.GetAllValuesAndDescriptions<ValidationFlag>();

//...
public static IEnumerable<KeyValuePair<string, string>> GetAllValuesAndDescriptions<TEnum>() where TEnum : struct, IConvertible, IComparable, IFormattable
{
    return typeof(TEnum).IsEnum ? (from e in Enum.GetValues(typeof(TEnum)).Cast<Enum>() select new KeyValuePair<string, string>(e.ToString(), e.GetDescription())) : throw new ArgumentException("TEnum must be an Enumeration type");
}
<ListView
        x:Name="FlagsListView"
        SelectionMode="Multiple"
        ItemsSource="{x:Bind ViewModel.ValidationFlagsList, Mode=OneTime}"
        SelectedValuePath="Key"
        DisplayMemberPath="Value">
</ListView>

In another part of xaml I want to show the selected items. I tried two variants:

1.

<ListView ItemsSource="{Binding SelectedItems, ElementName=FlagsListView, Mode=OneWay}"/>

2.

<StackPanel DataContext="{Binding SelectedItems, ElementName=FlagsListView}">
    <TextBlock Text="{Binding}"/>
</StackPanel>

Nothing shows on UI. How can I bind correctly?

Is it because IEnumerable is static and ObservableCollection is needed? But the xaml ListView should give me some straightforward binding. Documentation points to this Data binding. I read about creating a class with IsSelected property, but I only need a readonly list, preferably to add something only in xaml.


Solution

  • The ListView does have a SelectedItems property but it's just a plain property and not a DependencyProperty. Unfortunately you can't use it with bindings.

    What you can do is create a custom ListView:

    ListViewEx.cs

    using Microsoft.UI.Xaml;
    using Microsoft.UI.Xaml.Controls;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace ListViews;
    
    public class ListViewEx : ListView
    {
        public ListViewEx() : base()
        {
            this.SelectionChanged += ListViewEx_SelectionChanged;
        }
    
        public new IList SelectedItems
        {
            get => (IList)GetValue(SelectedItemsProperty);
            set => SetValue(SelectedItemsProperty, value);
        }
    
        public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
            nameof(SelectedItems),
            typeof(IList),
            typeof(ListViewEx),
            new PropertyMetadata(new ObservableCollection<object>()));
    
        private void ListViewEx_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            foreach (object item in e.RemovedItems)
            {
                SelectedItems.Remove(item);
            }
    
            foreach (object item in e.AddedItems)
            {
                SelectedItems.Add(item);
            }
        }
    }
    

    and use it like this:

    
    <Grid ColumnDefinitions="*,*">
        <local:ListViewEx
            x:Name="FlagsListView"
            Grid.Column="0"
            DisplayMemberPath="Value"
            ItemsSource="{x:Bind ViewModel.ValidationFlagsList, Mode=OneTime}"
            SelectedValuePath="Key"
            SelectionMode="Multiple" />
    
        <ListView
            Grid.Column="1"
            ItemsSource="{x:Bind FlagsListView.SelectedItems, Mode=OneWay}" />
    </Grid>