简体   繁体   中英

Export ListView Items to Excel (AutoWidth Columns, Headers in First Row, Hide Certain Rows)

I've got a small application that I'm trying to add an enhancement to for, on button click, to export all data in a form listview to an Excel worksheet. After a lot of searching, I came upon this solution at DaniWeb that does a good job, but I'm trying to expand on it.

Excel.Application app = new Excel.Application();
app.Visible = true;
Excel.Workbook wb = app.Workbooks.Add(1);
Excel.Worksheet ws = (Excel.Worksheet)wb.Worksheets[1];
int i = 1;
int i2 = 1;
foreach (ListViewItem lvi in lvData.Items)
{
    i = 1;
    foreach (ListViewItem.ListViewSubItem lvs in lvi.SubItems)
    {
        ws.Cells[i2, i] = lvs.Text;
        i++;
    }
    i2++;
}

Excel.Range rng = null;
rng = Excel.Range("A1:Z" + lvData.Items.Count); // Error: Microsoft.Office.InterOp.Excel.Range is a 'type', which is not valid in the given context.
rng.Columns.AutoFit();

// RANGE COPY IN WORD INTEROP
// oWord.ActiveDocument.Sections[cnt].Range.Copy();
// Set focus to the New Word Doc instance
// oNewWord.Activate();
// Paste copied range to New Word Doc
// oNewWord.ActiveDocument.Range(0, 0).Paste();

MessageBox.Show("Export Completed", "Export to Excel", MessageBoxButtons.OK, MessageBoxIcon.Information);

I also want to:

  1. Automatically set all Cell Widths to that of the data.
  2. Place values of ListView column Headers into first row of Excel.
  3. I have 26 total columns in my listview, but in reality I only need to export around 10 to the user (I am hiding many of these columns from user view on the frontend, and then using them on the backend for other functionality processing).

I'm still looking, but haven't found any solutions as of yet. I have experience going from Excel/Access to .Txt and likewise, but haven't ever done ListView to Excel. Anyone have ideas on how I may accomplish the above?

Thanks!

EDIT:

Using Derek's suggestion all the columns now get their widths set automatically at the end of processing to the size of the largest cell content in the column. Using Miles suggestion, I did some toying around and managed to cobble together some code to insert the ColumnHeader values into the first Excel Row.

Now just trying to figure out how to hide certain unwanted columns OR not export specific columns.

                // http://www.daniweb.com/software-development/csharp/threads/192620/listview-to-excel
                Excel.Application app = new Excel.Application();
                app.Visible = true;
                Excel.Workbook wb = app.Workbooks.Add(1);
                Excel.Worksheet ws = (Excel.Worksheet)wb.Worksheets[1];
                int i = 1;
                int i2 = 2;
                int x = 1;
                int x2 = 1;
                int j = 0;
                int colNum = lvData.Columns.Count;

                // Set first ROW as Column Headers Text
                foreach (ColumnHeader ch in lvData.Columns)
                {
                        ws.Cells[x2, x] = ch.Text;
                        x++;
                }

                foreach (ListViewItem lvi in lvData.Items)
                {
                    i = 1;
                    foreach (ListViewItem.ListViewSubItem lvs in lvi.SubItems)
                    {
                        ws.Cells[i2, i] = lvs.Text;
                        i++;
                    }
                    i2++;
                }

                // AutoSet Cell Widths to Content Size
                ws.Cells.Select();
                ws.Cells.EntireColumn.AutoFit();

                MessageBox.Show("Export Completed", "Export to Excel", MessageBoxButtons.OK, MessageBoxIcon.Information);

EDIT2 :

I did something in another program using a streamwriter where I looked at specific indices. If it was not the last indice (column), I appended a value to the end of a long string. If it was the last value (column), I wrote the entire string out to file. In this manner I could export values from ListView to a (!) delimited .Txt file. I am attempting something similar here for #3, but it's not quite there yet:

                // indices is used to designate which columns in the ListView we want
                var indices = new int[] { 0, 1, 2, 8, 9, 15, 19, 22, 23, 24, 25 };
                foreach (ListViewItem lvi in lvData.Items)
                {
                    i = 1;
                    foreach (int id in indices)
                    {
                        ws.Cells[i2, i] = lvi.SubItems.ToString();
                        i++;
                    }

                    //foreach (ListViewItem.ListViewSubItem lvs in lvi.SubItems)
                    //{
                    //    ws.Cells[i2, i] = lvs.Text;
                    //    i++;
                    //}
                    i2++;
                }

For the first part, it looks like you can do something like this:

    ws.Cells[i, j].ColumnWidth = lvs.Text.Length;

Check this page: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.range_members.aspx

For the second and third part, I'm a little confused what the issue is. Could you not just iterate through your ListView and write those column values to the first row?

For autofit:

  //make all columns autofit
  workSheet.Cells.Select();
  workSheet.Cells.EntireColumn.AutoFit();

for column headers (using Linq):

(may need to adjust indices, but I think not)

  foreach ( ColumnHeader column in lv.Columns.OfType<ColumnHeader>() )
  {
     workSheet.Cells[ 1, column.Index + 1 ] = column.Name;
  }

Then you probably need to adjust the next bit of your code. I'd loop through ListViewItems and set the cells individually.

You can handle looping through the listviewitems and doing only the ones that you want. eg first ten. I would just do worksheet.Cells[ rowIndex, columnIndex ] = ... to set all of the values.

One of the tricks.
SubItems actually starts at the second column.

listViewItem.Name gives you the first column

so:

within looping through listViewItems...

worksheet.Cells[ rowIndex, 1 ] = listViewItem.Name;
j = 2;

foreach (ListViewItem.ListViewSubItem lvs in lvi.SubItems)
{
   ws.Cells[rowIndex, j] = lvs.Text;
   j++;
 }
 j++;

Bro, the answer is already in your code. I don't know if you noticed already. You made an array of Integers, now use it. Like:

            var indices = new int[] { 0, 1, 2, 8, 9, 15, 19, 22, 23, 24, 25 };

            foreach (ListViewItem lvi in lvData.Items)
            {
                i = 1;
                int bol = lvi.SubItems.Count;

                foreach (int id in indices)
                { 
                    if (bol > 0)
                    {
                       ws.Cells[i2, i] = lvi.SubItems[id].Text;
                       i++;
                       bol--;
                    }
                }
                i2++;
            }

That's the easiest way of doing it, for me. But if you want something prettier, well, luck! I assure you that it works!

PD: That if that I wrote is to prevent an error, feel free to do something else. But in this way it works, trust me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM