WPF - Lier à l'Index de l'Élément à partir de l'intérieur de ItemTemplate de ItemsControl?

y a-t-il un moyen de lier ItemIndex à partir de L'ItemTemplate d'un ItemsControl?

par exemple:

<ItemsControl ItemsSource="{Binding Path=ItemList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=ThisItemsIndex}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
23
demandé sur Rachel 2010-08-10 20:57:46

3 réponses

si vous n'utilisez aucun type de styles de rangées alternées, vous pourriez être en mesure de détourner L'index D'Alternationpour cela. Réglez AlternationCount sur votre ItemsControl à quelque chose de plus grand que le nombre maximum possible de vos articles et ensuite utiliser

Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(ItemsControl.AlternationIndex)}"

Edit: comme bradgonesurfing souligné dans les commentaires , ce n'est pas recommandé si vous utilisez la virtualisation, car il ne indexera que les éléments qui sont générés et non la liste entière.

26
répondu John Bowen 2017-05-23 12:34:27

Voici une méthode que j'ai utilisée pour ajouter un index bindable sur un article de collection. En gros, j'enroule mon article dans un contenant qui a un index, et j'ai une collecte observable personnalisée qui accepte l'enrubannage.

Note que MoveItem n'est pas remplacé, mais pour une mise en œuvre complète.

public class IndexedItemContainerCollection<T> : ObservableCollection<IndexedItemContainer<T>>
{
    public IndexedItemContainerCollection()
    {

    }

    public IndexedItemContainerCollection(IEnumerable<IndexedItemContainer<T>> collection)
        : base(collection)
    {
        var index = 0;
        foreach (var item in this)
        {
            item.Index = index;
        }
    }

    protected override void InsertItem(int index, IndexedItemContainer<T> item)
    {
        item.Index = index;
        base.InsertItem(index, item);
        foreach (var indexedItem in this.Where(x=>x.Index > index))
        {
            indexedItem.Index++;
        }
    }

    protected override void RemoveItem(int index)
    {
        base.RemoveItem(index);
        foreach (var indexedItem in this.Where(x => x.Index > index))
        {
            indexedItem.Index--;
        }
    }

}

public class IndexedItemContainer<T>
{
    public int Index { get; set; }
    public T Item { get; set; }
}

j'étends ma classe wrapper pour obtenir une propriété liable que j'ai le contrôle sur la façon dont l'indice est affiché:

public class NamedIndexedItemContainer<T> : IndexedItemContainer<T>
{
    public string Name
    {
        get { return string.Format("Item #{0}", Index + 1); }
    }
}

Exemple D'Utilisation

XAML:

    <ComboBox ItemsSource="{Binding ItemList}">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}" />
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>

Code:

private IndexedItemContainerCollection<MyItem> _itemList;
public IndexedItemContainerCollection<MyItem> ItemList
{
    get { return _itemList; }
    set { _itemList= value; OnPropertyChanged(); }
}


ItemList = new IndexedItemContainerCollection<MyItem>();
var newItem = new NamedIndexedItemContainer<MyItem>() { Item = new MyItem() { ... } };
ItemList.Add(newItem);

bien sûr, toute liaison avec L'instance MyItem actuelle devrait passer par la propriété de IndexedItemContainer" Item .

3
répondu EnderWiggin 2013-04-16 02:27:36

pour l'enregistrement, il y a une autre façon d'accomplir ceci: en utilisant personnalisé convertisseur . Un peu plus compliqué, mais vous n'avez pas à vous soucier D'AlternationCount/Index.

public sealed class ArrayWrapperConverter : IValueConverter
{
    private static readonly Type ArrayWrappingHelperType = typeof(ArrayWrappingHelper<>);

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
        {
            return null;
        }

        Type valueType = value.GetType();
        if (!valueType.IsArray)
        {
            return DependencyProperty.UnsetValue;
        }

        Type elementType = valueType.GetElementType();
        Type specificType = ArrayWrappingHelperType.MakeGenericType(elementType);

        IEnumerable wrappingHelper = (IEnumerable) Activator.CreateInstance(specificType, value);
        return wrappingHelper;
    }

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

internal class ArrayWrappingHelper<TValue> : IEnumerable
{
    private readonly TValue[] _array;

    public ArrayWrappingHelper(object array)
    {
        _array = (TValue[]) array;
    }

    public IEnumerator GetEnumerator()
    {
        return _array.Select((item, index) => new ArrayItemWrapper<TValue>(_array, index)).GetEnumerator();
    }
}

public class ArrayItemWrapper<TValue>
{
    private readonly TValue[] _array;
    private readonly int _index;

    public int Index 
    {
        get { return _index; }
    }

    public TValue Value
    {
        get { return _array[_index]; }
        set { _array[_index] = value; }
    }

    public ArrayItemWrapper(TValue[] array, int index)
    {
        _array = array;
        _index = index;
    }
}

exemple d'utilisation:

<Window x:Class="WpfArrayBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:WpfArrayBinding.Converters"
        xmlns:s="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <ResourceDictionary>
            <c:ArrayWrapperConverter x:Key="ArrayWrapperConverter" />

            <x:Array Type="{x:Type s:String}" x:Key="MyArray">
                <s:String>Foo</s:String>
                <s:String>Bar</s:String>
                <s:String>Baz</s:String>
            </x:Array>
    </ResourceDictionary>
    </Window.Resources>

    <ItemsControl ItemsSource="{Binding Source={StaticResource MyArray}, Converter={StaticResource ArrayWrapperConverter}}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>                
                <StackPanel Orientation="Horizontal">
                    <Label Content="{Binding Index}" />
                    <TextBox Text="{Binding Value}" />
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>
2
répondu Maciej Wozniak 2013-01-22 19:01:43