views:

1771

answers:

9

I'm writing a Windows application and using a Listbox control. I'm developing with Visual Studio C# 2008 Express Edition.

I've got a data object that looks something like this

public class RootObject
{
   public List<SubObject> MySubObjects{ get; set;}
}

I've got a ListBox on my form, and also a property "MyRootObject" which, obviously, holds a RootObject. When the control is initialized, I set:

_listBox.DataSource = MyRootObject.MySubObjects;

Now, when the form loads, I debug and see that the DataSource is being set correctly. But nothing is displayed. I've overridden SubObject's ToString() method and it's not even being called. I tried setting _listBox.DisplayMember to a property of SubObject just to see if there was some problem there, but still nothing. I tried calling _listBox.Update() and _listBox.Refresh() after setting the DataSource, but still no love. The DataSource has all the data... it's just refusing to display it.

So while debugging, I wondered WTF and I decided to just do

_listBox.DataSource = new List<SubObject>{ new SubObject(), new SubObject() };

Sure enough, this worked, and I see two things listed in my listbox.

So then, really curious, I decided to try copying the list of objects and putting that in the listbox, like so:

_listBox.DataSource = MyRootObject.MySubObjects.ToArray();

This works! And it's a workaround to my problem for now... but a very annoying one. Does anyone know why I need to basically copy the list of objects like this to get it to work, rather than just setting the _listBox.DataSource = MyRootObject.MySubObjects; ? Again, the DataSource has all the right data either way after setting it... it's just when it's copied data, it actually displays, and when it's not, it's not displayed.

A: 

Off the top of my head, this is because the ListBox.DataSource property must contain something that implements the IList interface. Your generic List<SubObject> does not implement IList; it implements IList<T> (in the System.Collections.Generic namespace). Array objects, on the other hand do inherit from IList, so handing the data in via that kind of object works.

You could try pulling an Enumerator (which also implements IList) out of your List<SubObject> object and plug that in. If it works, then the issue I've described is your problem. If it doesn't, then I'm talking out of my hat.

If this is indeed the issue, I am surprised that shoving in an unsupported object doesn't throw an exception.

Dustman
A: 

I think you have to call Bind method after assigning to the list box data source something like _listBox.DataSource.bind() and you will have your listbox disappeared

Spikie
A: 

you could try and use a BindingSource ( http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx )

inbetween the Listbox and your collection. Bindingsources handles a bunch of stuff and also includes Suspend/ResumeBinding properies that can be useful when updating the list

you could also try out wpf as its databinding is far superior to that of winforms :) but maybe thats not possible in your case

A: 

I believe you need to call _listbox.DataBind(); after assigning the datasource. However, I've never used a property as a datasource before, I've only used methods. Have you tried changing your property to a method to see if that's the problem?

I could be wrong but I believe that's only applicable in ASP.NET. I think the OP is probably using Winforms.
Scott Whitlock
I'm sorry, I didn't notice that. You're probably right. Thanks for the clarification. :-)
A: 

Hi, so far that i know, whenever you want to set a collection to a

[ComboBox,ListBox].DataSource

you have to set the DisplayMember and ValueMember. DisplayMember and ValueMember are filled with the property name of the Class in the collection that is assigned to the listbox/combobox. Ex.

//Populate the data
RootObject root = new RootObject();
root.MySubObjects.Add(new SubObject("1", "data 1"));
root.MySubObjects.Add(new SubObject("2", "data 2"));

//Assign data to the data source
_listBox.DisplayMember = "DisplayProperty";
_listBox.ValueMember = "ValueProperty";
_listBox.DataSource = root.MySubObjects;

root.MySubObjects returns List of SubObject, and SubObject has to have properties called DisplayProperty and ValueProperty, ex.

public class RootObject
{
     public List<SubObject> MySubObjects { get; set; }
}

public class SubObject
{
     public string ValueProperty { get; set; }
     public string DisplayProperty { get; set; }
}
Setting DisplayMember and ValueMember is usual, but optional; if you don't configure DisplayMember, the control calls ToString() on the object; and if you don't set ValueMember, the control returns the whole object for the value.
Bevan
A: 

You could try

_listBox.DataSource = new BindingList<SubObject> (MyRootObject.MySubObjects);

instead. BindingList implements some more interfaces than List, which are essential for DataBinding.

Simpzon
+1  A: 

I have a special rule for problems like this that I always try to remember before wasting an entire day hammering on it (believe me, I've spent many days hammering!). The rule is: when system behavior is really strange, the underlying cause must be very stupid. As intelligent programmers, we have a tendancy to search for esoteric causes or bugs in framework code to explain our problems. Sometimes the answer is that we were in a hurry and made a careless mistake that we haven't yet caught.

To quote Sherlock Holmes, "when you've removed the impossible, then whatever remains, no matter how improbable, must be the truth". In this case, the improbable truth is that the MySubObjects property of MyRoot object is null.

Paul Keister
+1  A: 
((CurrencyManager)_listBox.BindingContext[_listBox.DataSource]).Refresh();

Sux0r I know, but this works. (originally found answer here)

Nat