xamlwinui-3winuiwindows-community-toolkitwinui-xaml

Displaying Index in WinUI 3 ListView


I am displaying a ListView using WinUI3. I am showing an array of structs, but is it possible to display an array index that is not part of the struct in XAML?

<ListView>
  <ListView.ItemTemplate>
    <DataTemplate x:DataType="local:MyData">
      <TextBlock Text="{x:Bind Index}"/>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

I saw that in WPF, you can do it like the following, but it results in an error in WinUI3. Is there no such method in WinUI3?

<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ListViewItem}, Path=(ItemsControl.AlternationIndex)}" />

Solution

  • Since it seems that there is not easy way to do this, you should keep track of the index in the item (MyData) itself. But if you can't do that, you can try what I came up with. It works. Visual Studio will yell at you but it works.

    First, you need to install the CommunityToolkit.WinUI.Extensions NuGet package.

    Then:

    public partial class ListViewItemElementToIndexConverter : IValueConverter
    {
        private ListView? OwnerListView { get; set; }
    
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            var frameworkElement = value as FrameworkElement;
            OwnerListView ??= frameworkElement?.FindAscendant<ListView>();
            string index = OwnerListView?.Items.IndexOf(frameworkElement?.DataContext).ToString() ?? "-1";
            return index;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
    
    <Page.Resources>
        <local:ListViewItemElementToIndexConverter x:Key="ListViewItemElementToIndexConverter" />
    </Page.Resources>
    
    <ListView ItemsSource="{x:Bind ViewModel.MyDataCollection, Mode=OneWay}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:MyData">
                <ListViewItem>
                    <TextBlock
                        x:Name="IndexTextBlock"
                        Text="{x:Bind IndexTextBlock, Mode=OneWay, Converter={StaticResource ListViewItemElementToIndexConverter}}" />
                </ListViewItem>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    UPDATE

    You need to wrap the DataTemplate content with a ListViewItem unless the DataContext won't be set.