C# 编号列表框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/745568/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 23:33:47  来源:igfitidea点击:

Numbered listbox

c#wpflistbox

提问by Wallstreet Programmer

I have a sorted listbox and need to display each item's row number. In this demo I have a Person class with a Name string property. The listbox displays a a list of Persons sorted by Name. How can I add to the datatemplate of the listbox the row number???

我有一个排序的列表框,需要显示每个项目的行号。在这个演示中,我有一个带有 Name 字符串属性的 Person 类。列表框显示按姓名排序的人员列表。如何将行号添加到列表框的数据模板中???

XAML:

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

后面的代码:

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

采纳答案by David Brown

This should get you started:

这应该让你开始:

http://weblogs.asp.net/hpreishuber/archive/2008/11/18/rownumber-in-silverlight-datagrid-or-listbox.aspx

http://weblogs.asp.net/hpreishuber/archive/2008/11/18/rownumber-in-silverlight-datagrid-or-listbox.aspx

It says it's for Silverlight, but I don't see why it wouldn't work for WPF. Basically, you bind a TextBlock to your data and use a custom value converter to output the current item's number.

它说它适用于 Silverlight,但我不明白为什么它不适用于 WPF。基本上,您将 TextBlock 绑定到您的数据并使用自定义值转换器输出当前项目的编号。

回答by Wallstreet Programmer

The idea in David Brown's link was to use a value converter which worked. Below is a full working sample. The list box has row numbers and can be sorted on both name and age.

David Brown 链接中的想法是使用一个有效的值转换器。下面是一个完整的工作示例。列表框有行号,可以按姓名和年龄排序。

XAML:

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

后面的代码:

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

Value converter:

价值转换器:

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

回答by Seven

Finally! If found a way much more elegant and probably with better performance either. (see also Accessing an ItemsControl item as it is added)

最后!如果找到一种更优雅并且可能具有更好性能的方法。(另请参阅在添加 ItemsControl 项时访问它

We "misuse" the property ItemsControl.AlternateIndexfor this. Originally it is intended to handle every other row within a ListBoxdifferently. (see http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.alternationcount.aspx)

我们ItemsControl.AlternateIndex为此“滥用”了该属性。最初它旨在以不同的方式处理每隔一行ListBox。(参见http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.alternationcount.aspx

1. Set AlternatingCount to the amount of items contained in the ListBox

1. 将 AlternatingCount 设置为 ListBox 中包含的项目数量

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

2. Bind to AlternatingIndex your DataTemplate

2. 将您的 DataTemplate 绑定到 AlternatingIndex

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

So this works without a converter, an extra CollectionViewSourceand most importantly without brute-force-searching the source collection.

所以这在没有转换器的情况下工作,一个额外的CollectionViewSource,最重要的是没有蛮力搜索源集合。

回答by Parrhesia Joe

Yet another answer. I tried the above, which works in WPF (AlternationCountsolution), but I needed code for Silverlight, so I did the following. This is more elegant than the other brute force method.

还有一个答案。我尝试了上面的方法,它在 WPF(AlternationCount解决方案)中有效,但是我需要 Silverlight 的代码,所以我做了以下操作。这比其他蛮力方法更优雅。

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

And behind

而后面

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

This is newly available in Silverlight 5 with the introduction of RelativeSourcebinding.

这是 Silverlight 5 中新引入的RelativeSource绑定功能。

回答by Andreas

Why not just binding to count property of the listbox.

为什么不只绑定到列表框的 count 属性。

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