Problem in binding a listbox to data - c#

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

Related

ComboBox keeps old values while displaying new ones

So, it's been 4 hours since I started struggling here.
See, I've got this comboBox and it's bound to a List<>, - loads up like it's supposed to; but here I've also got a textBox which is supposed to contain text for filter criteria of the List<>. Goes nice, packs all the filtered items into a new list, the comboBox displays it... But when I choose to pick an item from it, i.e. a comboBox.Item, it returns the items from the first list. Yes, the first list, displaying the values from the filtered list; those values are class objects I'm packing into a dataGridView later on.
Here's the TextChanged:
private void textBox4_TextChanged_1(object sender, EventArgs e)
{
IEnumerable<artikal> filtered =
from artikal in art
where artikal.naziv.ToUpper().Contains(textBox4.Text.ToUpper()) || artikal.plu.Contains(textBox4.Text) || artikal.barkod.Contains(textBox4.Text)
select artikal;
comboBox1.DataSource = null;
comboBox1.Items.Clear();
List<artikal> filter = filtered.ToList<artikal>();
comboBox1.DataSource = filter;
and here's the class, I mean, if it's this important, but I'm not convinced it is:
public class artikal
{
public string plu { get; set; }
public string naziv { get; set; }
public string kolicina { get; set; }
public string nabavnaCena { get; set; }
public string prodajnaCnea { get; set; }
public string barkod { get; set; }
public override string ToString()
{
return plu + " " + naziv;
}
}
This art list is a global list defined above all else in the world. Here is how I populate the gridview:
public partial class NabavkaFrm : Form
{
#region some stuff lying here
List<item> art = new List<item>();
// other code
row.Cells[0].Value = art[comboBox1.SelectedIndex].plu;
row.Cells[1].Value = art[comboBox1.SelectedIndex].naziv;
}
So, yeah, any suggestions? And good time o' the day to everyone passing by :D
As I mentioned in the comment, the issue is not in the code where you filter the items. It cannot be. The issue is right here:
row.Cells[0].Value = art[comboBox1.SelectedIndex].plu;
row.Cells[1].Value = art[comboBox1.SelectedIndex].naziv;
and because art is the class level field, if the item from the combobox is 0, it will always show item at index 0 in art. You do not want this. You want to show item at index 0 from your filtered list but that list keeps changing. Sometimes item at index 0 is one thing, whilst another thing another time.
Do it like this instead:
var selectedItem = (comboBox1.SelectedValue as item);
row.Cells[0].Value = selectedItem.plu;

Bound DataGridView: the DataGridViewComboBoxColumn changes to a different value when I click outside it

I hope someone can help me out with an obscure problem that has me absolutely flummoxed.
I wish to set up a DataGridView, which allows a user to select an option from a DataGridViewComboBoxColumn, and for the object which is a datasource for the DataGridView, to be updated with the object that the user pick in the ComboBox.
[NB I wish the DataGridViewComboBoxColumn to show more than a single property in the dropdown, hence why I am using a DataTable as the DataGridViewComboBoxColumn datasource. In other words, I want them to see a description which is a combination of other properties concatenated together.]
My code works, but when I click outside the ComboBox cell, the value gets automatically set (it gets set back to the first item in BindingList). I cannot get it to stick to the value that the user selected. Ultimately the wrong object instance gets assigned to the DataGridView's dataSource.
I would post a pic, but I don't have enough rep.
So my question is, why does the DataGridViewComboBoxColumn switch to the first item in its datasource (a DataTable) when I click outside the cell. How can I get it to keep the option I selected, in the cell. I am absolutely BAFFLED.
I hope it's okay to post this much code up to the StackOverflow website without it annoying everyone. I've tried to create suitably generic example to help any other souls trying to find out how to select and assign an object to the property of another object, using a DataGridViewComboBoxColumn. So hopefully this is of use to someone else. I've also tried to make it relatively easy to recreate, should someone need to solve this kind of problem.
I've tried all manner of things, including using a List as a datasource of the DataGridViewComboBoxColumn, doing things with the CurrentCellDirtyStateChanged event - all to no avail.
Here's my 2 classes:
public class CarPartChoice
{
public string Name { get; set; }
public int Value { get; set; }
public string Comment {get; set;}
public CarPartChoice(string name, int value, string comment)
{
Name = name;
Value = value;
Comment = comment;
}
}
public class Car
{
public string Manufacturer { get; set; }
public string Model { get; set; }
public CarPartChoice WheelChoice {get; set;}
public Car(string maker, string model, CarPartChoice wheel)
{
Manufacturer = maker;
Model = model;
WheelChoice = wheel;
}
public Car(string maker, string model)
{
Manufacturer = maker;
Model = model;
}
}
public static class GlobalVariables
{
public static BindingList<CarPartChoice> GlobalChoiceList { get; set; }
public static BindingList<Car> GlobalCarsList { get; set; }
}
And here's me creating my dataGridView:
private void Form1_Load(object sender, EventArgs e)
{
//Setup the wheel choices to be selected from the DataGridViewComboBoxColumn.
CarPartChoice myWheelChoice = new CarPartChoice("Chrome", 19, "This is the chromes wheels option.");
CarPartChoice myWheelChoice2 = new CarPartChoice("HubCaps", 16, "This is the nasty plastic hubcaps option.");
BindingList<CarPartChoice> tempBLChoice = new BindingList<CarPartChoice>();
tempBLChoice.Add(myWheelChoice);
tempBLChoice.Add(myWheelChoice2);
GlobalVariables.GlobalChoiceList = tempBLChoice;
//Setup the cars to populate the datagridview.
Car car1 = new Car("Vauxhall", "Astra");
Car car2 = new Car("Mercedes", "S-class");
BindingList<Car> tempListCars = new BindingList<Car>();
tempListCars.Add(car1);
tempListCars.Add(car2);
GlobalVariables.GlobalCarsList = tempListCars;
dataGridView1.AutoGenerateColumns = false;
dataGridView1.CurrentCellDirtyStateChanged += new EventHandler(dataGridView1_CurrentCellDirtyStateChanged);
// Set up 2 DataGridViewTextBox columns, one to show the manufacturer and the other to show the model.
DataGridViewTextBoxColumn manufacturer_col = new DataGridViewTextBoxColumn();
manufacturer_col.DataPropertyName = "Manufacturer";
manufacturer_col.Name = "Manufacturer";
manufacturer_col.HeaderText = "Manufacturer";
DataGridViewTextBoxColumn model_col = new DataGridViewTextBoxColumn();
model_col.DataPropertyName = "Model";
model_col.Name = "Model";
model_col.HeaderText = "Model";
// Create a DataTable to hold the Wheel options available for the user to choose from. This DT will be the DataSource for the
// ...combobox column
DataTable wheelChoices = new DataTable();
DataColumn choice = new DataColumn("Choice", typeof(CarPartChoice));
DataColumn choiceDescription = new DataColumn("Description", typeof(String));
wheelChoices.Columns.Add(choice);
wheelChoices.Columns.Add(choiceDescription);
foreach (CarPartChoice wheelchoice in GlobalVariables.GlobalChoiceList)
{
wheelChoices.Rows.Add(wheelchoice, wheelchoice.Name + " - " + wheelchoice.Value.ToString() + " - " + wheelchoice.Comment);
}
// Create the Combobox column, populated with the wheel options so that user can pick one.
DataGridViewComboBoxColumn wheelOption_col = new DataGridViewComboBoxColumn();
wheelOption_col.DataPropertyName = "WheelChoice";
wheelOption_col.Name = "WheelChoice";
wheelOption_col.DataSource = wheelChoices;
wheelOption_col.ValueMember = "Choice";
wheelOption_col.DisplayMember = "Description";
wheelOption_col.ValueType = typeof(CarPartChoice);
// Add the columns and set the datasource for the DGV.
dataGridView1.Columns.Add(manufacturer_col);
dataGridView1.Columns.Add(model_col);
dataGridView1.Columns.Add(wheelOption_col);
dataGridView1.DataSource = GlobalVariables.GlobalCarsList;
}
UPDATE:
I have tried to use a "BindingSource" object and setting the datasource of the BindingSource to the DataTable. That made no difference. I've also tried getting the Car to implement the "INotifyPropertyChanged" interface and that didn't make any difference.
The one thing if I've noticed is that the Car item in the datagridview DOES get its WheelChoice property updated. The Car objects IS INDEED updated with the value I've selected from the ComboBox. I think this is a just a display issue and the DataGridViewComboBoxColumn just isn't populated with the correct value. Still baffled.
Thanks to those who left comments. I found the answer in the end.
The answer is for me simply to define an overridden "ToString()" method for my "CarPartChoice" class (i.e. the class which is used to provide the items to be looked-up by clicking on the ComboBoxColumn). I guess it was having trouble formatting my cell with a nice string when I moved control away from it.
So, for anyone who ever wants to do this in future, here's a full example of how you might use a datagridview to update a list of objects of a certain class, and using a DataGridViewComboBoxColumn to provide the user with a choice of objects, and for their selected object (which they chose from the dropdown), to be populated into the list (to be precise: in the field of an object in the list which is of the type of object which gets selected by the user in the dropdown). Yes, I know that's a very horrible sentence.
Here's the complete code which would accomplish this task (I would have thought it was something that people would often want to do).
Kindest regards to all.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//Setup the wheel choices to be selected from the DataGridViewComboBoxColumn.
CarPartChoice myWheelChoice = new CarPartChoice("Chrome", 19, "This is the chromes wheels option.");
CarPartChoice myWheelChoice2 = new CarPartChoice("HubCaps", 16, "This is the nasty plastic hubcaps option.");
CarPartChoice myWheelChoice3 = new CarPartChoice("Iron", 15, "These are metal wheels.");
CarPartChoice myWheelChoice4 = new CarPartChoice("Spoked", 15, "This is the fancy classic hubcaps option.");
CarPartChoice myWheelChoice5 = new CarPartChoice("solid", 13, "This wheels has no spokes or holes.");
CarPartChoice myWheelChoice6 = new CarPartChoice("SpaceHubCaps", 17, "Newly developed space hubcaps.");
BindingList<CarPartChoice> tempBLChoice = new BindingList<CarPartChoice>();
tempBLChoice.Add(myWheelChoice);
tempBLChoice.Add(myWheelChoice2);
tempBLChoice.Add(myWheelChoice3);
tempBLChoice.Add(myWheelChoice4);
tempBLChoice.Add(myWheelChoice5);
tempBLChoice.Add(myWheelChoice6);
GlobalVariables.GlobalChoiceList = tempBLChoice;
//Setup the cars to populate the datagridview.
Car car1 = new Car("Vauxhall", "Astra");
Car car2 = new Car("Mercedes", "S-class");
BindingList<Car> tempListCars = new BindingList<Car>();
tempListCars.Add(car1);
tempListCars.Add(car2);
GlobalVariables.GlobalCarsList = tempListCars;
dataGridView1.AutoGenerateColumns = false;
dataGridView1.CurrentCellDirtyStateChanged += new EventHandler(dataGridView1_CurrentCellDirtyStateChanged);
// Set up 2 DataGridViewTextBox columns, one to show the manufacturer and the other to show the model.
DataGridViewTextBoxColumn manufacturer_col = new DataGridViewTextBoxColumn();
manufacturer_col.DataPropertyName = "Manufacturer";
manufacturer_col.Name = "Manufacturer";
manufacturer_col.HeaderText = "Manufacturer";
DataGridViewTextBoxColumn model_col = new DataGridViewTextBoxColumn();
model_col.DataPropertyName = "Model";
model_col.Name = "Model";
model_col.HeaderText = "Model";
// Create a DataTable to hold the Wheel options available for the user to choose from. This DT will be the DataSource for the
// ...combobox column
DataTable wheelChoices = new DataTable();
DataColumn choice = new DataColumn("Choice", typeof(CarPartChoice));
DataColumn choiceDescription = new DataColumn("Description", typeof(String));
wheelChoices.Columns.Add(choice);
wheelChoices.Columns.Add(choiceDescription);
foreach (CarPartChoice wheelchoice in GlobalVariables.GlobalChoiceList)
{
wheelChoices.Rows.Add(wheelchoice, wheelchoice.Name + " - " + wheelchoice.Value.ToString() + " - " + wheelchoice.Comment);
}
// Create the Combobox column, populated with the wheel options so that user can pick one.
DataGridViewComboBoxColumn wheelOption_col = new DataGridViewComboBoxColumn();
wheelOption_col.DataPropertyName = "WheelChoice";
wheelOption_col.DataSource = wheelChoices;
wheelOption_col.ValueMember = "Choice";
wheelOption_col.DisplayMember = "Description";
wheelOption_col.ValueType = typeof(CarPartChoice);
// Add the columns and set the datasource for the DGV.
dataGridView1.Columns.Add(manufacturer_col);
dataGridView1.Columns.Add(model_col);
dataGridView1.Columns.Add(wheelOption_col);
dataGridView1.DataSource = GlobalVariables.GlobalCarsList;
}
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
var grid = sender as DataGridView;
if (grid.IsCurrentCellDirty)
grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
public class CarPartChoice
{
public string Name { get; set; }
public int Value { get; set; }
public string Comment { get; set; }
public CarPartChoice(string name, int value, string comment)
{
Name = name;
Value = value;
Comment = comment;
}
public override string ToString()
{
return Name.ToString() + " - " + Value.ToString() + " - " + Comment.ToString();
}
}
public class Car
{
public string Manufacturer { get; set; }
public string Model {get; set; }
public CarPartChoice WheelChoice { get; set; }
public Car(string maker, string model, CarPartChoice wheel)
{
Manufacturer = maker;
Model = model;
WheelChoice = wheel;
}
public Car(string maker, string model)
{
Manufacturer = maker;
Model = model;
}
}
public static class GlobalVariables
{
public static BindingList<CarPartChoice> GlobalChoiceList { get; set; }
public static BindingList<Car> GlobalCarsList { get; set; }
}
I too had the same problem but with a different dataset. I was trying to add a keyvalue pair to the combobox and everytime I click outside, it got reset to the first item. What I was missing was that I hadn't set displayMember property of the combobox.
(Hope this would help if any others are here for the same problem)

Behind the scenes property of Winforms listbox item

So I'm going back and cleaning up code I did years ago. In one part, (and I used this several times) I have a list box displaying employee names such as
Smith, John
Doe, Jane
When the user clicks the name, I do something like
String unBrokenName = ListBox1.SelectedItem.ToString();
String LastName = unBrokenName.Substring(...
You get the idea, I extract the first and last name based upon the ", "
Then I do this to get the employee from the sql database.
Employee SelectedEmployee = Employee.GetEmployeeByFirstLast(FirstName, LastName);
At the time, it was the best I knew. Now it feels wrong, because I KNOW I should be able to get the sql ID of the employee when they select it, like
int EmployeeId = SOMELISTBOXSELECTEDITEMPROPERTY;
Employee SelectedEmployee = Employee.GetEmployeeByID(EmployeeId);
Is there some property for a listbox item that will store this id while displaying the same name the users are used to seeing?
You can actually add anything you'd like to a listbox:
class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
}
And then:
listBox1.Items.Add( new Foo() { Id = 101, Name = "Foo Bar" } );
listBox1.Items.Add( new Foo() { Id = 102, Name = "Foo Bar Jr." } );
The SelectedItem property will now give you the selected Foo, while displaying the Name property in the list itself.
private void listBox1_SelectedIndexChanged( object sender, EventArgs e )
{
Foo item = ( listBox1.SelectedItem as Foo );
if( item != null )
{
// use item.Id here
}
}
Instead of overriding ToString, you can also use the DisplayMember property of the listbox to select which property the listbox will display.
You can do something like this:
listBox1.DataSource = employeesList;
listBox1.DisplayMember = "LastName";
listBox1.ValueMember = "EmployeeId";
When you run your application the listbox will have the list of employees that you are passing and it will show the LastName. But when you select an item, you can access the id by:
`listBox1.SelectedValue();`
And then in the listbox1_Click event something like:
if (listBox1.SelectedIndex != -1)
{
int employeeId = listBox1.SelectedValue();
//do something here;
}

Bind values from a list array to listbox

Could any body give a short example for binding a value from list array to listbox in c#.net
It depends on how your list array is.
Let's start from an easy sample:
List<string> listToBind = new List<string> { "AA", "BB", "CC" };
this.listBox1.DataSource = listToBind;
Here we have a list of strings, that will be shown as items in the listbox.
Otherwise, if your list items are more complex (e.g. custom classes) you can do in this way:
Having for example, MyClass defined as follows:
public class MyClass
{
public int Id { get; set; }
public string Text { get; set; }
public MyClass(int id, string text)
{
this.Id = id;
this.Text = text;
}
}
here's the binding part:
List<MyClass> listToBind = new List<MyClass> { new MyClass(1, "One"), new MyClass(2, "Two") };
this.listBox1.DisplayMember = "Text";
this.listBox1.ValueMember = "Id"; // optional depending on your needs
this.listBox1.DataSource = listToBind;
And you will get a list box showing only the text of your items.
Setting also ValueMember to a specific Property of your class will make listBox1.SelectedValue containing the selected Id value instead of the whole class instance.
N.B.
Letting DisplayMember unset, you will get the ToString() result of your list entries as display text of your ListBox items.

Make ListBox items have a different value than item text

I want a ListBox full of items. Although, each item should have a different value.
So when the user selects an item and presses a button, a method will be called which will use the value the select item has.
I don't want to reveal the item values to the user.
EDIT: This is not for ASP.NET, it's for a Windows Forms application. I just thought the HTML example would be easy to read.
I have the inspiration from HTML:
<form>
<input type="radio" name="sex" value="Value1" /> Male
<br />
<input type="radio" name="sex" value="Value2" /> Female
</form>
This also allows me to use different values than what the user sees.
You can choose what do display using the DisplayMember of the ListBox.
List<SomeData> data = new List<SomeData>();
data.Add(new SomeData() { Value = 1, Text= "Some Text"});
data.Add(new SomeData() { Value = 2, Text = "Some Other Text"});
listBox1.DisplayMember = "Text";
listBox1.DataSource = data;
When the user selects an item, you can read the value (or any other property) from the selected object:
int value = (listBox1.SelectedItem as SomeData).Value;
Update: note that DisplayMember works only with properties, not with fields, so you need to alter your class a bit:
public class SomeData
{
public string Value { get; set; };
public string Text { get; set; };
}
items have a property called 'Tag', which you can use to store any information you want (hidden from the user)
ListViewItem myItem = new ListViewItem();
myItem.Text = "Users see this";
myItem.Tag = "Users don't see this";
(or set the appropriate properties in the property explorer)
Very simple:
foreach(var item in *Your Source List*)
{
ListItem dataItem = new ListItem();
dataItem.Text = "value to show";
dataItem.Value = *another value you want*;
listBox.Items.Add(dataItem);
}
As stated by the 1st answer, the use of DisplayMember works whether you are using asp.net or winforms.
And to comment a bit more, it also works if you are using the rather old fashion Items.add way of adding items to a ListBox.
Just for fun, here is a simple demo of what you need (just create a new form and drop on it a ListBox and a Label):
public partial class Form1 : Form
{
class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return string.Format("{0} {1}", LastName, FirstName);
}
}
public Form1() { InitializeComponent(); }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
listBox1.DisplayMember = "LastName";
listBox1.DataSource = GetCustomers();
//listBox1.Items.AddRange(GetCustomers().ToArray());
}
private IEnumerable<Customer> GetCustomers()
{
return new List<Customer>()
{
new Customer() { FirstName = "Gustav", LastName = "MAHLER" },
new Customer() { FirstName = "Johann Sebastian", LastName = "BACH" }
};
}
private void lb_SelectedIndexChanged(object sender, EventArgs e)
{
label1.Text = listBox1.SelectedItem.ToString();
}
}
Enjoy
PS: #2nd post Tag is not available to ListBox: because it accepts an array of object, not a specific item container like ListView... but you don't need any in your case. Tag is useful when you want to carry additional data along with a specific TreeViewItem or ListViewItem for example.
By the way, Tag is defined at the Control level and so exists for Button, Label, and so on... but for my part I think it is rather a bad idea to store business data in it (untyped, UI coupled...) apart from the ListView and TreeView cases for which it is rather convenient.
Easy!
protected void Page_Load(object sender, EventArgs e)
{
llenaListBox(ListBox1, 0, 10);
}
private void llenaListBox(ListBox PoListBox, int PiMinimo, int PiMaximo)
{
int Li;
for (Li = PiMinimo; Li <= PiMaximo; Li++)
{
ListItem obj = new ListItem();
obj.Text = Li.ToString();
obj.Value = Li.ToString();
PoListBox.Items.Add(obj);
}
}

Categories