Self Sorting Listbox
Asked Answered
O

8

9

Totally stumped by something that seems easy, and has been done to death... Yet still stumped.

What I want to do: I've got a WinForms ListBox. Its items are populated with objects, the DisplayMember is set. As the app runs, the data in the listed items might change, including the field behind the DisplayMember. I want the text displayed in the ListBox to change when this happens, and I also want the ListBox to re-sort itself so the items remain in alphabetical order.

A BindingList works fine to update the displayed text when the data changes, but for the life of me, I can't get it to sort.

I reviewed this: http://msdn.microsoft.com/en-us/library/ms993236.aspx

Plus numerous threads here about how to do this, but none of it seems to work for a ListBox.

Setting the Sorted property on the ListBox is similarly unhelpful.

What do I need to do to get a ListBox to sort itself?

Ostensive answered 20/9, 2010 at 1:42 Comment(5)
Are you binding directly to the BindingList or using a DataSource then BindingList? I have never had problems with the latter.Mistook
I have a list of objects (actually Entity Framework objects), I'm passing those in to the constructor of a BindingList, and then assigning that BindingList to the DataSource of the ListBox. This will update the DisplayMember, but won't automatically sort. Since BindingList doesn't sort natively, that's not a suprise. But I can't seem to get it to sort, even when I make my own derived version of it, as in the MSDN example, nor via other approaches.Ostensive
What version of .Net are we talking about here?Samiel
Have you considered implementing IBindingList yourself?Horologist
I wonder if you need to use INotifyPropertyChanged on items in a BindingList so the ListBox knows to resort after the DisplayMember property changes? Although I wouldn't be surprised if ListBox doesn't handle that correctly...Nummary
M
1

You can use a BindingSource object. Just drag-n-drop it into your form and point your ListBox.DataSource property to this BindingSource object. Then go to the BindingSource's properties and define Sort as you need.

Then in code you can set myBindingSource.DataSource = myCollection and voila, your listbox is populated and sorted. Easy.

Matterhorn answered 20/9, 2010 at 10:59 Comment(1)
The BindingSource doesn't seem to re-sort after a change. It ends up behaving pretty much like the BindingList did. It shows the rename of the DisplayMember, but doesn't sort the items. I tried putting a SortableBindingList as its DataSource, it showed as SupportsSorting, but when things change, still no sort.Ostensive
W
1

As with Patrol02's post, however you may want to try setting the DataSource to null and then reassigning it based on an event triggered by the list size changing. You could use the observer pattern on the collection, overriding the Add and Remove methods to notify watchers to rebind themselves.

Wiebmer answered 21/9, 2010 at 11:26 Comment(1)
One option that does work is to use a BindingList, which has an event for ListChanged, wire to that event, detect a change, and use that as a cue to repopulate the ListBox. I found that binding to the DataSource didn't work well here, and instead one has to Clear and repopulate the Items in the ListBox. This is a bit inelegant, I'm hoping there's a better solution... But it does work.Ostensive
L
1

Resetting the DataSource will effectively sort the ListBox:

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

But that's not automatic. As I understand, sorting should happen whenever the field behind the DisplayMember is updated, through an event or something like that...

See my complete test anyway:

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;
    }
}
Legitimize answered 24/9, 2010 at 19:42 Comment(0)
S
1

The LVS_SORT style on the list control should work, but you say it doesn't. I would double check that it is applied. I've never had any trouble with a self-sorting drop-down list control. Note this is a list control we're speaking of, not a listview control.

Subservience answered 26/9, 2010 at 16:45 Comment(0)
M
1

I did this by creating a new class, BindingSortingList, which inherited from BindingList. In it I overrode all the necessary methods, like ApplySortCore() and RemoveSortCore(). When you apply the sort, internally you basically have to copy it to a standard list, which has sorting ability, sort it, then copy it back into the "this" list. It seems crazy but now I have a reusable class for this purpose.

Mayan answered 4/5, 2011 at 19:50 Comment(0)
E
0
 private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
      //Sorting function
    }

What about this??

Evalynevan answered 20/9, 2010 at 3:50 Comment(1)
The data behind the ListBox will actually change independant of when the selected item changes. The user will select the item, that will populate a form with the item's information, allowing them to change it. In doing so, they might change the property behind the DisplayMember on the Listbox. That's when I'd want it to re-sort, which would actually be when they hit Save on the other form. I tried wiring up to the ListChanged event of the BindingList behind the ListBox, sorted there, saw the BindingList sort, but the ListBox didn't redraw itself, it'd only update the changed item.Ostensive
M
-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>
Malcom answered 4/10, 2011 at 6:12 Comment(0)
M
-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;
            }
        }
    }
}
Malcom answered 4/10, 2011 at 6:27 Comment(1)
-1 because this code has nothing to do with the question. If I'm wrong, please edit your answer and make it clearer.Abeyant

© 2022 - 2024 — McMap. All rights reserved.