自排序列表框

9
完全被看似简单的东西难住了,而这已经被做到了极致……但仍然难住了。
我想做的是:我有一个WinForms ListBox。它的项由对象填充,设置了DisplayMember。随着应用程序运行,列出的项目中的数据可能会更改,包括DisplayMember后面的字段。当发生这种情况时,我希望在ListBox中显示的文本发生更改,并且我还希望ListBox重新排序,以便项目保持按字母顺序排列。
BindingList可以很好地更新数据更改时显示的文本,但是我无论如何都无法让它进行排序。
我查阅了这个:http://msdn.microsoft.com/en-us/library/ms993236.aspx 加上这里关于如何做到这一点的众多线程,但对于ListBox,这些都似乎无济于事。
将Sorted属性设置为ListBox同样没有帮助。
我需要做什么才能使ListBox自动排序?

你是直接绑定到 BindingList 还是使用 DataSource 然后再绑定到 BindingList?我从未遇到过后者的问题。 - leppie
我有一个对象列表(实际上是Entity Framework对象),我将它们传递给BindingList的构造函数,然后将该BindingList分配给ListBox的DataSource。这将更新DisplayMember,但不会自动排序。由于BindingList没有本地排序功能,这并不令人惊讶。但即使我像MSDN示例中那样创建自己的派生版本,或者采用其他方法,似乎也无法对其进行排序。 - Jack
我们在谈论哪个版本的 .Net? - Steve Ellinger
你考虑过自己实现IBindingList吗? - user180326
我想知道在BindingList中的项是否需要使用INotifyPropertyChanged,以便ListBox在DisplayMember属性更改后知道重新排序?尽管如果ListBox不能正确处理它,我也不会感到惊讶... - Jeff B
8个回答

1
重置数据源将有效地对ListBox进行排序:
    listBox1.DataSource = null;
    listBox1.DataSource = myBindingList;
    listBox1.DisplayMember = "MyField";

但这不是自动的。据我所知,排序应该在 DisplayMember 后面的字段通过事件或类似方式更新时发生...

无论如何,请查看我的完整测试:

public partial class Form1 : Form
{
    public BindingList<ABC> myBindingList = new BindingList<ABC>();

    public Form1() {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e) {
        myBindingList.Add(new ABC("zzz"));
        myBindingList.Add(new ABC("aaa"));
    }

    private void button2_Click(object sender, EventArgs e) {
        myBindingList[0].MyField = "ccc"; // was "zzz"
        myBindingList[1].MyField = "ddd"; // was "aaa"

        listBox1.DataSource = null;
        listBox1.DataSource = myBindingList;
        listBox1.DisplayMember = "MyField";
    }

    private void Form1_Load(object sender, EventArgs e) {
        listBox1.DataSource = myBindingList;
        listBox1.DisplayMember = "MyField";

    }
}

public class ABC  {
    public string MyField { get; set; } 
    public ABC(string val) {
        MyField = val;
    }
}

1
我通过创建一个新类BindingSortingList来实现这一点,该类继承自BindingList。在其中,我重写了所有必要的方法,如ApplySortCore()和RemoveSortCore()。当您应用排序时,内部基本上必须将其复制到具有排序能力的标准列表中,对其进行排序,然后将其重新复制回“this”列表中。看起来很疯狂,但现在我有了一个可重用的类来实现此目的。

1
你可以使用BindingSource对象。只需将它拖放到您的窗体中,并将您的ListBox.DataSource属性指向此绑定源对象。然后转到BindingSource的属性并按需定义Sort。
然后,在代码中,您可以设置myBindingSource.DataSource = myCollection,然后您的列表框就会被填充和排序。非常简单。

BindingSource似乎无法在更改后重新排序。它的行为基本上与BindingList相同。它显示了DisplayMember的重命名,但不对项进行排序。我尝试将SortableBindingList作为其数据源,它显示为SupportsSorting,但当事情发生变化时,仍然没有排序。 - Jack

1
与Patrol02的帖子一样,但您可能希望尝试将DataSource设置为null,然后根据列表大小更改触发的事件重新分配它。您可以在集合上使用观察者模式,覆盖Add和Remove方法以通知观察者重新绑定自己。

有一个可行的选择是使用BindingList,它具有ListChanged事件,将其绑定到该事件上,检测到变化后,可以用此作为提示重新填充ListBox。我发现在这里绑定到DataSource并不起作用,而是需要清除并重新填充ListBox中的项。这有点不够优雅,我希望能找到更好的解决方案...但它确实有效。 - Jack

1
列表控件上的LVS_SORT样式应该可以工作,但您说它不起作用。我建议您再次确认是否已应用此样式。我从未遇到过自动排序下拉列表控件的任何问题。请注意,我们正在讨论列表控件,而不是列表视图控件。

0
 private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
      //Sorting function
    }

这个怎么样?


ListBox后面的数据实际上会在所选项目更改时独立更改。用户将选择该项目,这将使用该项目的信息填充表单,使他们能够更改它。在这样做时,他们可能会更改Listbox上的DisplayMember属性背后的属性。那时我希望它重新排序,这实际上是当他们在其他表单上点击保存时。我尝试连接到ListBox后面的BindingList的ListChanged事件,对其进行排序,看到BindingList排序,但ListBox没有重新绘制自己,它只会更新已更改的项目。 - Jack

-2
<ListBox x:Name="UsersList"  SelectionChanged="SelectionChngd">
            <ListBox.ItemTemplate>
                <DataTemplate >
                    <Border BorderBrush="Red" BorderThickness="5">
                    <Grid MouseEnter="Grid_MouseEnter"> 
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                            <TextBlock   Text="{Binding Name}"/>
                        <TextBlock Grid.Row="1" Text="{Binding Email}"/>
                    </Grid>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>

        </ListBox>

-4
namespace SilverlightApplication8
{
    public partial class MainPage : UserControl
    {
        ObservableCollection<UserData> users = new ObservableCollection<UserData>();
        public MainPage()
        {
            Service1Client client = new Service1Client();
            client.GetUsersCompleted += completed;
            client.GetUsersAsync(5);
            InitializeComponent();

            image.Source = new BitmapImage(new Uri(@"c:\1.JPG"));
        }    

        private void completed(object sender, GetUsersCompletedEventArgs e)
        {
            users=e.Result;

            UsersList.ItemsSource = users;
        }

        private void SelectionChngd(object sender, SelectionChangedEventArgs e)
        {
            UserData u= (UserData)(UsersList.SelectedItem);
            DescText.Text = u.Desc;

            image.Source = new BitmapImage(new Uri(@"http://profile.ak.fbcdn.net/hprofile-ak-snc4/49939_713180125_9000_q.jpg"));
        }

        private void Grid_MouseEnter(object sender, MouseEventArgs e)
        {
            if (UsersList.SelectedItem != null)
            {
                UserData u = (UserData)(UsersList.SelectedItem);
                DescText.Text = u.Desc;
            }
        }
    }
}

1
-1 是因为这段代码与问题无关。如果我错了,请编辑您的答案并使其更清晰。 - Marek Grzenkowicz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接