There is a way to do this without a converter and with the ability to have duplicated items in the collection, but it means that you have to maintain an organized list that also stores the index by using KeyValuePair<int, T>
as list item type.
Here is a sample implementation for a String list. It will show the text inside the buttons and will bind the index on the command parameter:
#region Items
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(CulturePicker),
new FrameworkPropertyMetadata(new ObservableCollection<string>(),
FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(OnItemsChanged)));
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
private static void OnItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CulturePicker _this = (CulturePicker)d;
ObservableCollection<string> oldItems = (ObservableCollection<string>)e.OldValue;
ObservableCollection<string> newItems = _this.Items;
if (oldItems != null)
{
oldItems.CollectionChanged -= this.Items_CollectionChanged;
}
List<KeyValuePair<int, string>> organizedItems = new List<KeyValuePair<int, string>>();
for (int i = 0; i < newItems.Count; i++)
{
organizedItems.Add(new KeyValuePair<int, string>(i, newItems[i]));
}
this.OrganizedItems = organizedItems;
newItems.CollectionChanged += this.Items_CollectionChanged;
}
private void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
List<KeyValuePair<int, string>> organizedItems = new List<KeyValuePair<int, string>>();
for (int i = 0; i < e.NewItems.Count; i++)
{
organizedItems.Add(new KeyValuePair<int, string>(i, (string)e.NewItems[i]));
}
this.OrganizedItems = organizedItems;
}
#endregion
#region OrganizedItems
/// <summary>
/// OrganizedItems Dependency Property
/// </summary>
private static readonly DependencyProperty OrganizedItemsProperty =
DependencyProperty.Register("OrganizedItems", typeof(List<KeyValuePair<int, string>>), typeof(CulturePicker),
new FrameworkPropertyMetadata((List<KeyValuePair<int, string>>)null,
FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(OnOrganizedItemsChanged)));
/// <summary>
/// Gets or sets the OrganizedItems property. This dependency property
/// indicates an organized dictionary with the index of the Items as key and the region itself as value.
/// </summary>
private List<KeyValuePair<int, string>> OrganizedItems
{
get { return (List<KeyValuePair<int, string>>)GetValue(OrganizedItemsProperty); }
set { SetValue(OrganizedItemsProperty, value); }
}
/// <summary>
/// Handles changes to the OrganizedItems property.
/// </summary>
private static void OnOrganizedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CulturePicker _this = (CulturePicker)d;
List<KeyValuePair<int, string>> oldOrganizedItems = (List<KeyValuePair<int, string>>)e.OldValue;
List<KeyValuePair<int, string>> newOrganizedItems = _this.OrganizedItems;
}
#endregion
<UserControl ...
Name="_">
...
<ItemsControl ItemsSource="{Binding OrganizedItems, ElementName=_}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command, ElementName=_}"
CommandParameter="{Binding Key}"
Text="{Binding Value}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...
In the XAML Key is the index and Value is the actual item, in this case a the string. The Command
property itself is not included in this example. Also please note that it will re-create the organized list on any change of the source list which will trigger the re-render and cause slowness on big lists.