2009-04-13 19 views
22

Tengo un listbox ordenado y necesito mostrar el número de fila de cada artículo. En esta demostración, tengo una clase de persona con una propiedad de cadena de nombre. El cuadro de lista muestra una lista de Personas ordenadas por nombre. ¿Cómo puedo agregar a la plantilla de datos del cuadro de lista el número de fila?Listbox numerado

XAML:

<Window x:Class="NumberedListBox.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Height="300" Width="300"> 
    <ListBox 
     ItemsSource="{Binding Path=PersonsListCollectionView}" 
     HorizontalContentAlignment="Stretch"> 
     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <TextBlock Text="{Binding Path=Name}" /> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
    </ListBox> 
</Window> 

Código atrás:

using System; 
using System.Collections.ObjectModel; 
using System.Windows.Data; 
using System.Windows; 
using System.ComponentModel; 

namespace NumberedListBox 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 

      Persons = new ObservableCollection<Person>(); 
      Persons.Add(new Person() { Name = "Sally"}); 
      Persons.Add(new Person() { Name = "Bob" }); 
      Persons.Add(new Person() { Name = "Joe" }); 
      Persons.Add(new Person() { Name = "Mary" }); 

      PersonsListCollectionView = new ListCollectionView(Persons); 
      PersonsListCollectionView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending)); 

      DataContext = this; 
     } 

     public ObservableCollection<Person> Persons { get; private set; } 
     public ListCollectionView PersonsListCollectionView { get; private set; } 
    } 

    public class Person 
    { 
     public string Name { get; set; } 
    } 
} 

Respuesta

3

La idea en el enlace de David Brown era usar un convertidor de valor que funcionó. A continuación se muestra una muestra completa de trabajo. El cuadro de lista tiene números de fila y se puede ordenar tanto por nombre como por antigüedad.

XAML:

<Window x:Class="NumberedListBox.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:NumberedListBox" 
    Height="300" Width="300"> 

    <Window.Resources> 

     <local:RowNumberConverter x:Key="RowNumberConverter" /> 

     <CollectionViewSource x:Key="sortedPersonList" Source="{Binding Path=Persons}" /> 

    </Window.Resources> 

    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition /> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition /> 
      <ColumnDefinition /> 
     </Grid.ColumnDefinitions> 
     <ListBox 
      Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" 
      ItemsSource="{Binding Source={StaticResource sortedPersonList}}" 
      HorizontalContentAlignment="Stretch"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <StackPanel Orientation="Horizontal"> 
         <TextBlock 
          Text="{Binding Converter={StaticResource RowNumberConverter}, ConverterParameter={StaticResource sortedPersonList}}" 
          Margin="5" /> 
         <TextBlock Text="{Binding Path=Name}" Margin="5" /> 
         <TextBlock Text="{Binding Path=Age}" Margin="5" /> 
        </StackPanel> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 
     <Button Grid.Row="1" Grid.Column="0" Content="Name" Tag="Name" Click="SortButton_Click" /> 
     <Button Grid.Row="1" Grid.Column="1" Content="Age" Tag="Age" Click="SortButton_Click" /> 
    </Grid> 
</Window> 

Código atrás:

using System; 
using System.Collections.ObjectModel; 
using System.Windows.Data; 
using System.Windows; 
using System.ComponentModel; 
using System.Windows.Controls; 

namespace NumberedListBox 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 

      Persons = new ObservableCollection<Person>(); 
      Persons.Add(new Person() { Name = "Sally", Age = 34 }); 
      Persons.Add(new Person() { Name = "Bob", Age = 18 }); 
      Persons.Add(new Person() { Name = "Joe", Age = 72 }); 
      Persons.Add(new Person() { Name = "Mary", Age = 12 }); 

      CollectionViewSource view = FindResource("sortedPersonList") as CollectionViewSource; 
      view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending)); 

      DataContext = this; 
     } 

     public ObservableCollection<Person> Persons { get; private set; } 

     private void SortButton_Click(object sender, RoutedEventArgs e) 
     { 
      Button button = sender as Button; 
      string sortProperty = button.Tag as string; 
      CollectionViewSource view = FindResource("sortedPersonList") as CollectionViewSource; 
      view.SortDescriptions.Clear(); 
      view.SortDescriptions.Add(new SortDescription(sortProperty, ListSortDirection.Ascending)); 

      view.View.Refresh(); 
     } 
    } 

    public class Person 
    { 
     public string Name { get; set; } 
     public int Age { get; set; } 
    } 
} 

convertidor Valor:

using System; 
using System.Windows.Data; 

namespace NumberedListBox 
{ 
    public class RowNumberConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      CollectionViewSource collectionViewSource = parameter as CollectionViewSource; 

      int counter = 1; 
      foreach (object item in collectionViewSource.View) 
      { 
       if (item == value) 
       { 
        return counter.ToString(); 
       } 
       counter++; 
      } 
      return string.Empty; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
} 
+12

Uhhh, ¿esto no dará como resultado el comportamiento N^2? Para cada elemento en la pantalla, itere sobre todos los elementos posibles para encontrar el índice del índice del artículo actual. – Armentage

46

Finalmente! Si se encuentra de una manera mucho más elegante y, probablemente, con un mejor rendimiento tampoco. (vea también Accessing an ItemsControl item as it is added)

"Malversamos" la propiedad ItemsControl.AlternateIndex para esto. Originalmente está destinado a manejar cada dos filas dentro de un ListBox de manera diferente. (Ver http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.alternationcount.aspx)

1. Conjunto AlternatingCount a la cantidad de artículos que figuran en el cuadro de lista

<ListBox ItemsSource="{Binding Path=MyListItems}" 
     AlternationCount="{Binding Path=MyListItems.Count}" 
     ItemTemplate="{StaticResource MyItemTemplate}" 
... 
/> 

2. Enlazar a AlternatingIndex su DataTemplate

<DataTemplate x:Key="MyItemTemplate" ... > 
    <StackPanel> 
     <Label Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplatedParent.(ItemsControl.AlternationIndex)}" /> 
     ... 
    </StackPanel> 
</DataTemplate> 

Así que esto funciona sin un convertidor, un extra CollectionViewSource y lo más importante sin una búsqueda de fuerza bruta en la colección fuente.

+0

@yhw: Gracias, encontré una solución ... – Seven

+1

¡Doh! Silverlight no tiene AlternationCount. Publicó una solución Silverlight a continuación. –

+0

Me quedé atrapado en otra cosa, pero resolvió el problema, buen trabajo hermano +1 – Disposer

2

Otra respuesta más. Intenté lo anterior, que funciona en WPF (solución AlternationCount), pero necesitaba código para Silverlight, así que hice lo siguiente. Esto es más elegante que el otro método de fuerza bruta.

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:RowNumber" x:Name="userControl" 
    x:Class="RowNumber.MainPage" Width="640" Height="480"> 
<Grid x:Name="LayoutRoot" Background="White"> 
    <ListBox ItemsSource="{Binding Test, ElementName=userControl}"> 
    <ListBox.Resources> 
     <local:ListItemIndexConverter x:Key="IndexConverter" /> 
    </ListBox.Resources> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <StackPanel Orientation="Horizontal"> 
       <TextBlock Width="30" 
        Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Converter={StaticResource IndexConverter}}" /> 
       <TextBlock Text="{Binding}" /> 
      </StackPanel> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
    </ListBox> 
</Grid> 
</UserControl> 

Y detrás

using System; 
    using System.Collections.Generic; 
    using System.Globalization; 
    using System.Linq; 
    using System.Windows.Controls; 
    using System.Windows.Controls.Primitives; 
    using System.Windows.Data; 

    namespace RowNumber 
    { 
    public class ListItemIndexConverter : IValueConverter 
    { 
     // Value should be ListBoxItem that contains the current record. RelativeSource={RelativeSource AncestorType=ListBoxItem} 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      var lbi = (ListBoxItem)value; 
      var listBox = lbi.GetVisualAncestors().OfType<ListBox>().First(); 
      var index = listBox.ItemContainerGenerator.IndexFromContainer(lbi); 
      // One based. Remove +1 for Zero based array. 
      return index + 1; 
     } 
     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } 
    } 
    public partial class MainPage : UserControl 
    { 
     public MainPage() 
     { 
      // Required to initialize variables 
      InitializeComponent(); 
     } 
     public List<string> Test { get { return new[] { "Foo", "Bar", "Baz" }.ToList(); } } 
    } 
    } 

Ésta es nuevamente disponible en Silverlight 5, con la introducción de RelativeSource vinculante.

-1

¿Por qué no solo vinculante para contar la propiedad del listbox?

<ListView x:Name="LstFocusImageDisplayData" 

          > 
        <ListView.ItemTemplate> 
         <DataTemplate> 
          <GroupBox Header="Item Count"> 
           <DockPanel> 
            <TextBlock Text="{Binding ElementName=LstFocusImageDisplayData, Path=Items.Count, Mode=OneTime}" /> 
           </DockPanel> 
          </GroupBox> 

         </DataTemplate> 
        </ListView.ItemTemplate> 
       </ListView>