2010-08-10 7 views
23

Gibt es eine Möglichkeit zum ItemIndex zu binden, aus dem ItemTemplate eines Items?WPF - Bindung an Artikelindex von ItemTemplate von ItemsControl?

Zum Beispiel:

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

Antwort

26

Wenn Sie nicht jede Art von abwechselnder Reihe Stilen verwenden Sie könnten in der Lage sein, das Alter dafür kapern. Set Alter auf Itemscontrol etwas größer ist als die maximal mögliche Zahl Ihrer Artikel und verwenden Sie dann

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

Edit: Wie bradgonesurfing pointed out in comments, ist dies nicht empfehlenswert, wenn Sie die Virtualisierung verwenden, wie es indiziert nur die Elemente, die werden generiert und nicht die gesamte Liste.

+0

Vielen Dank, dass nur gut in meiner Situation gearbeitet! – Rachel

+4

Tun Sie das nicht. Siehe http://stackoverflow.com/questions/6511180/wpf-itemscontrol-the-current-listitem-index-in-the-itemssource/17962582#17962582 – bradgonesurfing

+0

hilfreich, aber es beginnt bei 0, was es sei denn, nicht sehr nützlich ist Die Benutzeroberfläche ist für Programmierer gedacht. Ich stelle mir die meisten Leute wollen dies wollen es bei 1. – Chris

1

Für die Aufzeichnung gibt es eine andere Möglichkeit, dies zu erreichen: benutzerdefinierte Converter. Ein bisschen komplizierter, aber Sie müssen sich nicht um AlternationCount/Index kümmern.

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

Verwendungsbeispiel:

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

Dank beginnen, ich werde dies beim nächsten Mal einen Schuss geben haben muss ich die ItemIndex in einem Items :) Haben Sie Probleme mit der Leistung hatte dies mit? – Rachel

+0

Nein, aber ich habe es nur mit relativ kleinen Arrays verwendet. –

3

Hier ist eine Methode, die ich verwendet, um einen bindungsfähigen Index für eine Sammlung Element hinzuzufügen. Ich verpacke mein Element im Grunde in einen Container mit einem Index und verwende eine benutzerdefinierte ObservableCollection, die den Wrapper akzeptiert.

Beachten Sie, dass MoveItem nicht überschrieben wird, aber für eine vollständige Implementierung erforderlich wäre.

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

ich dann meine Wrapper-Klasse erweitern, um eine bindbare Eigenschaft zu erhalten, die ich Kontrolle darüber haben, wie der Index angezeigt wird:

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

Probe Nutzungs

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

Natürlich kann jedes mit der tatsächlichen MyItem Instanz verbindlich müßte die IndexedItemContainer geht durch Artikel Eigenschaft.

+0

Danke. Ich in der Regel nicht gerne von 'ObservableCollection vererben ', aber werde ich dies im Auge behalten, wenn ich so etwas wie dies in der Zukunft brauche :) – Rachel