I have a main directory which contains many sub directories. In each sub directory there will be images. I have managed to export images from each subdirectory into each excel spreadsheet in a excel workbook. For example, if I have 10 sub directories, there will be 1 excel workbook with 10 excel spreadsheets, in each spreadsheet there will be each sub directory's images.
What I want to accomplish now is if there is no image in any sub directory, the sub directory exported to an excel spreadsheet will be blank. I want to add "No image found" to that blank excel spreadsheet as text.
This is what I have tried:
foreach (string subdir in filesindirectory)
{
string[] splitter = subdir.Split('\\');
string folderName = splitter[splitter.Length - 1];
ExcelWorksheet ws = package.Workbook.Worksheets.Add("Worksheet-" + folderName); //create new worksheet
ImageCount = 0;
if (Directory.GetFiles(subdir).Length == 0)
{
ws.Cells["J9:L10"].Merge = true;
ws.Cells["J9:L10"].Style.VerticalAlignment = ExcelVerticalAlignment.Top;
ws.Cells["J9:L10"].Value = "No chart to display";
ws.Cells["J9:L10"].Style.Font.Size = 16;
}
foreach (string img in Directory.GetFiles(subdir))
{
ImageCount++;
System.Drawing.Image Image1 = System.Drawing.Image.FromFile(img);
var AddImage = ws.Drawings.AddPicture("Image" + ImageCount.ToString(), Image1 );
Image1 .Dispose();
// Row, RowoffsetPixel, Column, ColumnOffSetPixel
if (ImageCount > 1)
{
AddImage .SetPosition(ImageFinalPosition, 0, 2, 0);
AddImage .SetSize(770, 450);
ImageFinalPosition += (ImagePosition + 1); // Add 1 to have 1 row of empty row
}
else
{
AddImage.SetPosition(ImageCount, 0, 2, 0);
AddImage.SetSize(770, 450);
ImageFinalPosition = (ImageCount + ImagePosition) + 1; // Add 1 to have 1 row of empty row
}
The problem I faced now is when I add the text to specific merged cells, if I view the spreadsheet with different screen size, smaller screen size will display text at centre of spreadsheet while larger screen size will display the text on the left of the spreadsheet.
This is a sample of what I see:
Smaller screensize(laptop):
Larger screensize(desktop):
This is the output I want to achieve(all screensize):
Please help me on this, thanks!
You must understand how foreach loop works..
If your count is 0 your code in foreach doesn't run at all
You should put your checking before the foreach loop to write inside the worksheet
Put your code before the foreach loop as per below
if (Directory.GetFiles(subdir).Length == 0) //if no image in that sub directory
{
ws.Cells["A1:A3"].Merge = true;
ws.Cells["A1:A3"].Style.VerticalAlignment = ExcelVerticalAlignment.Top;
ws.Cells["A1:A3"].Style.Border.Top.Style = ExcelBorderStyle.Thin;
ws.Cells["A1:A3"].Style.Border.Left.Style = ExcelBorderStyle.Thin;
ws.Cells["A1:A3"].Style.Border.Right.Style = ExcelBorderStyle.Thin;
ws.Cells["A1:A3"].Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
ws.Cells["A1:A3"].Style.Fill.PatternType = ExcelFillStyle.Solid;
ws.Cells["A1:A3"].Style.Fill.BackgroundColor.SetColor(System.Drawing.ColorTranslator.FromHtml("#f0f3f5"));
ws.Cells["A1:A3"].Value = "content not found";
}
foreach (string img in Directory.GetFiles(subdir))
{
// Your else code
}
Related
I am using Microsoft DocumentFormat.OpenXml SDK to read data from excel file.
While doing so I am taking into consideration if a cell has blank values(If Yes, read that too).
Now, facing issues with one of the excel sheets where the workSheet.SheetDimension is null hence the code is throwing an exception.
Code used :
class OpenXMLHelper
{
// A helper function to open an Excel file using OpenXML, and return a DataTable containing all the data from one
// of the worksheets.
//
// We've had lots of problems reading in Excel data using OLEDB (eg the ACE drivers no longer being present on new servers,
// OLEDB not working due to security issues, and blatantly ignoring blank rows at the top of worksheets), so this is a more
// stable method of reading in the data.
//
public static DataTable ExcelWorksheetToDataTable(string pathFilename)
{
try
{
DataTable dt = new DataTable();
string dimensions = string.Empty;
using (FileStream fs = new FileStream(pathFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (SpreadsheetDocument document = SpreadsheetDocument.Open(fs, false))
{
// Find the sheet with the supplied name, and then use that
// Sheet object to retrieve a reference to the first worksheet.
//Sheet theSheet = document.WorkbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == worksheetName).FirstOrDefault();
//--Sheet theSheet = document.WorkbookPart.Workbook.Descendants<Sheet>().FirstOrDefault();
//--if (theSheet == null)
//-- throw new Exception("Couldn't find the worksheet: "+ theSheet.Id);
// Retrieve a reference to the worksheet part.
//WorksheetPart wsPart = (WorksheetPart)(document.WorkbookPart.GetPartById(theSheet.Id));
//--WorksheetPart wsPart = (WorksheetPart)(document.WorkbookPart.GetPartById(theSheet.Id));
WorkbookPart workbookPart = document.WorkbookPart;
WorksheetPart wsPart = workbookPart.WorksheetParts.FirstOrDefault();
Worksheet workSheet = wsPart.Worksheet;
dimensions = workSheet.SheetDimension.Reference.InnerText; // Get the dimensions of this worksheet, eg "B2:F4"
int numOfColumns = 0;
int numOfRows = 0;
CalculateDataTableSize(dimensions, ref numOfColumns, ref numOfRows);
//System.Diagnostics.Trace.WriteLine(string.Format("The worksheet \"{0}\" has dimensions \"{1}\", so we need a DataTable of size {2}x{3}.", worksheetName, dimensions, numOfColumns, numOfRows));
SheetData sheetData = workSheet.GetFirstChild<SheetData>();
IEnumerable<Row> rows = sheetData.Descendants<Row>();
string[,] cellValues = new string[numOfColumns, numOfRows];
int colInx = 0;
int rowInx = 0;
string value = "";
SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
// Iterate through each row of OpenXML data, and store each cell's value in the appropriate slot in our [,] string array.
foreach (Row row in rows)
{
for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
// *DON'T* assume there's going to be one XML element for each column in each row...
Cell cell = row.Descendants<Cell>().ElementAt(i);
if (cell.CellValue == null || cell.CellReference == null)
continue; // eg when an Excel cell contains a blank string
// Convert this Excel cell's CellAddress into a 0-based offset into our array (eg "G13" -> [6, 12])
colInx = GetColumnIndexByName(cell.CellReference); // eg "C" -> 2 (0-based)
rowInx = GetRowIndexFromCellAddress(cell.CellReference) - 1; // Needs to be 0-based
// Fetch the value in this cell
value = cell.CellValue.InnerXml;
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
value = stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
}
cellValues[colInx, rowInx] = value;
}
}
// Copy the array of strings into a DataTable.
// We don't (currently) make any attempt to work out which columns should be numeric, rather than string.
for (int col = 0; col < numOfColumns; col++)
{
//dt.Columns.Add("Column_" + col.ToString());
dt.Columns.Add(cellValues[col, 0]);
}
//foreach (Cell cell in rows.ElementAt(0))
//{
// dt.Columns.Add(GetCellValue(doc, cell));
//}
for (int row = 0; row < numOfRows; row++)
{
DataRow dataRow = dt.NewRow();
for (int col = 0; col < numOfColumns; col++)
{
dataRow.SetField(col, cellValues[col, row]);
}
dt.Rows.Add(dataRow);
}
dt.Rows.RemoveAt(0);
//#if DEBUG
// // Write out the contents of our DataTable to the Output window (for debugging)
// string str = "";
// for (rowInx = 0; rowInx < maxNumOfRows; rowInx++)
// {
// for (colInx = 0; colInx < maxNumOfColumns; colInx++)
// {
// object val = dt.Rows[rowInx].ItemArray[colInx];
// str += (val == null) ? "" : val.ToString();
// str += "\t";
// }
// str += "\n";
// }
// System.Diagnostics.Trace.WriteLine(str);
//#endif
return dt;
}
}
}
catch (Exception ex)
{
return null;
}
}
public static void CalculateDataTableSize(string dimensions, ref int numOfColumns, ref int numOfRows)
{
// How many columns & rows of data does this Worksheet contain ?
// We'll read in the Dimensions string from the Excel file, and calculate the size based on that.
// eg "B1:F4" -> we'll need 6 columns and 4 rows.
//
// (We deliberately ignore the top-left cell address, and just use the bottom-right cell address.)
try
{
string[] parts = dimensions.Split(':'); // eg "B1:F4"
if (parts.Length != 2)
throw new Exception("Couldn't find exactly *two* CellAddresses in the dimension");
numOfColumns = 1 + GetColumnIndexByName(parts[1]); // A=1, B=2, C=3 (1-based value), so F4 would return 6 columns
numOfRows = GetRowIndexFromCellAddress(parts[1]);
}
catch
{
throw new Exception("Could not calculate maximum DataTable size from the worksheet dimension: " + dimensions);
}
}
public static int GetRowIndexFromCellAddress(string cellAddress)
{
// Convert an Excel CellReference column into a 1-based row index
// eg "D42" -> 42
// "F123" -> 123
string rowNumber = System.Text.RegularExpressions.Regex.Replace(cellAddress, "[^0-9 _]", "");
return int.Parse(rowNumber);
}
public static int GetColumnIndexByName(string cellAddress)
{
// Convert an Excel CellReference column into a 0-based column index
// eg "D42" -> 3
// "F123" -> 5
var columnName = System.Text.RegularExpressions.Regex.Replace(cellAddress, "[^A-Z_]", "");
int number = 0, pow = 1;
for (int i = columnName.Length - 1; i >= 0; i--)
{
number += (columnName[i] - 'A' + 1) * pow;
pow *= 26;
}
return number - 1;
}
}[enter image description here][1]
The SheetDimension part is optional (and therefor you cannot always rely on it being up to date). See the following part of the OpenXML specification:
18.3.1.35 dimension (Worksheet Dimensions)
This element specifies the used range of the worksheet. It specifies the row and column bounds of
used cells in the worksheet. This is optional and is not required.
Used cells include cells with formulas, text content, and cell
formatting. When an entire column is formatted, only the first cell in
that column is considered used.
So an Excel file without any SheetDimension part is perfectly valid, so you should not rely on it being present in an Excel file.
Therefor I'd suggest to simply parse all Row elements contained in the SheetData part, and "count" the number of rows (instead of reading the SheetDimensions part to get the number of rows / columns). This way you can also take into account that an Excel file may contain completely blank rows in-between the data.
I want to create an application which exports Excel files into HTML files using the Excel interop dll. One catch is that I want to make sure the .HTML file preview looks as close to reality as if it was being viewed in Excel.
When normally exporting Excel files as HTML files no borders / 'grid lines' are shown. To fix this issue you must add your own borders.
However when Excel renders coloured cells, grid lines are not shown.
I wrote the below C# code to add borders to cells that have "no fill":
using Microsoft.Office.Interop.Excel;
namespace CS_HelloExcel
{
class Program
{
static void Main()
{
//Create excel application:
Application application = new Application();
//Define filename "C:\Users\sancarn\Documents\"
String filename = "C:\\Users\\sancarn\\Documents\\myFormatedXL.xlsx";
//Open workbook as read only, don't update links
Workbook workbook = application.Workbooks.Open(filename, false, true);
foreach (Worksheet sheet in workbook.Sheets)
{
Range ur = sheet.UsedRange;
ur = ur.Resize[ur.Rows.Count + 1, ur.Columns.Count + 1];
//Create individual cells where blank cells originally occur - Takes 0.2s
ur.Replace("", "'");
// Takes 3.4s
foreach(Range cell in ur.Cells)
{
if(cell.Interior.Pattern == -4142)
{
Borders b = cell.Borders;
b[XlBordersIndex.xlEdgeBottom].LineStyle= XlLineStyle.xlContinuous;
b[XlBordersIndex.xlEdgeBottom].ThemeColor = 3;
b[XlBordersIndex.xlEdgeBottom].Weight = 2;
b[XlBordersIndex.xlEdgeLeft].LineStyle= XlLineStyle.xlContinuous;
b[XlBordersIndex.xlEdgeLeft].ThemeColor = 3;
b[XlBordersIndex.xlEdgeLeft].Weight = 2;
b[XlBordersIndex.xlEdgeRight].LineStyle= XlLineStyle.xlContinuous;
b[XlBordersIndex.xlEdgeRight].ThemeColor = 3;
b[XlBordersIndex.xlEdgeRight].Weight = 2;
b[XlBordersIndex.xlEdgeTop].LineStyle= XlLineStyle.xlContinuous;
b[XlBordersIndex.xlEdgeTop].ThemeColor = 3;
b[XlBordersIndex.xlEdgeTop].Weight = 2;
}
}
}
//Save workbook as html file takes 0.5s
application.DisplayAlerts = false;
workbook.SaveAs("myFormatedXL.html",XlFileFormat.xlHtml);
//Get workbook path
String path = workbook.FullName;
//Close application
workbook.Close(false);
application.Quit();
//Free up memory
application = null;
}
}
}
However as shown in the comments the the loop over all the cells takes 3.5 seconds... I was wondering if anyone knows how I can speed up this task?
This option seems to have better results:
//Takes 0ms?
Range b=null;
foreach(Range cell in ur.Cells)
{
if(cell.Interior.Pattern == -4142)
{
b = b!=null ? application.Union(b, cell) : cell;
}
}
b.Borders[XlBordersIndex.xlEdgeBottom].LineStyle = XlLineStyle.xlContinuous;
b.Borders[XlBordersIndex.xlEdgeBottom].ThemeColor = 3;
b.Borders[XlBordersIndex.xlEdgeBottom].Weight = 2;
b.Borders[XlBordersIndex.xlEdgeTop].LineStyle = XlLineStyle.xlContinuous;
b.Borders[XlBordersIndex.xlEdgeTop].ThemeColor = 3;
b.Borders[XlBordersIndex.xlEdgeTop].Weight = 2;
b.Borders[XlBordersIndex.xlEdgeLeft].LineStyle = XlLineStyle.xlContinuous;
b.Borders[XlBordersIndex.xlEdgeLeft].ThemeColor = 3;
b.Borders[XlBordersIndex.xlEdgeLeft].Weight = 2;
b.Borders[XlBordersIndex.xlEdgeRight].LineStyle = XlLineStyle.xlContinuous;
b.Borders[XlBordersIndex.xlEdgeRight].ThemeColor = 3;
b.Borders[XlBordersIndex.xlEdgeRight].Weight = 2;
b.Borders[XlBordersIndex.xlInsideHorizontal].LineStyle = XlLineStyle.xlContinuous;
b.Borders[XlBordersIndex.xlInsideHorizontal].ThemeColor = 3;
b.Borders[XlBordersIndex.xlInsideHorizontal].Weight = 2;
b.Borders[XlBordersIndex.xlInsideVertical].LineStyle = XlLineStyle.xlContinuous;
b.Borders[XlBordersIndex.xlInsideVertical].ThemeColor = 3;
b.Borders[XlBordersIndex.xlInsideVertical].Weight = 2;
First we make the range that needs to be formatted, and then afterwards we apply the formatting. However for some reason the formatting that comes as a result of this is... odd (in the resulting html file).
I might want to do this in CSS instead.
I have a datagridview with some datagridviewimagecolumn. The datagridview has cellcontentclick event as it is an image column (is this correct). When I click on the image it takes me to another dialog so I can choose an image. So far all works.
Now when it returns the image is good and shown. I then do a dataGridView2.ClearSelection() to deselect all cells. Next if I click again into the same cell the event does not trigger but if I click in another cell and then back into this cell it will trigger. I looked for things like reset events etc but cannot see anything helpful. How do I fix this?
Here is some code does this help:
clicking on the cell enters into this section
else if (e.ColumnIndex.ToString() == "3")
{
try
{
display_label = new ChooseLabel(dataGridView2[3, e.RowIndex].Tag.ToString()); // open dalog to choose the format
display_label.ShowDialog(this); // this to get the dialog to open on the same screen as the app
//when the Display_Layouts dialog closes we retrieve the data and use it here
BitmapFilenameToDisplay = display_label.txtbx_layout_bitmap.Text;
Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
Label_Bitmap = new Bitmap(BitmapFilenameToDisplay);
dataGridView2[e.ColumnIndex, e.RowIndex].Value = Label_Bitmap.GetThumbnailImage(175, 100, myCallback, IntPtr.Zero); //create a bitmap of the image
// file_name = display_label.txtbx_layout_bitmap.Text; // bitmap to showtxtbx_layout_bitmap
dataGridView2[e.ColumnIndex, e.RowIndex].Tag = display_label.txtbx_choice.Text + ".bmp"; //BitmapFilenameToDisplay; // + ".bmp";
try
{
BitmapFilenameToDisplay = LayoutPath + dataGridView2[2, e.RowIndex].Tag.ToString();
}
catch
{
BitmapFilenameToDisplay = LayoutPath + "blank.bmp";
}
fill_in_graphics_data(e.RowIndex);
Layout_Bitmap = new Bitmap(BitmapFilenameToDisplay);
dataGridView2[1, e.RowIndex].Value = MergeTwoImages(Layout_Bitmap, Label_Bitmap).GetThumbnailImage(175, 100, myCallback, IntPtr.Zero);
dataGridView2.ClearSelection()
}
catch
{
showmessagebox(strings.label_preview_problem, "100");
//MessageBox.Show("Please try again to choose a Label Preview");
}
}
As we enter this code we have
display_label = new ChooseLabel(dataGridView2[3, e.RowIndex].Tag.ToString()); // open dalog to choose the format
and this opens a dialog that goes to a folder and shows me all the bitmaps in the folder and I choose the dialog and return here. Next you see the code for the dialog that opens.
public ChooseLabel(string choosen_bitmap)
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
MainForm defaultpaths = new MainForm();
LayoutsPath = defaultpaths.txtbx_eggboxlabels_path.Text + "\\";
// string folder_path1 = Application.StartupPath; // get the current path //System.IO.Directory.GetCurrentDirectory();
string file_name = "";
// Format_bitmap_path = folder_path1 + "\\graphics\\" + nozzle + "\\" + format + "\\";
int grid_y = 0, grid_x = 0, index = 0, array_length = 0, num_rows = 1, num_bmp = 1;
DirectoryInfo d = new DirectoryInfo(LayoutsPath); //Path to the images
FileInfo[] Files = d.GetFiles("*.bmp"); //Getting image file names into an array
array_length = Files.Length;
int remainder = (array_length % num_col);
if (remainder != 0)
{
num_rows = (array_length / num_col) + 1; // add extra row as we have more bitmaps but not a full row.
}
else
{
num_rows = (array_length / num_col);
}
//loop round all the cells and make the NULL image blank.
foreach (var column in dataGridView1.Columns)
{
if (column is DataGridViewImageColumn)
(column as DataGridViewImageColumn).DefaultCellStyle.NullValue = null;
}
while (grid_x < num_rows)
{
grid_x = grid_x + 1;
dataGridView1.Rows.Add();
}
grid_x = 0;
while ((grid_y < num_col) & (num_bmp <= array_length))
{
file_name = Files[index].ToString();
Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
Bitmap myBitmap = new Bitmap(#LayoutsPath + file_name);
dataGridView1[grid_y, grid_x].Value = myBitmap.GetThumbnailImage(350, 200, myCallback, IntPtr.Zero); //create a bitmap of the image
dataGridView1[grid_y, grid_x].Tag = file_name.Substring(0, file_name.LastIndexOf('.')); //remove the extension and add to TAG
dataGridView1[grid_y+1, grid_x].Value = file_name.Substring(0, file_name.LastIndexOf('.')); //remove the extension and add to TAG
dataGridView1[grid_y + 1, grid_x].Tag = file_name.Substring(0, file_name.LastIndexOf('.'));
grid_y = grid_y + 1;
index = index + 1;
num_bmp = num_bmp + 1;
if ((num_bmp <= array_length) & (grid_y >= num_col))
{
grid_y = 0;
grid_x = grid_x + 1;
}
}
dataGridView1.AllowUserToAddRows = false; // add this so we do not get the extra row at the bottom
//fill in the data coming from the mainform.
string cell_to_highlight = choosen_bitmap.Substring(0, choosen_bitmap.LastIndexOf('.'));
txtbx_choice.Text = cell_to_highlight;
txtbx_layout_bitmap.Text = LayoutsPath + cell_to_highlight + ".bmp";
//work out which cell to highlight.
for (int i = 0; i < (dataGridView1.Rows.Count); i++)
{
if (dataGridView1.Rows[i].Cells[0].Tag.ToString() == cell_to_highlight)
{
dataGridView1.CurrentCell = dataGridView1.Rows[i].Cells[0];
}
}
}
when I return to main grid cell I have focus on it but clicking the same cell does not seem to fire the event.
I'm trying to get some Data into my Datagridview with a For-Loop
But the thing is, my Loop jumps out if my counter is at 2 for no reason and I dont know why
This is my Code:
var row = new DataGridViewRow();
//image directory
DirectoryInfo dir = new DirectoryInfo(#"C:\Users\Daniel\Pictures");
//getting all files from the Directory
foreach(FileInfo file in dir.GetFiles())
{
try
{
this.img16x16.Images.Add(Image.FromFile(file.FullName));
}catch
{
Console.WriteLine("This is not an image file");
}
}
for (int j = 0; j <= this.img16x16.Images.Count; j++)
{
dataGridView1.Rows[j].Cells[0].Value = img16x16.Images[j].ToString();
img16x16.ImageSize = new Size(16, 16);
dataGridView1.Rows[j].Cells[1].Value = img16x16.Images[j];
dataGridView1.Rows.Add(row);
Thanks for your help
edit: I found the solution, i just had to put these 2:
var row = new DataGridViewRow();
dataGridView1.Rows.Add(row);
in front of my code inside the for-loop
Looks like you've mixed up your row variable and the datagrid's rows.
for(int j = 0; j < this.img16x16.Images.Count; j++)
{
row = new DataGridViewRow();
//Add first cell and its data to the variable "row"
//Size changes
//Add second cell and its data to the variable "row"
//Add "row" to the grid:
dataGridView1.Rows.Add(row);
}
In your version you were writing straight to the grid without any reference to its size, and I'm guessing it was one or two rows large by some other settings you have, and you tried to write outside its bounds.
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:
Automatically set all Cell Widths to that of the data.
Place values of ListView column Headers into first row of Excel.
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. e.g. 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.