2009-04-13 8 views
22

Ho una casella di riepilogo ordinata e devo visualizzare il numero di riga di ogni articolo. In questa demo ho una classe Person con una proprietà String di nome. La casella di riepilogo visualizza una lista di Persone ordinate per Nome. Come posso aggiungere al datatemplate della listbox il numero di riga ???casella di elenco numerata

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> 

codice dietro:

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

risposta

3

L'idea nel collegamento di David Brown era di utilizzare un convertitore di valore che funzionava. Di seguito è riportato un esempio funzionante completo. La casella di riepilogo ha numeri di riga e può essere ordinata sia per nome che per età.

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> 

codice dietro:

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

convertitore Valore:

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, questo non porterà a un comportamento N^2? Per ogni articolo sullo schermo, scorrere tutti gli elementi possibili per trovare l'indice dell'indice corrente dell'articolo. – Armentage

46

Finalmente! Se trovato un modo molto più elegante e probabilmente con prestazioni migliori sia. (vedere anche Accessing an ItemsControl item as it is added)

Per "utilizzare impropriamente" la proprietà ItemsControl.AlternateIndex per questo. Originariamente è destinato a gestire ogni altra riga all'interno di un ListBox in modo diverso. (Vedi http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.alternationcount.aspx)

1. Impostare AlternatingCount alla quantità di elementi contenuti nel ListBox

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

2. Associare al AlternatingIndex tuo DataTemplate

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

Quindi questo funziona senza un convertitore, un extra CollectionViewSource e, soprattutto, senza la ricerca della forza bruta nella raccolta di sorgenti.

+0

@yhw: Grazie, ho trovato una soluzione ... – Seven

+1

Doh! Silverlight non ha AlternationCount. Di seguito una soluzione Silverlight. –

+0

Ho bloccato qualcos'altro ma ha risolto il problema, buon lavoro bro +1 – Disposer

2

Un'altra risposta. Ho provato quanto sopra, che funziona in WPF (soluzione AlternationCount), ma avevo bisogno del codice per Silverlight, quindi ho fatto quanto segue. Questo è più elegante dell'altro metodo di forza 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> 

E dietro

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

Questo è nuovamente disponibile in Silverlight 5 con l'introduzione di RelativeSource vincolante.

-1

Perché non limitarsi a contare le proprietà della 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> 
Problemi correlati