listbox items not displaying properly - c#

I have a listbox and a ListItem object which I am adding to the listbox so that I can retrieve a value from the selected item which is different then the displayed member.
class ListItem
{
public string DisplayMember;
public string ValueMember;
public ListItem(string n,string v){
DisplayMember = n;
ValueMember = v;
}
}
public CompareTimeFramesForm()
{
InitializeComponent();
listBox1.Items.Add(new ListItem("# of Bookings", null));
listBox1.Items.Add(new ListItem("People", "guaranteed_count"));
}
This is a winform FYI.
The problem I am having is the item shown in the actual listbox has the object rather then the string I would like to be displayed in the first argument of the ListItem constructor.
It looks like Bookings.Helpers.ListItem rather then "# of Bookings"
In the designer I changed the displayMember property to DisplayMember and its not working.

From MSDN:
When an object is being added to the ListBox, the control uses the text defined in the ToString method of the object unless a member name within the object is specified in the DisplayMember property.
ListBox will convert item to string calling ToString(). In your case you just need to change your ListItem class like this:
class ListItem
{
public string DisplayMember;
public string ValueMember;
public ListItem(string n,string v) {
DisplayMember = n;
ValueMember = v;
}
public override string ToString() {
return DisplayMember;
}
}
As alternative you can se the DisplayMember property (in designer or with code) to use your property (you called that property DisplayMember but its name is free because it must be specified and it doesn't use any convention):
listBox1.DisplayMember = "DisplayMember";

try this it should work for you!
class ListItem
{
public string DisplayMember;
public string ValueMember;
public ListItem(string n,string v){
DisplayMember = n;
ValueMember = v;
}
}
public CompareTimeFramesForm()
{
InitializeComponent();
listBox1.Items.Add(new ListItem("# of Bookings", null).DisplayMember);
listBox1.Items.Add(new ListItem("People", "guaranteed_count").DisplayMember);
}

Related

Why does ValueMember override an empty DisplayMember

When I set a DataSource on a control and want to use .ToString() as DisplayMember, I need to set the DisplayMember last or the ValueMember will override it.
MSDN on empty string as display member:
The controls that inherit from ListControl can display diverse types of objects. If the specified property does not exist on the object or the value of DisplayMember is an empty string (""), the results of the object's ToString method are displayed instead.
Code to reproduce:
Class:
class SomeClass
{
public string PartA { get; set; }
public string PartB { get; set; }
public string WrongPart { get { return "WRONG"; } }
public override string ToString()
{
return $"{PartA} - {PartB}";
}
}
Form:
var testObj = new SomeClass() { PartA = "A", PartB = "B" };
comboBox1.DataSource = new [] { testObj };
comboBox1.DisplayMember = "";
comboBox1.ValueMember = "WrongPart";
comboBox2.DataSource = new[] { testObj };
comboBox2.ValueMember = "WrongPart";
comboBox2.DisplayMember = "";
You can try it by making a new form and adding 2 combobox's.
Result:
Conclusion and question:
This can be easily fixed by setting them in the correct order however this is prone to errors, it also does not show this behavior if I use an actual property as DisplayMember instead of ""/ToString.
I would really like to know why it displays this behavior and if I could possibly set .ToString() explicitly as DisplayMember (for code clarity).
I have searched in the reference source and found this bit:
if (!newValueMember.Equals(valueMember)) {
// If the displayMember is set to the EmptyString, then recreate the dataConnection
//
if (DisplayMember.Length == 0)
SetDataConnection(DataSource, newValueMember, false);
SetDataConnection method signature:
private void SetDataConnection(object newDataSource, BindingMemberInfo newDisplayMember, bool force)
This sets a new DisplayMember
displayMember = newDisplayMember;
so now we've come to the root of the issue

Custom data binding on ComboBox

I am trying to fill a ComboBox with multiple instances of a custom ComboBoxItem class. The ComboBoxItem class looks like this:
class ComboBoxItem
{
public string Text { get; set; }
public object Value { get; set; }
public override string ToString()
{
return Text;
}
}
I can fill the CombBox and read it's values just fine. My only problem is when an existing item comes in, the values should be bound to my ComboBox. But I don't know how to tell the Binding that it should use ComboBoxItem.Value as Value field.
//what to put in place of "SelectedItem"??
comboBox.DataBindings.Add(new Binding("SelectedItem", row, "F_KundenId", true, DataSourceUpdateMode.OnPropertyChanged));
This is how I bind all my Windows ComboBoxes in my app:
First use this to load your dataSource, in this case a List:
public static void LoadComboBox(ComboBox comboBox, object dataSource, string valueMember, string displayMember)
{
comboBox.DataSource = dataSource;
comboBox.ValueMember = valueMember;
comboBox.DisplayMember = displayMember;
}
Then use this to bind the selected value to your "row" column "F_KundenId":
public static void BindComboBox(ComboBox comboBox, object boundDataSource, string boundDataMember)
{
comboBox.DataBindings.Clear();
comboBox.DataBindings.Add("SelectedValue", boundDataSource, boundDataMember);
}
And here is a helper method to do both in a single call:
public static void LoadAndBindComboBox(ComboBox comboBox, object dataSource, string valueMember, string displayMember,
object boundDataSource, string boundDataMember)
{
LoadComboBox(comboBox, dataSource, valueMember, displayMember);
BindComboBox(comboBox, boundDataSource, boundDataMember);
}
This code can be used with ANY datasources you want, and can bind to any column of a DataTable, DataRow or object.
Example:
LoadAndBindComboBox(comboBox, myItems, "Value", "Text", row, "F_KundenId");
Ok, alternatively to Mangist's answer, I have come up with a solution of my own. No need for any custom class. Just use Dictionary<Key, Value>. The only thing is that you have to be careful WHEN during runtime you set DisplayMember and ValueMember. If you reset the DataSource of the ComboBox (set it to null and rebind it), then you need to also set the two Member properties. Then just bind the Dictionary<Key, Value> to the ComboBox using a BindingSource object.
private void InitCombos()
{
Dictionary<string, int> items = GetItems();
combo.DisplayMember = "Key";
combo.ValueMember = "Value";
combo.DataSource = new BindingSource(items, null);
}
//This was where my problem was. I didn't set the two Member properties of my ComboBox,
//thus preventing correct rebinding of DataSource
public void combo2_SelectedIndexChanged(object sender, EventArgs e)
{
combo.DataSource = null;
Dictionary<string, int> newItems = GetItems();
combo.DisplayMember = "Key";
combo.ValueMember = "Value";
combo.DataSource = new BindingSource(items, null);
}

CheckedListBox checked list item property binding to field in a class

I have a class with two properties and I want to bind it to a CheckedListBox item. WebsiteName property will be the DisplayValue and IsChecked should be the Checked state of the item :
public class ACLASS:
{
public string WebsiteName
{
get;
set;
}
public bool IsChecked
{
get;
set;
}
public ACLASS()
{
}
}
This is how I 'try' to bind it:
ACLASS aclass = new ACLASS();
aclass.WebsiteName = "www.example.com";
BindingList<ACLASS> list = new BindingList<ACLASS>();
list.Add(aclass);
checkedlistbox.DataSource = list;
checkedlistbox.DisplayMember = "WebsiteName";
This is all fine and works but how do I also bind the Checked state of the items in the CheckedListBox with the IsChecked property of the class so when I check a item in the CheckedListBox it will also change the IsChecked property of the ACLASS instance? I need this so I can verify the checked state of the item through the ACLASS instance.
The CheckedListBox control doesn't really support a DataSource, which is why the designers hid it from Intellisense. You can use it, but the checkmark property of the listbox won't work with your class, even if you set the ValueMember property to your boolean property.
Unfortunately, it means you have to set the values yourself:
checkedlistbox.DataSource = list;
checkedlistbox.DisplayMember = "WebsiteName";
for (int i = 0; i < checkedListbox.Items.Count; ++i) {
checkedListbox.SetItemChecked(i, ((ACLASS)checkedListbox.Items[i]).IsChecked);
}
and you also have to track the changes yourself, too:
checkedListBox1.ItemCheck += (sender, e) => {
list[e.Index].IsChecked = (e.NewValue != CheckState.Unchecked);
};
checkedlistbox.ValueMember = "IsChecked";

Problem in binding a listbox to data

I have a table that have these fields: ID , Name
I have bound a listbox to the table.
My question is, when the user has selected an item in listbox, how would I find out what the ID of the selected item is?
Note: The id is not equal to the selectedindex or id of items in items list
e.g.
Suppose you have a DataTable dt, with columns ID and Name in it.
then while binding include the following code,
this.listbox.DataSource = dt;
this.listbox.DisplayMember = "Name";
this.listbox.ValueMember = "ID";
while reading the selected values of listbox,
this.listbox.SelectedItem will give u the selected Name and
this.listbox.SelectedValue will give u the corresponding ID
test it
lst.SelectedItem.Value;
OR
lst.SelectedValue;
where lst is a ListBox Cotrol
What type of application is this? ASP.net, Windows Forms, WPF?
I have a feeling you are working with Windows Forms, as the other two are much clearer...
Here is some code for a Windows Forms App... Basically, you create your own class, and use that for the list items. The list box will display the results of the ToString() method, so override that to get the value you wanna display. When you access ListBox.SelectedItem, it will be an instance of the class you defined, and you can access whatever properties are necessary:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
MyListItem item1 = new MyListItem("Java", 1);
MyListItem item2 = new MyListItem("C#", 221);
MyListItem item3 = new MyListItem("C++", 13);
listBox1.Items.Add(item1);
listBox1.Items.Add(item2);
listBox1.Items.Add(item3);
}
private class MyListItem
{
public string ItemName { get; set; }
public int ItemId { get; set; }
public MyListItem(string name, int id)
{
this.ItemName = name;
this.ItemId = id;
}
public override string ToString()
{
return this.ItemName;
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
MyListItem selectedItem = (MyListItem)listBox1.SelectedItem;
MessageBox.Show(string.Format("Name is: {0}, Id is: {1}", selectedItem.ItemName, selectedItem.ItemId));
}
}

C# combobox overridden ToString

I am experiencing some problems while working with ComboBox.
The display member for my combobox is not being populated by the overridden ToString method of class MAP.
Here is my code:
Form1.cs:
private void Form1_Load(object sender, EventArgs e) {
...
...
MAPList MAP = new MAPList();
comboBox1.DataSource = MAP.All;
comboBox1.ValueMember = "Code";
...
...
}
MAPList.cs:
public class MAPList {
public readonly List<MAP> All;
public MAPList() {
All = new List<MAP>();
var MapData = // Getting map data
foreach(MAP m in MapData) {
All.Add(new Map(m.Name, m.Code));
}
}
}
MAP.cs:
public class MAP {
public readonly string Name;
private string code;
public string Code { get { return code; } }
public RadioCode(string Name, string Code) {
this.Name = Name;
this.code = Code;
}
public override string ToString() {
return String.Format("{0}: {1}", Name, Code);
}
}
ToString will not be called if you set ValueMember. If you do not set ValueMember it will work as expected but then of course Code will not be used as the selected value of the ComboBox.
Alternatively, if you wish to use ValueMember you may also want to set DisplayMember. You can create a property on your MAP that is used for display, i.e.:
public class MAP
{
public readonly string Name;
private string code;
public string Code { get { return code; } }
public string Display { get { return ToString(); } }
public MAP(string Name, string Code)
{
this.Name = Name;
this.code = Code;
}
public override string ToString()
{
return String.Format("{0}: {1}", Name, Code);
}
}
In the form you can then set DisplayMember:
MAPList MAP = new MAPList();
comboBox1.DataSource = MAP.All;
comboBox1.ValueMember = "Code";
comboBox1.DisplayMember = "Display";
This is because you've set your ValueMember property to "Code", so the values in the combobox are not your Map objects but rather the strings corresponding to their Code properties.
If you remove this line:
comboBox1.ValueMember = "Code";
...it will work as you expect.
If you want the ComboBox to display its items according to your Map type's ToString method, then Jakob's answer is right on: create a property on your Map type that provides a string formatted exactly how you want it, and set the DisplayMember property of the ComboBox to the name of this property.
this could be because u r using ValueMember. use DisplayMember Property, add another property on the Map class in the get of this property return the formatted string.
I know this is an old post, but if someone wants to use ToString() without creating a property to just call ToString(), you'll have to explicitly set the DisplayMember value to an empty string like this:
Form1.cs:
private void Form1_Load(object sender, EventArgs e) {
...
...
MAPList MAP = new MAPList();
comboBox1.DataSource = MAP.All;
comboBox1.ValueMember = "Code";
comboBox1.DisplayMember = ""; // Explicitly set it to an empty String
...
...
}

Categories