Есть некоторый список с данными. Нужно отобразить его, добавив автонумерацию пунктов списка.
Как сделать автонумерацию пунктов в списке?
Лучший ответ:
Есть решение, но выглядит не очень красиво (источник приводить не буду, потому что это гнусный проприетарный сайт). Для примера рассмотрим список из двух колонок, в первой — номер строки, во второй — текст:
<ListBox ItemsSource="{Binding ItemsSourceField}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Text= "{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem},
Converter={StaticResource rowIndexConverter}}"/>
<TextBlock
Grid.Row="0"
Grid.Column="1"
Text="{Binding Text}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Код конвертера:
public class RowIndexConverter: IValueConverter
{
public object Convert(
object value, Type targetType,
object parameter, CultureInfo culture)
{
ListBoxItem item = value as ListBoxItem;
ListBox view = ItemsControl.ItemsControlFromItemContainer(item) as ListBox;
int index = view.ItemContainerGenerator.IndexFromContainer(item);
return index.ToString();
}
public object ConvertBack(
object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new Exception(...);
}
}
Если коллекция-источник для списка может изменяться, то после изменения нужно выполнить код для синхронизации:
ICollectionView view =
CollectionViewSource.GetDefaultView(ItemsSourceField)
as ListCollectionView;
view.Refresh();
Есть еще один вариант — в статье Rownumber in Silverlight Datagrid or Listbox
Новые ответы
Да, я тоже нашел этот вариант и остановился на моменте обновления коллекции. Никак не мог придумать, как это сделать. Да и сейчас думаю, на какое событие ListBox повесить данное обновление. Как ни странно, ничего подобного у ListBox найти не могу.
Вообще, текстовое поле с номером строки будет обновляться, если будет обновляться то значение, на которое оно забайндено. В вышеприведенном примере оно забайндено на сам ListBoxItem, а уж конвертер преобразует ListBoxItem в его порядковый номер в ListBox. Пока не изменится ListBoxItem, номер не изменится.
Вот и возникла идея забайндить текстовое поле на количество строк в списке. При изменении списка количество строк изменяется, соответственно будет обновляться и значение в текстовом поле. Но тут несложно заметить некоторую неувязку - что мы получим в текстовом поле? Правильно, кол-во элементов в списке, что нам в общем-то не нужно. Однако проблема обновления решится. В конечном счете то, что будет отражено в текстовом поле, возвращает конвертер. В данном случае на вход ему подалось бы число элементов в списке, что не несет для нас никакой пользы. Однако тут есть еще одна лазейка - конвертору на вход можно подавать еще и дополнительный параметр. Синтаксис будет следующий:
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=ListBox}, Path=Items.Count, Converter={StaticResource myConverterKey},
ConverterParameter=и_вот_тут_вопрос}"/>
Непонятно, как туда подать ListBoxItem. На сколько я понял, байндинг внутри байндинга использовать нельзя.