Display highlighted data from Listbox in Textboxes

CarlMartin10
01-20-2010, 12:41 PM
I have an address book program completely done, except for one thing...

When a name is highlighted in the listbox, I need all of their information displayed in the related textboxes. All the contact information is stored in an array. I was playing with the code, and I know how to display just the information in the listbox, which is the first and last name, but I need ALL of the contact info displayed in the textboxes. I have included my code, and a screenshot of what should happen when a name is selected from the listbox.

http://carlmartin10.webs.com/pics/Screen.JPG

public partial class frmAddressBook : Form
{
// Create Array to hold contact information
string[] Contacts = new string[20];
int i = 0;

public frmAddressBook()
{
InitializeComponent();
}

private void AddressBook_Load(object sender, EventArgs e)
{
}

private void btnExit_Click(object sender, EventArgs e)
{
// Close program
this.Close();
}

private void btnAdd_Click(object sender, EventArgs e)
{
// Declare variables
string lastName = tbxLastName.Text;
string firstName = tbxFirstName.Text;
string street = tbxStreet.Text;
string city = tbxCity.Text;
string state = tbxState.Text;
string zip = tbxZip.Text;

// Add contact information for New contact to array
if (i < 20)
{

Contacts[i] = lastName + ", " + firstName;

i++;

}

else
{

MessageBox.Show("The Address Book Can Hold a Maximum of 20 Contacts.", "Address Book Full");

}


//Clear TextBoxes
tbxFirstName.Text = "";
tbxLastName.Text = "";
tbxStreet.Text = "";
tbxCity.Text = "";
tbxState.Text = "";
tbxZip.Text = "";

// Display list of contact names from array in the listbox
lstContacts.Items.Clear();

for (int j = 0; j < 20; j++)
{

if (Contacts[j] != null)
{

lstContacts.Items.Add(Contacts[j].ToString().Trim());

}


}
}

private void btnDelete_Click(object sender, EventArgs e)
{
// Delete selected contact
for (int k = 0; k < Contacts.Length; k++)
{

try
{

if (Contacts[k].Contains(lstContacts.SelectedItem.ToString()))
{

Contacts[k] = null;

}

}

catch
{

}



}

//Display Refreshed Records in ListBox
lstContacts.Items.Clear();

for (int j = 0; j < 10; j++)
{

if (Contacts[j] != null)
{

lstContacts.Items.Add(Contacts[j].ToString().Trim());

}

}



}

private void lstContacts_SelectedIndexChanged(object sender, EventArgs e)
{
tbxFirstName.Text = lstContacts.SelectedItem.ToString();

}
}
}

Iceplug
01-20-2010, 02:35 PM
All the contact information is stored in an array.
It doesn't look like it is because
string[] Contacts = new string[20];
all you have is an array of strings... but even more important:
Contacts[i] = lastName + ", " + firstName;
the only thing you put in the array item is the lastname and the firstname.

I'd recommend making a struct to keep the associated items separate and then you can make an array of structs to access the elements directly using just the index and the name of the property you want. :)

CarlMartin10
01-20-2010, 02:39 PM
I noticed those issues, that is something I can figure out. The real thing I have no clue about is how to tell C# that when a name is highlighted in the listbox, that their information needs to be put into the correct textboxes. I am not an expert on C#, and I am trying to teach myself. I have a book, and that is where this problem is from. As I run into issues I can not figure out, I ask for help. The book says to use just an array, unfortunately. The idea is to learn how to do it this way. I am trying to learn how to do this. I will keep working on it, thanks.

Iceplug
01-21-2010, 10:13 AM
The issue still remains: where is their information stored? You can't just get this information without telling where it comes from.

CarlMartin10
01-21-2010, 10:38 AM
I am going to just use an array of structures. I know I do not have all the data being stored in the array, I was trying to just put in the first and last name, then once I figured out how to get that to display correctly, I would know how to add the rest.

Iceplug
01-21-2010, 12:20 PM
If you have structs set up then you have two options:
Store the items in the listbox so that they have the same indices as the corresponding item in the array of structures.
That way, when you click on an item in the listbox, you can use the SelectedIndex as an index to the array of structures and populate the textboxes from there.

The other method would involve searching (more work, but doesn't require the items in the listbox to be in a certain order; facilitates sorting the listbox alphabetically) the array structure for the item with the matching first and last names, finding the matching index, and then using that index for the array of structures to populate the textboxes.
:)

CarlMartin10
01-21-2010, 05:14 PM
If you have structs set up then you have two options:
Store the items in the listbox so that they have the same indices as the corresponding item in the array of structures.
That way, when you click on an item in the listbox, you can use the SelectedIndex as an index to the array of structures and populate the textboxes from there.

The other method would involve searching (more work, but doesn't require the items in the listbox to be in a certain order; facilitates sorting the listbox alphabetically) the array structure for the item with the matching first and last names, finding the matching index, and then using that index for the array of structures to populate the textboxes.
:)

Sounds like this is gonna be pretty complicated and a lot of work. I may use classes instead, I am undecided...As long as there is an array to store the data then the requirement is met. Thanks.

CarlMartin10
01-25-2010, 10:20 PM
OK, here is my current code. I can not figure out how to code the Delete Button to delete the item in the array of structs that matches the selected item in the listbox. This code obviously has errors - I am trying to figure this out.

amespace AddressBook
{
public partial class frmAddressBook : Form
{
public frmAddressBook()
{
InitializeComponent();
}

private void frmAddressBook_Load(object sender, EventArgs e)
{
}
public struct Contacts
{
public string FirstName;
public string LastName;
public string Street;
public string City;
public string State;
public string Zip;

} Contacts [] arrayOfContacts = new Contacts [20];
int index = 0;

private void btnExit_Click(object sender, EventArgs e)
{
// Close program
this.Close();

}

private void btnAdd_Click(object sender, EventArgs e)
{
// Declare variables
Contacts entry;
entry.FirstName = tbxFirstName.Text;
entry.LastName = tbxLastName.Text;
entry.Street = tbxStreet.Text;
entry.City = tbxCity.Text;
entry.State = tbxState.Text;
entry.Zip = tbxZip.Text;

// Add contact to array
if (index < 20)
{
arrayOfContacts[index++] = entry;

}

else
{
MessageBox.Show("The Address Book Can Hold a Maximum of 20 Contacts.", "Address Book Full");
}


// Display Contacts (First and Last name only) in Listbox
lstContacts.Items.Add(entry.LastName + ", " + entry.FirstName);

// Return message when Array reaches maximum of 20 entries

// Clear Text Boxes
tbxFirstName.Text = "";
tbxLastName.Text = "";
tbxStreet.Text = "";
tbxCity.Text = "";
tbxState.Text = "";
tbxZip.Text = "";

}

private void btnDelete_Click(object sender, EventArgs e)
{
for (int k = 0; k < arrayOfContacts.Length; k++)


try
{

if (arrayOfContacts[k] = lstContacts.SelectedItem.ToString())
{

arrayOfContacts[k] = null;

}

}

catch
{

}



}

Display Refreshed Records in ListBox
lstContacts.Items.Clear();

for (int j = 0; j < index; j++)
{

if (arrayOfContacts[j] != null)
{

lstContacts.Items.Add(arrayOfContacts[j].ToString().Trim());


}

}

}

Iceplug
01-25-2010, 10:46 PM
If you 'delete' an item in an array, you end up with a vacancy in the middle of the array.
To move the vacancy to the end of the array, you need to use a For Loop: starting at the deleteindex + 1, you'll replace item 'X' with item 'X + 1' ... this will move an item towards the beginning of the array by one. Doing this in a For Loop will move all of the items after the delete index towards the beginning by one, removing the vacancy in the For Loop.

CarlMartin10
01-25-2010, 10:47 PM
Wow, that sounds like a pain in the ***. I am super new to C#, this particular problem is kicking my *****.

Iceplug
01-25-2010, 10:54 PM
Removing the vacancy may not be necessary, unless you want to remove it by pushing it to the end of the array.
Searching the array shouldn't be affected if there is an empty element... you'd just have to empty the element yourself by setting all of the members to 0 or ""... setting it to null won't work.
The principle is the same in VB as it would be in C#.

CarlMartin10
01-25-2010, 10:57 PM
thanks

AtmaWeapon
01-26-2010, 09:18 AM
I think we're kind of going off into the weeds here.

In a professional application, you'd have several layers of abstraction at work because there'd be a strong separation between data and UI. Moving a rung down the abstraction ladder, you would make a data class and a user control that can display that data class. Moving a rung further down, you'd have a data class and a set of controls used to display that data class. At the bottom of the abstraction ladder, you have parallel arrays. However, the complexity ladder is inverted. With layers of abstraction in place, you just say "display this data" and don't care about how it happens. With the user control, you still say "display this data". Without the user control, you say "put this property in this control, this property in this control, ...". With parallel arrays, you say "put the value from this array into this control, ...". The less abstraction you use, the harder it is to do anything. The more abstraction you use, the harder it is to understand how everything works together. You want something in the middle.

I don't think you need an array at all. You have stated that you want to be able to remove items. To remove an item from an array you have to shift all of the other elements to fill in the hole or write logic that recognizes holes and ignores them. This isn't unique to C#; this is true in every language that has arrays (though sometimes the language runtime has mechanisms to help.) It's good to know how arrays work, but 99% of the time it's better to work at a higher level of abstraction.

Another thing: structures are for advanced cases in .NET. They have value semantics and can hurt performance except in very specific scenarios. It is best to use classes by default and only switch to structures when you know you need them. Less than 1% of the code I write uses structures.

I'll demonstrate a solution at the 2nd lowest level of abstraction. This is the level that doesn't use a UserControl but still uses a class. You start by defining the data type you want to represent; in this case it's an Employee with a name and ID:

public class Employee
{
public integer Id { get; set; }
public string Name { get; set; }
}

Since you want to add/remove employees, you really don't want to use an array. Arrays are the lowest-level collection type, and are optimized for data sets that are of a fixed or predictable size. For example, if you know there are always 10 employees, an array is appropriate. If there are always 10, 20, or 30 employees, an array is still appropriate. If there can be any number of employees and the number will change at runtime, arrays are inappropriate. The List<T> generic class represents a list data structure, which is like an array that can change its size on a whim. There's some performance implications, but part of List<T>'s implementation hides this. If you're unfamiliar with the "<T>" part, that means it's a generic type. Generic types take a "type parameter" that controls how they behave. The best way to describe the difference is to compare it to an array:
int[] values = new int[10]; // 10-element array of integers
List<int> valuesList = new List<int>(); // ???-element list of integers
The type parameter in a list describes what the element type of the list will be. You can put any type in place of T, even List<T> itself, but you *must* replace T with a type before you can use it.

Now, that aside, let's talk about the form. Obviously you'll have a ListBox, I'm calling mine lstEmployees. There will be two text boxes for the ID and name of the selected employee (txtId, txtName.) There will be a "remove" button that will remove an employee from the list (btnRemove.) Anything else (labels, group boxes, etc.) is unimportant to the topic.

Step 1 is making a list of employees. To do this, I used a loop to make 10 employees; you'll likely be doing something else. After the list of employees is made, you have to do something with it. Windows Forms leads you to believe you have to put strings in a ListBox, but in reality the control can store *anything* so long as you tell it how to display the object. I'm going to put the whole list of employees into the ListBox directly. I also set the DisplayMember property. By default, the ListBox just displays whatever its contents' ToString() method returns. If you set DisplayMember to a property name, the ListBox will look for a property with that name and use that instead. Here's what my Load event handler looks like:
private void Form1_Load(object sender, EventArgs e)
{
// Get your list of employees and store them in the form; your method may vary.
for (int i = 0; i < 10; i++)
{
lstEmployees.Items.Add(new Employee() { Id = i, Name = "Employee " + i.ToString()});
}

lstEmployees.DisplayMember = "Name";
}

Instead of an array or list of employees, we have a ListBox filled with employees. The list box is told how to display an employee (it displays the name.) Running the form at this point gets us 10 employees in a list box. Yay!

Step 2 is getting information about the currently selected employee into the text boxes. To do this, watch one of the selection change events on the list box; I'm going to use SelectedItemChanged. When the selected item changes, I'll get that item and put the appropriate values in the text boxes. Keep in mind that if nothing is selected, nothing should go in the text boxes:

private void lstEmployees_SelectedIndexChanged(object sender, EventArgs e)
{
txtId.Clear();
txtName.Clear();

Employee selectedEmployee = (Employee)lstEmployees.SelectedItem;
if (selectedEmployee != null)
{
txtId.Text = selectedEmployee.Id.ToString();
txtName.Text = selectedEmployee.Name;
}
}

In summary, this clears the old information then attempts to replace it with the new information. Notice that we didn't even have to fool with an array; in this case all we care about is the selected item, and the ListBox is capable of giving it to us. Do note that SelectedItem is of type object, so you have to cast it to Employee before treating it as an Employee.

Now step 3: list removal. The ListBox has an Items property that represents all of the items in the list. This collection has a Remove() method that lets you remove an item. When the "Remove" button is clicked, we want to remove the selected item. We already know how to get the selected item (and that there might not be one), so all we have to add is a call to Remove():

private void btnRemove_Click(object sender, EventArgs e)
{
Employee selectedEmployee = (Employee)lstEmployees.SelectedItem;
if (selectedEmployee != null)
{
lstEmployees.Items.Remove(selectedEmployee);
}
}

Ta da! Remove() is smart enough to know how to fill in the hole left by the deleted item, so it's done for you. This actually clears the selection in the ListBox; in a professional application you'd set the selection to another item; I omitted this because it's slightly complicated.

You *can* use an array or a list of items in conjunction with a ListBox. My first years of VB .NET were spent writing code this way, because it's how things worked in VB6 and how people taught me to do it in VB .NET. When I stumbled upon the fact that you can make the ListBox the list itself, my applications got *much* simpler because I quit having to write synchronization code. In theory, data binding is supposed to make things easier, but that was just a dream until WPF got data binding right. I'm not walking through a WPF solution.

I'm not trying to usurp or discredit Iceplug's comments so far. I've noticed that we tend to want to teach beginners the lowest-level technique of doing something (I do this a lot too.) I was taught to use arrays for this at first, and it led to tons of extra code and time spent debugging minor synchronization errors. The problem is we're seeing things from years of experience, and in many cases not understanding arrays leads to not understanding other, more advanced concepts. If I didn't understand how arrays work, I wouldn't know so much about how lists work. So we teach arrays, and the beginners learn them, and no one remembers to follow up with the "once you know arrays, you should take advantage of this..." version of the story. It took me 2 years to figure out that a ListBox *is* an array. You just learned it in a week. :)

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum