【问题标题】:WPF - Bind to Item Index from within ItemTemplate of ItemsControl?WPF - 从 ItemsControl 的 ItemTemplate 中绑定到项目索引?
【发布时间】:2011-03-27 22:57:40
【问题描述】:

有没有办法从 ItemsControl 的 ItemTemplate 中绑定到 ItemIndex?

例如:

<ItemsControl ItemsSource="{Binding Path=ItemList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=ThisItemsIndex}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

【问题讨论】:

    标签: wpf data-binding itemscontrol


    【解决方案1】:

    如果您没有使用任何类型的交替行样式,您可能会为此劫持 AlternationIndex。将 ItemsControl 上的 AlternationCount 设置为大于项目的最大可能计数,然后使用

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

    编辑:作为bradgonesurfing pointed out in comments,如果您使用虚拟化,则不建议这样做,因为它只会索引生成的项目而不是整个列表。

    【讨论】:

    • 有帮助,但它从 0 开始,除非 UI 是为程序员设计的,否则这不是很有用。我想大多数想要这个的人都希望它从 1 开始。
    【解决方案2】:

    这是我用来在集合项上添加可绑定索引的方法。我基本上将我的项目包装在一个具有索引的容器中,并有一个接受包装器的自定义 ObservableCollection。

    请注意,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; }
    }
    

    然后我扩展我的包装类以获得一个可绑定的属性,我可以控制索引的显示方式:

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

    示例用法

    XAML:

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

    代码:

    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);
    

    当然,与实际 MyItem 实例的任何绑定都必须通过 IndexedItemContainer 的 Item 属性。

    【讨论】:

    • 谢谢。我通常不喜欢从ObservableCollection&lt;T&gt; 继承,但是如果我将来需要这样的东西,我会记住这一点:)
    【解决方案3】:

    为了记录,还有另一种方法可以做到这一点:使用自定义转换器。稍微复杂一点,但您不必担心 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;
        }
    }
    

    示例用法:

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

    【讨论】:

    • 谢谢,下次我需要 ItemsControl 中的 ItemIndex 时,我将不得不试一试 :) 您在使用此功能时遇到任何问题吗?
    • 没有,但我只在相对较小的数组中使用它。
    猜你喜欢
    • 2010-12-03
    • 2014-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 1970-01-01
    • 2013-04-13
    相关资源
    最近更新 更多