2009-04-13 17 views
22

Ich habe eine sortierte Listbox und muss die Zeilennummer jedes Elements anzeigen. In dieser Demo habe ich eine Person-Klasse mit einer String-Eigenschaft Name. Die Listbox zeigt eine Liste der nach Namen sortierten Personen an. Wie kann ich der Dateiliste der Listbox die Zeilennummer hinzufügen ???Nummerierte Listbox

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> 

-Code hinter:

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

Antwort

3

Die Idee in David Browns Link war, einen Wertkonverter zu verwenden, der funktionierte. Unten ist ein voll funktionsfähiges Beispiel. Das Listenfeld enthält Zeilennummern und kann nach Name und Alter sortiert werden.

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> 

-Code hinter:

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

Werteumwandler:

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, wird das nicht zu N^2 Verhalten führen? Navigieren Sie für jedes Objekt auf dem Bildschirm über alle möglichen Elemente, um den Index des aktuellen Objektindex zu finden. – Armentage

46

Endlich! Wenn man eine viel elegantere und wahrscheinlich auch bessere Leistung findet. (siehe auch Accessing an ItemsControl item as it is added)

Wir "missbrauchen" die Eigenschaft ItemsControl.AlternateIndex dafür. Ursprünglich ist vorgesehen, jede andere Zeile innerhalb einer ListBox anders zu behandeln. (Siehe http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.alternationcount.aspx)

1. Set AlternatingCount auf die Menge der Elemente in der List-Box enthalten

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

2. Binden Sie Ihre Datatemplate

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

Also AlternatingIndex das funktioniert ohne ein Konverter, ein extra CollectionViewSource und vor allem ohne Brute-Force-Suche der Quellensammlung.

+0

@yhw: Danke, eine Lösung gefunden ... – Seven

+1

Doh! Silverlight verfügt nicht über AlternationCount. Posted eine Silverlight-Lösung unten. –

+0

Ich steckte auf etwas anderes, aber es löste das Problem, gute Arbeit bro +1 – Disposer

2

Noch eine andere Antwort. Ich versuchte das oben, das in WPF funktioniert (AlternationCount Lösung), aber ich benötigte Code für Silverlight, also tat ich folgendes. Dies ist eleganter als die andere Brute-Force-Methode.

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

Und hinter

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

Die in Silverlight 5 mit der Einführung von RelativeSource Bindung neu zur Verfügung steht.

-1

Warum binden nicht nur zu zählen Eigenschaft der 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> 
Verwandte Themen