I found an issue of a beginner developer on a forum about importing a CSV phonebook file into DataGridView and having some search option.
First of all, I have created 2 forms:
- one for the main form
- one for adding new phonebook entry
The main form has a DataGridView, a MenuStrip, an OpenFileDialog, a SaveFileDialog, a Label, a TextBox and a Button. The MenuStrip has 2 items: File and Phonebook. File menu has 4 elements: Open CSV, Save…, a separator and Exit. The Phonebook menu has only 1 menu: Add…
Now create our Phonebook class and implement the IComparable interface (so the csv file items will be sorted alphabetically. Let’s say we have 6 fields (by request of the guy at the forum):
- Station
- Section
- Infaco
- Location
- DirectDial
- AutoNumber
(if I were him, I would have an ID field too…)
We also need 3 constructors of the class: one empty, one with the 6 arguments mentioned above as fields and one with string array for the CSV import). Also need a public method for CSV export, called ToCsv().
Finally the most interesting part of the class: implementing the interface with the method of public int CompareTo(object obj)
The full class looks like this:
public class Phonebook : IComparable { public string Station { get; set; } public string Section { get; set; } public string Infaco { get; set; } public string Location { get; set; } public string DirectDial { get; set; } public int AutoNumber { get; set; } public Phonebook() { } public Phonebook(string station, string section, string infaco, string location, string directDial, int autoNumber) { Station = station; Section = section; Infaco = infaco; Location = location; DirectDial = directDial; AutoNumber = autoNumber; } public static Phonebook FromCsv(string csvLine) { string[] values = csvLine.Split(','); Phonebook phonebook = new Phonebook(); phonebook.Station = values[0]; phonebook.Section = values[1]; phonebook.Infaco = values[2]; phonebook.Location = values[3]; phonebook.DirectDial = values[4]; phonebook.AutoNumber = Convert.ToInt32(values[5]); return phonebook; } public string ToCsv() { return Station + "," + Section + "," + Infaco + "," + Location + "," + DirectDial + "," + AutoNumber; } public int CompareTo(object obj) { string s1 = this.Station as string; if (s1 == null) return 0; string s2 = (obj as Phonebook).Station as string; if (s2 == null) return 0; int len1 = s1.Length; int len2 = s2.Length; int marker1 = 0; int marker2 = 0; while (marker1 < len1 && marker2 < len2) { char ch1 = s1[marker1]; char ch2 = s2[marker2]; char[] space1 = new char[len1]; int loc1 = 0; char[] space2 = new char[len2]; int loc2 = 0; do { space1[loc1++] = ch1; marker1++; if (marker1 < len1) { ch1 = s1[marker1]; } else { break; } } while (char.IsDigit(ch1) == char.IsDigit(space1[0])); do { space2[loc2++] = ch2; marker2++; if (marker2 < len2) { ch2 = s2[marker2]; } else { break; } } while (char.IsDigit(ch2) == char.IsDigit(space2[0])); string str1 = new string(space1); string str2 = new string(space2); int result; if (char.IsDigit(space1[0]) && char.IsDigit(space2[0])) { int thisNumericChunk = int.Parse(str1); int thatNumericChunk = int.Parse(str2); result = thisNumericChunk.CompareTo(thatNumericChunk); } else { result = str1.CompareTo(str2); } if (result != 0) { return result; } } return len1 - len2; } }
On the form create a class level List<> variable of our phonebook type:
List<Phonebook> phonebookFromCsv;
This will keep our phonebook entries.
Let’s create the CSV opening event on the form:
private void openCSVToolStripMenuItem_Click(object sender, EventArgs e) { if (ofdCsv.ShowDialog() == DialogResult.OK) { if (phonebookFromCsv == null) phonebookFromCsv = File.ReadAllLines(ofdCsv.FileName) .Skip(1) .Select(x => Phonebook.FromCsv(x)) //.Where(x => x.Station != "")//optional conditions here .ToList(); else { phonebookFromCsv.AddRange(File.ReadAllLines(ofdCsv.FileName) .Skip(1) .Select(x => Phonebook.FromCsv(x)) //.Where(x => x.Station != "")//optional conditions here .ToList()); } phonebookFromCsv.Sort(); BindData(); } }
The BindData() method only sets the datasource of the DataGridView:
private void BindData() { dataGridView1.DataSource = null; dataGridView1.DataSource = phonebookFromCsv; addToolStripMenuItem.Enabled = true; dataGridView1.ClearSelection(); }
If we successfully read our CSV file, we should save it:
private void saveToolStripMenuItem_Click(object sender, EventArgs e) { if (sfdCsv.ShowDialog() == DialogResult.OK) { StreamWriter writer = new StreamWriter(sfdCsv.FileName); writer.WriteLine("Station ,Section,Infaco,Location ,Direct Dial,Auto No."); for (int i = 0; i < dataGridView1.Rows.Count; i++) { Phonebook entry = (Phonebook)dataGridView1.Rows[i].DataBoundItem; writer.WriteLine(entry.ToCsv()); } writer.Close(); } }
Create a TextChanged event for the TextBox. This will select every row where our search condition fits:
private void txbSearch_TextChanged(object sender, EventArgs e) { try { dataGridView1.ClearSelection(); if (txbSearch.Text.Length > 0) { foreach (DataGridViewRow row in dataGridView1.Rows) { for (int i = 0; i < row.Cells.Count; i++) { if (row.Cells[i].Value != null && row.Cells[i].Value.ToString().ToLower().Contains(txbSearch.Text.ToLower())) { int rowIndex = row.Index; dataGridView1.Rows[rowIndex].Selected = true; break; } } } } else { dataGridView1.ClearSelection(); } } catch (Exception ex) { MessageBox.Show(ex.Message); } }
Finally create the window opening event for the NewEntry form:
private void addToolStripMenuItem_Click(object sender, EventArgs e) { NewEntry dialog = new NewEntry(); if (dialog.ShowDialog() == DialogResult.OK) { if (phonebookFromCsv != null) phonebookFromCsv.Add(dialog.Entry); else { phonebookFromCsv = new List(); phonebookFromCsv.Add(dialog.Entry); } BindData(); } }
The main form should look something similar to this:
The new form should have only 6 labels with 6 textboxes (or 5 textboxes with 1 NumericUpDown for the AutoNumber field), a public field for the new entry and one button. This button should have DialogResult set to OK. The form code looks like this:
public partial class NewEntry : Form { public Phonebook Entry { get; set; } public NewEntry() { InitializeComponent(); } private void btnOK_Click(object sender, EventArgs e) { try { Entry = new Phonebook(txbStation.Text, txbSection.Text, txbInfaco.Text, txbLocation.Text, txbDirectDial.Text, (int)nudAutoNumber.Value); } catch (Exception ex) { MessageBox.Show(ex.Message); DialogResult = DialogResult.None; } } }
This form design should look like this:
If we start typing something in the search textbox, the row that fits the criteria will be selected. Let’s type: ne
Finally let’s save our new phonebook to a new CSV.