Как получить индекс текущего элемента ItemsControl?

есть ли способ получить индекс текущего ItemsControl элемент WPF?

например, я хочу сделать что-то вроде:

<ItemsControl>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding current_index}">
            </TextBox>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

так что после этого, первого TextBox показать текст "0", второй "1", третий "2" ....

4 ответов


Я бы предложил посмотреть на:

WPF ItemsControl текущий индекс ListItem в ItemsSource

это объясняет, как обойти тот факт, что в ItemsControl нет встроенного свойства индекса.

EDIT:

я попробовал следующий код:

<Window.Resources>
    <x:Array Type="{x:Type sys:String}" x:Key="MyArray">
        <sys:String>One</sys:String>
        <sys:String>Two</sys:String>
        <sys:String>Three</sys:String>
    </x:Array>
</Window.Resources>
<ItemsControl ItemsSource="{StaticResource MyArray}" AlternationCount="100" >
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=(ItemsControl.AlternationIndex), 
                RelativeSource={RelativeSource TemplatedParent}, 
                StringFormat={}Index is {0}}">
            </TextBlock>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl >

и получите окно с тремя текстовыми блоками, такими как:

[Index is 0]
[Index is 1]
[Index is 2]

зацените

 <ItemsControl ItemsSource="{Binding Items}" Name="lista">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Vertical">
                    <TextBlock>
                        <TextBlock.Text>
                            <MultiBinding Converter="{StaticResource converter}">
                                <Binding Path="."/>
                                <Binding ElementName="lista" Path="ItemsSource"/>
                            </MultiBinding>
                        </TextBlock.Text>
                    </TextBlock>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

конвертер выглядит так

 public class conv : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        ObservableCollection<string> lista = (ObservableCollection<string>)values[1];
        return String.Concat(lista.IndexOf(values[0].ToString()), " ", values[0].ToString());
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

в результате enter image description here


вот как я получаю ItemIndex

<ItemsControl>
        <ItemsControl.Resources>
            <CollectionViewSource x:Key="ProductItems" Source="{Binding SelectedScanViewModel.Products}">
                <CollectionViewSource.SortDescriptions>
                    <componentModel:SortDescription PropertyName="ProductName" Direction="Ascending"/>
                </CollectionViewSource.SortDescriptions>
            </CollectionViewSource>
        </ItemsControl.Resources>
        <ItemsControl.ItemsSource>
            <Binding Source="{StaticResource ProductItems}"/>
        </ItemsControl.ItemsSource>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel HorizontalAlignment="Center">
                    <TextBlock Text="{Binding ProductName}" HorizontalAlignment="Center" />
                    <TextBox Name="txtFocus" Text="{Binding Qty}" MinWidth="80" HorizontalAlignment="Center"
                                     behaviors:SelectTextOnFocus.Active="True">
                        <TextBox.TabIndex>
                            <MultiBinding Converter="{StaticResource GetIndexMultiConverter}" ConverterParameter="0">
                                <Binding Path="."/>
                                <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}" Path="ItemsSource"/>
                            </MultiBinding>
                        </TextBox.TabIndex>
                    </TextBox>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Columns="{Binding SelectedScanViewModel.Products.Count}"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

и конвертер:

public class GetIndexMultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var collection = (ListCollectionView)values[1];
        var itemIndex = collection.IndexOf(values[0]);

        return itemIndex;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException("GetIndexMultiConverter_ConvertBack");
    }
}

таким образом, вы можете привязать каждый тип коллекции к ItemSource, и он будет изменен на ListCollectionView. Таким образом, конвертер будет работать для разных типов коллекций.

xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"

Я сделал это через конвертер, который вычисляет индекс добавленного элемента.

это работает только в одну сторону. Если вы каким-то образом удаляете элементы или коллекцию, вы должны использовать thomething else. И вы должны создать отдельный конвертер для каждой коллекции, элементы которой необходимо индексировать.

public class LineMultiplierConverter : IValueConverter
{
    private int m_lineIndex = 0;
    Line m_curentLine = null;

    /// <summary>
    /// Base value that will be multiplied
    /// </summary>
    public double BaseValue { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var line = value as Line;

        if (line == null)
            return BaseValue;

        bool newLine = line != m_curentLine; //check the reference because this method will called twice on one element by my binding

        if (newLine)
        {
            m_lineIndex++;
            m_curentLine = line; 
        }

        return BaseValue * m_lineIndex;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Я использую его в XAML таким образом

<UserControl.Resources>
    <sys:Double x:Key="BusinessRowHeight">22</sys:Double>
    <local:LineMultiplierConverter x:Key="LineXConverter" BaseValue="{StaticResource BusinessRowHeight}" />
</UserControl.Resources>
<ItemsControl Grid.Row="1" ItemsSource="{Binding CarBusiness}" Margin="0 5 0 0">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Line StrokeThickness="1" Stroke="LightGray"  
                    X1="0" 
                    Y1="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource LineXConverter}}" 
                    X2="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl, Mode=FindAncestor}, Path=ActualWidth}" 
                    Y2="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource LineXConverter}}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

это рисует для меня линии для каждого элемента в коллекции со смещением BaseValue для координаты X.