Search code examples
c#winformslistviewcolumnheader

How can I read ListView Column Headers and their values?


I've been trying to find out a way to read data from the selected ListView row and display each value in their respected TextBox for easy editing.

The first and easiest way would be something like this:

ListViewItem item = listView1.SelectedItems[0];

buyCount_txtBox.Text = item.SubItems[1].Text;
buyPrice_txtBox.Text = item.SubItems[2].Text;
sellPrice_txtBox.Text = item.SubItems[3].Text;

There is nothing wrong with that code but I have around 40 or more TextBoxes that should display data. Coding all 40 or so would become very tedious.

The solution I've come up with, is to get all TextBox Controls in my User Control like so:

    foreach (Control c in this.Controls)
    {
        foreach (Control childc in c.Controls)
        {
            if (childc is TextBox)
            {
            }
        }
    }

Then I need to loop the selected ListView row column headers. If their column header matches TextBox.Tag then display the column value in their respected TextBox.

The final code would look something like this:

    foreach (Control c in this.Controls)
    {
        foreach (Control childc in c.Controls)
        {

          // Needs another loop for the selected ListView Row

            if (childc is TextBox && ColumnHeader == childc.Tag)
            {
               // Display Values
            }
        }
    }

So then my question would be: How can I loop through the selected ListView Row and each column header.


Solution

  • Looping over your ColumnHeaders is simply done like this:

    foreach(  ColumnHeader  lvch  in listView1.Columns)
    {
        if (lvch.Text == textBox.Tag) ; // either check on the header text..
        if (lvch.Name == textBox.Tag) ; // or on its Name..
        if (lvch.Tag  == textBox.Tag) ; // or even on its Tag
    }
    

    However the way you loop over your TextBoxes is not exactly nice even if it works. I suggest that you add each of the participating TextBoxes into a List<TextBox>. Yes, that means to add 40 items, but you can use AddRange maybe like this:

    To fill a list myBoxes:

    List<TextBox> myBoxes = new List<TextBox>()
    
    public Form1()
    {
        InitializeComponent();
        //..
        myBoxes.AddRange(new[] {textBox1, textBox2, textBox3});
    }
    

    Or, if you really want to avoid the AddRangeand also stay dynamic, you can also write a tiny recursion..:

    private void CollectTBs(Control ctl, List<TextBox> myBoxes)
    {
        if (ctl is TextBox) myBoxes.Add(ctl as TextBox);
        foreach (Control c in ctl.Controls) CollectTBs(c, myBoxes);
    }
    

    Now your final loop is slim and fast:

    foreach(  ColumnHeader  lvch  in listView1.Columns)
    {
        foreach (TextBox textBox in myBoxes)
            if (lvch.Tag == textBox.Tag)  // pick you comparison!
                textBox.Text = lvch.Text;
    }
    

    Update: since you actually want the SubItem values the solution could look like this:

    ListViewItem lvi = listView1.SelectedItems[0];
    foreach (ListViewItem.ListViewSubItem lvsu in  lvi.SubItems)
        foreach (TextBox textBox in myBoxes)
           if (lvsu.Tag == textBox.Tag)  textBox.Text = lvsu.Text;