WPF - 从 ItemsControl 的 ItemTemplate 绑定到项目索引?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3451422/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF - Bind to Item Index from within ItemTemplate of ItemsControl?
提问by Rachel
Is there a way to bind to the ItemIndex from within the ItemTemplate of an ItemsControl?
有没有办法从 ItemsControl 的 ItemTemplate 中绑定到 ItemIndex?
For example:
例如:
<ItemsControl ItemsSource="{Binding Path=ItemList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ThisItemsIndex}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
回答by John Bowen
If you're not using any type of alternating row styles you might be able to hiHyman the AlternationIndex for this. Set AlternationCount on your ItemsControl to something greater than the max possible count of your items and then use
如果您不使用任何类型的交替行样式,您可能可以为此劫持 AlternationIndex。将 ItemsControl 上的 AlternationCount 设置为大于项目的最大可能计数,然后使用
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(ItemsControl.AlternationIndex)}"
Edit: As bradgonesurfing pointed out in comments, this is not recommended if you're using virtualization, as it will only index the items that are generated and not the entire list.
编辑:正如bradgonesurfing 在评论中指出的那样,如果您使用虚拟化,则不建议这样做,因为它只会索引生成的项目,而不是整个列表。
回答by EnderWiggin
Here is a method I used to add a bindable index on a collection item. I basically wrap my item in a container that has an index, and have a custom ObservableCollection that accepts the wrapper.
这是我用来在集合项上添加可绑定索引的方法。我基本上将我的项目包装在一个具有索引的容器中,并有一个接受包装器的自定义 ObservableCollection。
Note that MoveItemis not overridden, but would have to be for a complete implementation.
请注意,MoveItem不会被覆盖,但必须是完整的实现。
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; }
}
I then extend my wrapper class to get a bindable property that I have control over how the index is displayed:
然后我扩展我的包装类以获得一个可绑定的属性,我可以控制索引的显示方式:
public class NamedIndexedItemContainer<T> : IndexedItemContainer<T>
{
public string Name
{
get { return string.Format("Item #{0}", Index + 1); }
}
}
Sample Usage
示例用法
XAML:
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);
Of course, any binding with the actual MyItem instance would have to go through the IndexedItemContainer's Itemproperty.
当然,任何与实际 MyItem 实例的绑定都必须通过 IndexedItemContainer 的Item属性。
回答by Maciej Wozniak
For the record, there is another way to accomplish this: using custom Converter. A little bit more complicated, but you do not have to worry about AlternationCount/Index.
为了记录,还有另一种方法可以实现此目的:使用自定义Converter。稍微复杂一点,但您不必担心 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;
}
}
Sample usage:
示例用法:
<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>