La solución obvia sería tener un número de fila de la propiedad en una vista de modelo del elemento, pero el inconveniente es que tienes que volver a generar los al agregar registros o cambiar el orden de clasificación.

Hay un elegante solución?

10 Comentarios

  1. 41

    Creo que han la solución elegante, pero funciona.

    XAML:

    <ListView Name="listviewNames">
      <ListView.View>
        <GridView>
          <GridView.Columns>
            <GridViewColumn
              Header="Number"
              DisplayMemberBinding="{Binding RelativeSource={RelativeSource FindAncestor, 
                                             AncestorType={x:Type ListViewItem}}, 
                                             Converter={StaticResource IndexConverter}}" />
            <GridViewColumn
              Header="Name"
              DisplayMemberBinding="{Binding Path=Name}" />
          </GridView.Columns>
        </GridView>
      </ListView.View>
    </ListView>

    ValueConverter:

    public class IndexConverter : IValueConverter
    {
        public object Convert(object value, Type TargetType, object parameter, CultureInfo culture)
        {
            ListViewItem item = (ListViewItem) value;
            ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
            int index = listView.ItemContainerGenerator.IndexFromContainer(item);
            return index.ToString();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    • Impresionante. Funciona a la perfección. Yo hice el cambio a agregar uno al valor del índice, de modo que no sería de base cero (tiene más sentido desde una interfaz de usuario punto de vista, creo).
    • Solución agradable! Mi único problema es que cuando cambio de la lista (por ejemplo, adición o eliminación de elementos) los números de fila no actualizar, así que al final me duplicados y los números que faltan. Alguna idea?
    • Un poco de adición: si desea que el índice de empezar a ser cambiado fácilmente (0, 1 base, lo que sea…): int paramValue; if (Int32.TryParse(parameter as string, out paramValue)) index += paramValue;
    • A continuación, en XAML, usted puede hacer: (...) Converter={StaticResource IndexConverter}, ConverterParamter=1} para una numeración a partir del 1.
    • No funciona cuando usted comience a desplazarse. el desplazamiento de los cambios en el índice.
    • No actualizar el número cuando eliminamos la fila.

  2. 6

    Si usted tiene una lista dinámica donde los elementos se agregan, eliminan o mueven, usted todavía puede usar esta muy bonito solución y simplemente dejar que la currentview de su listview actualización de sí mismo después de la relación de los cambios efectuados en su lista de origen son un hecho.
    Este ejemplo de código se elimina el elemento actual directamente en la lista origen de datos «mySourceList» (que es en mi caso una ObservableCollection) y, finalmente, las actualizaciones de los números de línea a los valores correctos .

    ICollectionView cv = CollectionViewSource.GetDefaultView(listviewNames.ItemsSource);
    if (listviewNames.Items.CurrentItem != null)
    {
        mySourceList.RemoveAt(cv.CurrentPosition);
        cv.Refresh();
    }
  3. 4

    Primer lugar es necesario establecer la AlternationCount a items count+1, por ejemplo:

    <ListView AlternationCount="1000" .... />

    Luego AlternationIndex mostrará el índice real, incluso durante el desplazamiento:

     <GridViewColumn
           Header="#" Width="30"
           DisplayMemberBinding="{Binding (ItemsControl.AlternationIndex),
           RelativeSource={RelativeSource AncestorType=ListViewItem}}" />
    • ¿Por qué un número mágico ? Si tenemos una colección también se podrían escribir <ListView AlternationCount=»{Binding ProjColl.Count}» ItemsSource=»{Binding ProjColl}» >
    • No podía conseguir que esto funcione, y que podría interferir con la alternancia de fila para colorear de todos modos.
    • Llego 0 número de índice, alguna forma de obtener el número de índice a partir de 1?
    • el tiro en un convertidor?
  4. 2

    Esto funciona como un encanto,
    Yo no sé acerca de rendimiento,
    Todavía podemos darle una oportunidad

    Crear un Multi Valor Convertidor

    public class NumberingConvertor : IMultiValueConverter
     {
      public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
       if (values != null && values.Any() && values[0] != null && values[1] != null)
       {
        //return (char)(((List<object>)values[1]).IndexOf(values[0]) + 97);
        return ((List<object>)values[1]).IndexOf(values[0]) + 1;
       }
       return "0";
      }
    
      public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
      {
       return null;
      }
     }
    }

    y
    su Xaml como este

    <ItemsControl ItemsSource="{Binding ListObjType}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Label>
                            <MultiBinding Converter="{StaticResource NumberingConvertor}">
                                <Binding Path="" />
                                <Binding Path="ItemsSource"
                                         RelativeSource="{RelativeSource AncestorType=ItemsControl}" />
                            </MultiBinding>
                        </Label>
                        <TextBlock Text="{Binding }" />
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

    Idea es enviar Objeto y lista para el convertidor y dejar que el convertidor de decidir el número. Usted puede modificar el convertidor a la pantalla de lista ordenada.

  5. 1

    Aquí es de otra manera, incluyendo el código de los comentarios que le ayudará a entender cómo funciona.

    public class Person
    {
        private string name;
        private int age;
        //Public Properties ....
    }
    
    public partial class MainWindow : Window
    {
    
        List<Person> personList;
        public MainWindow()
        {
            InitializeComponent();
    
            personList= new List<Person>();
            personList.Add(new Person() { Name= "Adam", Agen= 25});
            personList.Add(new Person() { Name= "Peter", Agen= 20});
    
            lstvwPerson.ItemsSource = personList;
    //After updates to the list use lstvwPerson.Items.Refresh();
        }
    }

    El XML

                <GridViewColumn Header="Number" Width="50" 
                    DisplayMemberBinding="{ 
                        Binding RelativeSource= {RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}},
                       DELETE Path=Content, DELETE
                        Converter={StaticResource IndexConverter}, 
                        ConverterParameter=1
                    }"/>

    RelativeSource se utiliza en particular vinculante de los casos, cuando tratamos de enlazar una propiedad de un objeto a otro la propiedad del objeto en sí mismo [1].

    Utilizando Mode=FindAncestor podemos atravesar la jerarquía de capas, y conseguir un determinado elemento, por ejemplo el ListViewItem (incluso podríamos agarrar el GridViewColumn). Si usted tiene dos ListViewItem elementos puede especificar que desea con «AncestorLevel = x».

    Camino: Aquí sólo me toma el contenido de la ListViewItem (que es mi objeto «Persona»).

    Convertidor Ya que quiero mostrar los números de fila en mi columna de Número y no con el objeto Persona que necesita para crear un Convertidor de clase que de alguna manera se puede transformar mi Persona objeto a un número correspondiente de la fila. Pero no es posible, yo sólo quería mostrar que el Camino que va al convertidor de. Eliminar la Ruta de acceso enviará el ListViewItem para el Convertidor.

    ConverterParameter Especificar un parámetro que desea pasar a la clase IValueConverter. Aquí usted puede enviar el estado si desea que el número de la fila de inicio en 0,1,100 o lo que sea.

    public class IndexConverter : IValueConverter
    {
        public object Convert(object value, Type TargetType, object parameter, System.Globalization.CultureInfo culture)
        {
            //Get the ListViewItem from Value remember we deleted Path, so the value is an object of ListViewItem and not Person
            ListViewItem lvi = (ListViewItem)value;
            //Get lvi's container (listview)
            var listView = ItemsControl.ItemsControlFromItemContainer(lvi) as ListView;
    
            //Find out the position for the Person obj in the ListView
    //we can get the Person object from lvi.Content
            //Of course you can do as in the accepted answer instead!
            //I just think this is easier to understand for a beginner.
            int index = listView.Items.IndexOf(lvi.Content);
    
            //Convert your XML parameter value of 1 to an int.
            int startingIndex = System.Convert.ToInt32(parameter);
    
            return index + startingIndex;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
  6. 1

    He encontrado la solución que funcionará incluso en el caso en el que usted necesita para mover sus elementos dentro de la colección. Así que en realidad lo que tenemos que hacer es notificar propiedad ficticia «ListNumbersNotify» cada vez que nuestra colección se cambia y se unen con todo lo que difícil MultiBinding convertidor.

    XAML:

                    <Window ...
                       x:Name="This">
                       ...
                     <ListView Name="ListViewCurrentModules">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <Label>
                                        <MultiBinding Converter="{helpers:NumberingConvertor}">
                                            <Binding Path="" />
                                            <Binding ElementName="ListViewCurrentModules" />
                                            <Binding Path="ListNumbersNotify" ElementName="This" />
                                        </MultiBinding>
                                    </Label>
                                    <Border>
                                     ...
                                    </Border>
                                </StackPanel>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>

    Convertidor:

        public abstract class MultiConvertorBase<T> : MarkupExtension, IMultiValueConverter
    where T : class, new()
    {
        public abstract object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
    
        public virtual object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (_converter == null)
                _converter = new T();
            return _converter;
        }
    
        private static T _converter = null;
    }
    
    public class NumberingConvertor : MultiConvertorBase<NumberingConvertor>
    {
        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return ((ListView)values[1]).Items.IndexOf(values[0]) + 1;
        }
    }

    Código detrás:

        public partial class AddModulesWindow: Window, INotifyPropertyChanged
        {
        ...
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string prop)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }
    
        public object ListNumbersNotify { get; }
    
        public AddModulesWindow(ICore core)
        {
            InitializeComponent();
    
            this.core = core;
            CurrentModuleInfos = new ObservableCollection<ModuleInfo>(core.Modules.Select(m => m?.ModuleInfo));
            CurrentModuleInfos.CollectionChanged += CurrentModuleTypes_CollectionChanged;
    
            ListViewCurrentModules.ItemsSource = CurrentModuleInfos;
        }
    
        private void CurrentModuleTypes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged("ListNumbersNotify");
        }
    • Por favor explicar un poco lo que están haciendo, de modo que usted puede ayudar a la OP a entender su respuesta.
    • Simplemente lo hice.
  7. 0

    Es la adición a respuesta de amaca para los problemas encontrados por Allon Guralnek y VahidN. El desplazamiento se resuelve un problema con la configuración del ListView.ItemsPanel a StackPanel en XAML:

    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>

    Esta sustitución de defecto VirtualizingStackPanel con simple StackPanel se deshabilita la regeneración de los internos de la colección de ListViewItem. Así que los índices no de forma caótica cambio cuando se desplaza. Pero esta sustitución puede disminuir el rendimiento en grandes colecciones. También, la dinámica de la numeración de los cambios que puede lograrse con la llamada CollectionViewSource.GetDefaultView(ListView.ItemsSource).Refresh() cuando ItemsSource colección cambiado. Al igual que con ListView filtrado. Cuando traté de agregar controlador a través de esta convocatoria en el evento INotifyCollectionChanged.CollectionChanged mi ListView de salida fue la duplicación de último añadido fila (pero con la correcta numeración). Arreglado esto mediante la colocación de actualización de la llamada después de cada cambio de colección en el código. Mala solución, pero funciona perfecto para mí.

  8. 0

    amaca respuesta es ideal para las listas estáticas. Dinámica:

    1. Debemos utilizar MultiBinding, segundo enlace es para el cambio de colección;
    2. Después de eliminar ItemsControl no contiene el objeto eliminado, pero ItemContainerGenerator contiene.
      Convertidor de listas dinámicas (yo lo uso para TabControl TabItem s):

      public class TabIndexMultiConverter : MultiConverterBase
      {
         public override object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
         {
            TabItem tabItem = value.First() as TabItem;
            ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(tabItem);
            object context = tabItem?.DataContext;
      
            int idx = ic == null || context == null //if all objects deleted
                   ? -1
                   : ic.Items.IndexOf(context) + 1;
      
            return idx.ToString(); //ToString necessary
         }
      }
  9. 0

    Aquí está mi pequeño convertidor de que grandes obras como de WPF en 2017 con .NET 4.7.2, incluso con el VirtualizingStackPanel totalmente habilitada:

    [ValueConversion(typeof(IList), typeof(int))]
    public sealed class ItemIndexConverter : FrameworkContentElement, IValueConverter
    {
        public Object Convert(Object data_item, Type t, Object p, CultureInfo _) =>
            ((IList)DataContext).IndexOf(data_item);
    
        public Object ConvertBack(Object o, Type t, Object p, CultureInfo _) =>
            throw new NotImplementedException();
    };

    Agregar una instancia de este IValueConverter a la Resources de la GridViewColumn.CellTemplate, o en otro lugar. O bien, crear una instancia in situ en el Binding de los elementos enlazados, como muestro aquí. En cualquier caso, usted necesita crear una instancia de la ItemIndexConverter y no te olvides de enlazar toda la colección de origen a la misma. Aquí estoy tirando de una referencia a la colección de origen de la ItemsSource propiedad de la ListView–pero esto conlleva algunos problemas relacionados sobre el acceso a la XAML de la raíz, así que si usted tiene una mejor y más fácil camino para referirse a la colección de origen, usted debe hacerlo.

    Como para acceder a una propiedad en la XAML de la raíz, el ListView raíz en XAML se le da el nombre w_root, y el XAML 2009 extensión de marcado {x:Reference ...} se utiliza para acceder a la XAML elemento raíz. No creo que «ElementName» unión va a trabajar aquí, ya que la referencia ocurre en una plantilla de contexto.

    <ListView x:Class="myApp.myListView"
        x:Name="w_root"
        xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:myApp"
        VirtualizingStackPanel.IsVirtualizing="True" 
        VirtualizingStackPanel.VirtualizationMode="Recycling">
    
        <ListView.View>
           <GridView>
              <GridViewColumn Width="50">
                 <GridViewColumn.CellTemplate>
                    <DataTemplate>
                       <TextBlock>
                          <TextBlock.Text>
                             <Binding>
                                <Binding.Converter>
                                   <local:ItemIndexConverter DataContext="{Binding 
                                        Source={x:Reference w_root},
                                        Path=(ItemsControl.ItemsSource)}" />
                               </Binding.Converter>
                            </Binding>
                         </TextBlock.Text>
                      </TextBlock>
                   </DataTemplate>
                </GridViewColumn.CellTemplate>
             </GridViewColumn>
          </GridView>
       </ListView.View>
    </ListView>

    Que es! Parece que funciona bastante rápido con un gran número de filas, y de nuevo, se puede ver que el reporte de los índices están en lo correcto cuando arbitrariamente de desplazamiento de alrededor, y que VirtualizingStackPanel.IsVirtualizing es, de hecho, establece en True.

    Cómo mostrar los números de fila en un ListView?

    No que el siguiente es realmente necesario, pero aviso que el xmlns= declaración de WPF se actualiza para indicar XAML 2009, en apoyo de la {x:Reference} de uso mencionados anteriormente. Hacer notar que hay dos cambios; «/winfx/» tiene que ser cambiado a «/netfx/» cuando el cambio de «2006» a «2009».

  10. 0

    Por la siguiente respuesta como la mejor solución que he encontrado un problema cuando los índices todavía no se actualiza después de la extracción/sustitución de los elementos dentro de la vista de lista. Para resolver que no hay uno que no está tan claro indicio (propongo que se utilice en pequeñas colecciones): después de la ejecución de elemento de extracción/sustitución debe invocar ObservableCollection(INotifyCollectionChanged).CollectionChanged evento con Reset acción. Esto es posible de hacer con la ampliación de la existente ObservableCollection, que es ItemsSource o reflexión de uso cuando esto no es posible.

    Ex.

    public class ResetableObservableCollection<T> : ObservableCollection<T>
    {
            public void NotifyReset()
            {
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
    }
    
    
    private void ItemsRearranged() 
    {
        Items.NotifyReset();
    }

Dejar respuesta

Please enter your comment!
Please enter your name here