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>
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.
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 .
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>