Behind the scenes property of Winforms listbox item - c#

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

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;

C# association with xaml

I am trying to make an enroll/withdraw student into course project however I am not entirely sure how to add specific students to specific course.
I have a Course and Student class, and then a xaml window with a combobox, and list box with appropriate buttons.
When I press the enrol right now it simply takes the selected student and adds it into the "EnrolledStudents" text box to display the name however it doesn't actually assign it to the selected Course.
Code I have so far:
public MainWindow()
{
InitializeComponent();
Course bsc = new Course("BSc(Hons) Applied Computing");
Course hnd = new Course("Higher National Diploma (HND) Applied Computing");
Course partTime = new Course("Computer Science Part Time (MSc)");
Student andy = new Student("Andy", "Watt");
Student dave = new Student("Dave","Newbold");
Student daniel = new Student("Daniel","Brown");
lbCourses.Items.Add(bsc);
lbCourses.Items.Add(hnd);
lbCourses.Items.Add(partTime);
cbStudents.Items.Add(andy);
cbStudents.Items.Add(dave);
cbStudents.Items.Add(daniel);
}
and the enroll button click code:
private void butEnroleStudent_Click(object sender, RoutedEventArgs e)
{
cbStudents.SelectedItem.ToString();
lbEnroledStudents.Items.Add(cbStudents.SelectedItem);
}
but I am not sure where to go from here. My main issue is I don't know how to select the Student and Course instead of the string values.
As BradleyDotNET suggests, MVVM would be a lot easier for you to use, especially if your UI is going to get a lot more complicated. Also, any application like this is most likely going to rely on a database behind the scenes and hence you would be looking to bind all of your data controls.
That said, here is a sample that would achieve what you are trying to do there.
Presuming your Student class looks something like this:
private class Student
{
public String FirstName { get; set; }
public String Surname { get; set; }
public Student(String firstName, String surname)
{
FirstName = firstName;
Surname = surname;
}
public override string ToString()
{
return FirstName + " " + Surname;
}
}
And your Course class something like this:
private class Course
{
public String Name { get; set; }
public List<Student> EnrolledStudents { get; set; }
public Course(String name)
{
Name = name;
EnrolledStudents = new List<Student>();
}
public override string ToString()
{
return Name;
}
}
(Note that I have added a List to store the students enrolled on the given course)
Rather than adding to the Items property of your ListBox and ComboBox create collections that we can bind to:
private List<Student> _students;
private List<Course> _courses;
Constructing your test data then looks like this:
_courses = new List<Course>();
_courses.Add(new Course("BSc(Hons) Applied Computing"));
_courses.Add(new Course("Higher National Diploma (HND) Applied Computing"));
_courses.Add(new Course("Computer Science Part Time (MSc)"));
_students = new List<Student>();
_students.Add(new Student("Andy", "Watt"));
_students.Add(new Student("Dave", "Newbold"));
_students.Add(new Student("Daniel", "Brown"));
lbCourses.ItemsSource = _courses;
cbStudents.ItemsSource = _students;
Then when you click your enroll button
private void butEnroleStudent_Click(object sender, RoutedEventArgs e)
{
if (lbCourses.SelectedIndex >= 0 && cbStudents.SelectedIndex >= 0)
{
// Both a student and course are selected
Course selectedCourse = (Course)lbCourses.SelectedItem;
Student studentToAdd = (Student)cbStudents.SelectedItem;
if (!selectedCourse.EnrolledStudents.Contains(studentToAdd))
{
// Course does not already contain student, add them
selectedCourse.EnrolledStudents.Add(studentToAdd);
lbEnroledStudents.Items.Refresh();
}
}
}
Finally, to show the enrolled students in lbEnroledStudents as you click through the courses, you will need to hook up a new event handler in the xaml:
<ListBox x:Name="lbCourses" SelectionChanged="lbCourses_SelectionChanged"></ListBox>
And in the code behind:
private void lbCourses_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Course selectedCourse = (Course)lbCourses.SelectedItem;
lbEnroledStudents.ItemsSource = selectedCourse.EnrolledStudents;
}

Implementing a find system for a ListView in C#

I am creating an application in C# using a ListView control that lets you create lists. I am implementing a Find function using the Find() method. Here’s my code:
if (findTextBox.Text != "")
{
ListViewItem[] lviFoundList = listItemsList.Items.Find(findTextBox.Text, true);
amountFound.Text = "Found " + Convert.ToString(lviFoundList.Count());
if (lviFoundList.Count() != 0)
{
int firstItemIndex = lviFoundList[0].Index;
listItemsList.Items[firstItemIndex].Selected = true;
}
}
else
{
amountFound.Text = "Found 0";
}
However, it doesn’t return any matches. What am I doing wrong?
Find method requires your listView item's Name, did you set your list view item's name property? If you want to search for text you can use this:
var lviFoundList = new List<ListViewItem>();
foreach(var item in listItemsList.Items)
{
if(item.Text == findTextBox.Text) lviFoundList.Add(item);
}
The Find() Method looks at the ListViewItem's name, not it's text.
You want this instead:
if (findTextBox.Text != "")
{
List<ListViewItem> items = new List<ListViewItems>();
foreach ListViewItem lvi in listItemsList.Items
{
if (lvi.Text == findTextBox.Text)
items.Add(lvi);
}
amountFound.Text = "Found " + Convert.ToString(lviFoundList.Count());
if(lviFoundList.Count() != 0)
{
int firstItemIndex = lviFoundList[0].Index;
listItemsList.Items[firstItemIndex].Selected = true;
}
}
else
{
amountFound.Text = "Found 0";
}
Honestly, the ListView.Find() method is rather poor and it's much easier to roll your own with LINQ. Think about what Find really is trying to accomplish -- a specific filtering, typically one record.
The first step, if you haven't already, would be to keep a cached collection of your data objects. Let's assume you have a list of Person classes like so:
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string JobTitle { get; set; }
public DateTime DateOfBirth { get; set; }
}
Then in your MainForm you have a ListView and a member variable people defined as a List<Person>. Your ListView.Items should reflect the contents of this List<Person>.
So now maybe you want to find a person based on their FirstName or LastName, right? You could use LINQ in a function like so:
int FindFirstIndexOfPersonNamed(string firstOrLastName)
{
// WARNING: This is case sensitive!
return people.FindIndex(p => p.FirstName.Contains(firstOrLastName) || p.LastName.Contains(firstOrLastName));
}
Since your ListView.Items should be reflecting your List<Person> the index should be identical:
// Get the found item and do whatever you want with it...
var selectedListViewItem = listView.Items[index];

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

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