OK so I have this function that works fine at inserting rows and then putting data within those rows.
public void inputRowData(string[] data, int rds)
{
int bestRow = getRowByRDS(rds);
string val = getValueOfCell(bestRow, 6);
if (val == null || val.Equals(""))
{
shiftRows(bestRow, data.Length-1);
string[] formatedData = formatOutput(bestRow, data);
// transform formated data into string[,]
string[][] splitedData = formatedData.Select(s => s.Split('\t')).ToArray();
var colCount = splitedData.Max(r => r.Length);
var excelData = new string[splitedData.Length, colCount];
for (int i = 0; i < splitedData.Length; i++)
{
for (int j = 0; j < splitedData[i].Length; j++)
{
excelData[i, j] = splitedData[i][j];
}
}
oSheet.get_Range("A" + bestRow.ToString()).Resize[splitedData.Length, colCount].Value = excelData;
}
else
{
Console.WriteLine("Line has some information already, skipping 1 more");
shiftRows(bestRow, data.Length + 1);
}
}
Now I if you take a look i find the "bestRow" which is determined by the last row in the excel with a int value at a particular column the code is show here:
private int getRowByRDS(int id)
{
int bestfit = -1;
Boolean foundOne = false;
Microsoft.Office.Interop.Excel.Range usedRange = oSheet.UsedRange;
for (int i = 2; i < usedRange.Rows.Count; i++)
{
string val = getValueOfCell(i, 3);
if (val == null)
continue;
int rds = int.Parse(val);
Console.WriteLine(val + " " +i);
if (rds == id)
{
bestfit = i;
foundOne = true;
}
else
if (foundOne)
return bestfit;
}
return bestfit;
}
What happens is that when it finishes on set of data in the inputRowData method it will move to another set with a different rds value. It will try to call getRowByRDS but it will throw an error.
On debug it looks like it didnt update the sheet... so lets say the first set of 10 strings was inserted at row 900, the and if the next set was suppose to start right after it bestRow will return 901 not 911.
The error is
A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in TTE Tool.exe
I found the problem. I disposed of the excel beforehand by mistake.
Related
I have a datagridview with several columns and different report types, where I have a method to export the records to Excel spreadsheet, in this datagridview I leave some columns like: visible = false according to the selected report type.
In the export method for spreadsheet I have a validation to consider only visible cells, true but it is not working.
int XLRow = 1;
int XLCol = 1;
// Export header
for (int i = 0; i < datagrid.Columns.Count; i++)
{
if (datagrid.Columns[i].Visible == true)
xlWorkSheet.Cells[XLRow, XLCol++] = datagrid.Columns[i].HeaderText;
}
XLRow = 2;
XLCol = 1;
// Controls for scrolling through records do DataGridView
for (int i = 0; i < datagrid.RowCount; i++)
{
for (int j = 0; j < datagrid.ColumnCount; j++)
{
DataGridViewCell cell = datagrid[j, i];
string conteudo = string.Empty;
if ((cell.Value != null) && (!string.IsNullOrEmpty(cell.Value.ToString())) && cell.Visible == true)
{
conteudo = cell.Value.ToString();
if ((Funcoes.EhNumerico(conteudo)) && (conteudo.Length > 8))
{
conteudo = string.Concat("'", conteudo);
}
xlWorkSheet.Cells[XLRow, XLCol++] = conteudo;
}
XLRow++;
XLCol = 1;
}
}
The spreadsheet leaves with the columns that are visible = false in white, as follows:
How can I resolve this?
In other words… in the first for loop… if the column is not visible… then you DO NOT want to increment i… and this is obviously going to mess up the for loop. Hence my suggestion to create two (2) int variables… int XLRow and int XLColumn, then use those indexes specifically for the WORKSHEET. Then loop through the grid columns as your code does using i and j, however, when a column is found that is not visible, then you DO NOT want to increment the XLCol index.
It will be a challenging juggling the loops i or j variables to also be used as an index into the worksheet columns as they may be completely “different” indexes. This is why I say “separate” them from the git go and keep it simple. Something like…
EDIT per OP comment…
I had the code that “increments” the XLCol “INSIDE” the if statement that checks for a null cell value or an empty string cell value… meaning if the cell is null or empty… then XLCol does not get incremented. The line xlWorkSheet.Cells[XLRow, XLCol++] = conteudo; should be “outside” that if statement. I have edited the code and it should work properly now with “empty” cells.
int XLRow = 1;
int XLCol = 1;
for (int i = 0; i < datagrid.Columns.Count; i++) {
if (datagrid.Columns[i].Visible == true)
xlWorkSheet.Cells[XLRow, XLCol++] = datagrid.Columns[i].HeaderText;
}
XLRow = 2;
XLCol = 1;
for (int i = 0; i < datagrid.RowCount; i++) {
for (int j = 0; j < datagrid.ColumnCount; j++) {
if (datagrid.Columns[j].Visible) {
DataGridViewCell cell = datagrid[j, i];
string conteudo = string.Empty;
if ((cell.Value != null) && (!string.IsNullOrEmpty(cell.Value.ToString()))) {
conteudo = cell.Value.ToString();
if ((Funcoes.EhNumerico(conteudo)) && (conteudo.Length > 8)) {
conteudo = string.Concat("'", conteudo);
}
//xlWorkSheet.Cells[XLRow, XLCol++] = conteudo; <- XLCol incremented ONLY if cell is not null or empty and this is wrong
}
xlWorkSheet.Cells[XLRow, XLCol++] = conteudo;
}
}
XLRow++;
XLCol = 1;
}
I am trying to save some information from textbox controls that have been dynamically created and may have been deleted since their creation. I can provide an array of strings that match a number already being used to save that data to match my list of deleted against. essentially to prevent exceptions from being thrown I need to not try and save data from the deleted textboxes. Here is what I am working with atm:
int TextboxNumber = 0;
for (int pc = 0; pc < Int32.Parse(PrinterCount.Text); pc++)
{
for (int r = 0; r <= 2; r++)
{
TextboxNumber = pc * 3 + r;
if (TextboxNumber != Int32.Parse(deleted[]))
{
TextboxName = "textBox" + TextboxNumber.ToString();
printerinfo = printerinfo + this.Controls[TextboxName].Text + ",";
}
}
using (StreamWriter w = File.AppendText(path))
{
w.WriteLine(printerinfo);
};
printerinfo = "";
}
You can see on Line 6 is where I have this if statement. The deleted[] array can be any series of three numbers such as 0,1,3 or 7,8,9 , or both or more... How would one validate in this situation?
This is the answer for any who come after; on line 6 put
if (checkdeleted(TextboxNumber))
then create this function
bool checkdeleted(int number)
{
string[] deleted = Deleted.Text.Split(',');
if (deleted.Count() != 1)
{
for (int c = deleted.Count() - 2; c >= 0; c--)
{
if (Int32.Parse(deleted[c]) == number)
{
return false;
}
}
}
return true;
}
Thank you very much me!
The below IndexOutOfRangeException is not letting my code run (it compiles). While I understand this kind of exception (array indexes etc) the issue is, what I am trying to do is simply update the String subsection2 with the value in cell B[excelrow]. For some reason, there is an index out of bounds exception which to me does not make sense. Neither subsection2 or excelrow is part of an array. The only array I can think of is the excel array, but excelrow is an integer with value of 3, it should updated to row B3, and so on. (I've even tried updating with B3 directly and I get the same error).
To help you out further with context, this method called createsource takes as input the excel spreadsheet and the total rows in that sheet. It does the below code to output a 2D array containing in the first dimension the excel index of each new order (each different customer), and the 2nd dimension is the number of items ordered per customer.
The method for the code is below:
private int[,] createsource(Microsoft.Office.Interop.Excel.Worksheet xlWorksheet, int totalRows)
{
String subsection = "";
object subsection2 = "";
int orders = 0;
//figures out how many different pages there are going to be
for (int n = 3; n < totalRows + 1; n++)
{
if (!(xlWorksheet.get_Range("B" + n.ToString()).Text == subsection))
{
subsection = xlWorksheet.get_Range("B" + n.ToString()).Text;
orders++;
}
}
MessageBox.Show(orders.ToString());
int[,] source = new int[orders, 2];
int excelrow = 3;
subsection2 = xlWorksheet.get_Range("B" + excelrow.ToString()).Text;
int i;
for (i = 0; i < orders + 1; i++)
{
int j = 1;
if (excelrow == totalRows + 1)
{
break;
}
//Out of bounds exception is found in the below if statement updating subsection2:
if (!(xlWorksheet.get_Range("B" + excelrow.ToString()).Text == subsection2))
{
source[i, 0] = excelrow;
//MessageBox.Show(xlWorksheet.get_Range("B" + excelrow.ToString()).Text.ToString());
subsection2 = xlWorksheet.get_Range("B" + excelrow.ToString()).Text;
excelrow++;
}
for (int iter = 0; iter < 1;)
{
if (excelrow == totalRows + 1)
{
break;
}
if (xlWorksheet.get_Range("B" + excelrow.ToString()).Text == subsection2)
{
excelrow++;
j++;
}
if (!(xlWorksheet.get_Range("C" + excelrow.ToString()).Text == subsection2))
{
subsection2 = xlWorksheet.get_Range("C" + excelrow.ToString()).Text;
iter = 1;
}
}
source[i, 1] = j;
}
MessageBox.Show(source[2, 0].ToString());
return source;
}
I see the problem. You're declaring source as:
int[,] source = new int[orders, 2];
... okay, but look at your loop:
for (i = 0; i < orders + 1; i++)
... which later feeds into:
source[i, 0] = excelrow;
Okay, so if orders = 100, you've declared a 100 long array, going from 0-99. Then your loop, you go from 0 to "less than 100+1", aka 0-100. When you get to the last loop, you're using a value of i=100, and trying to put it into the array spot that doesn't exist.
You need to either decrease your loop by one, or increase your array size by 1.
I am able to hide my text inside an Image. But when I tried to extract the text from my image, I always only able manage to get only the first character. I don't know where went wrong?
My embed operation code:
public static Bitmap embedMessage(string hiddenText, Bitmap oriImage)
{
Color currentPixel;
int[] colorRGB = new int[3];
List<int> messageValue = new List<int>();
messageValue = ConvertMessage(messageValue, hiddenText);
int messageIndex = messageValue.Count;
int binaryCount = 0;
int zeroAdded = 0;
for(int row = 0; row < oriImage.Height; row++)
{
for(int col = 0; col < oriImage.Width; col++)
{
currentPixel = oriImage.GetPixel(col, row);
colorRGB[0] = ConvertEven(currentPixel.R);
colorRGB[1] = ConvertEven(currentPixel.G);
colorRGB[2] = ConvertEven(currentPixel.B);
for(int rgbIndex = 0; rgbIndex < colorRGB.Length; rgbIndex++)
{
if(messageIndex > 0)
{
colorRGB[rgbIndex] += messageValue[messageValue.Count - messageIndex] % 2;
messageValue[messageValue.Count - messageIndex] /= 2;
}
}
if (messageIndex == 0 && zeroAdded < 8)
{
oriImage.SetPixel(col, row, Color.FromArgb(colorRGB[0], colorRGB[1], colorRGB[2]));
zeroAdded++;
Console.WriteLine("Adding zero number: " + zeroAdded);
}
else
{
Console.WriteLine("Set Pixel");
oriImage.SetPixel(col, row, Color.FromArgb(colorRGB[0], colorRGB[1], colorRGB[2]));
}
if (zeroAdded == 8)
{
Console.WriteLine("Final Pixel Add");
oriImage.SetPixel(col, row, Color.FromArgb(colorRGB[0], colorRGB[1], colorRGB[2]));
return oriImage;
}
binaryCount++;
if (binaryCount % 8 == 0) { messageIndex--; Console.WriteLine("Message Index deducted"); }
}
}
return oriImage;
}
My embed implementation is the same to this example As for extraction I used the exact extraction code from the example. No matter what I tried, I am still only getting the first character of my embedded text. I tried checking my code by printing each operations and all of them fires without any issue which means the embed operation should be working as expected.
Just found my problem. Everything in my embed method should be in the R G B for loop.
I'm trying to use a Script Task to export data to Excel because some of the reports I generate simply have too many columns to keep using a template file.
The most annoying part about using a template is: if something as simple as a column header changes, the metadata gets screwed forcing me to recreate my DataFlow. Because I use an OLE DB source, I need to use a Data Transformation task to convert between unicode and non-unicode character sets, then remap my Excel Destination to the "Copy of field x" in order for the Excel document to create properly.
This takes far too long and I need a new approach.
I have the following method in a script task using Excel = Microsoft.Office.Interop.Excel:
private void ExportToExcel(DataTable dataTable, string excelFilePath = null)
{
Excel.Application excelApp = new Excel.Application();
Excel.Worksheet workSheet = null;
try
{
if (dataTable == null || dataTable.Columns.Count == 0)
throw new System.Exception("Null or empty input table!" + Environment.NewLine);
excelApp.Workbooks.Add();
workSheet = excelApp.ActiveSheet;
for (int i = 0; i < dataTable.Columns.Count; i++)
{
workSheet.Cells[1, (i + 1)] = dataTable.Columns[i].ColumnName;
}
foreach (DataTable dt in dataSet.Tables)
{
// Copy the DataTable to an object array
object[,] rawData = new object[dt.Rows.Count + 1, dt.Columns.Count];
// Copy the column names to the first row of the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
rawData[0, col] = dt.Columns[col].ColumnName;
}
// Copy the values to the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
for (int row = 0; row < dt.Rows.Count; row++)
{
rawData[row + 1, col] = dt.Rows[row].ItemArray[col];
}
}
// Calculate the final column letter
string finalColLetter = string.Empty;
string colCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int colCharsetLen = colCharset.Length;
if (dt.Columns.Count > colCharsetLen)
{
finalColLetter = colCharset.Substring((dt.Columns.Count - 1) / colCharsetLen - 1, 1);
}
finalColLetter += colCharset.Substring((dt.Columns.Count - 1) % colCharsetLen, 1);
workSheet.Name = dt.TableName;
// Fast data export to Excel
string excelRange = string.Format("A1:{0}{1}", finalColLetter, dt.Rows.Count + 1);
//The code crashes here (ONLY in SSIS):
workSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
// Mark the first row as BOLD
((Excel.Range)workSheet.Rows[1, Type.Missing]).Font.Bold = true;
}
List<int> lstColumnsToSum = new List<int>() { 9 };
Dictionary<int, string> dictColSumName = new Dictionary<int, string>() { { 9, "" } };
Dictionary<int, decimal> dictColumnSummation = new Dictionary<int, decimal>() { { 9, 0 } };
// rows
for (int i = 0; i < dataTable.Rows.Count; i++)
{
for (int j = 1; j <= dataTable.Columns.Count; j++)
{
workSheet.Cells[(i + 2), (j)] = dataTable.Rows[i][j - 1];
if (lstColumnsToSum.Exists(x => (x == j)))
{
decimal val = 0;
if (decimal.TryParse(dataTable.Rows[i][j - 1].ToString(), out val))
{
dictColumnSummation[j] += val;
}
}
}
}
//Footer
int footerRowIdx = 2 + dataTable.Rows.Count;
foreach (var summablecolumn in dictColSumName)
{
workSheet.Cells[footerRowIdx, summablecolumn.Key] = String.Format("{0}", dictColumnSummation[summablecolumn.Key]);
}
// check fielpath
if (excelFilePath != null && excelFilePath != "")
{
try
{
if (File.Exists(excelFilePath))
File.Delete(excelFilePath);
workSheet.Activate();
workSheet.Application.ActiveWindow.SplitRow = 1;
workSheet.Application.ActiveWindow.FreezePanes = true;
int row = 1;
int column = 1;
foreach (var item in dataTable.Columns)
{
Excel.Range range = workSheet.Cells[row, column] as Excel.Range;
range.NumberFormat = "#";
range.EntireColumn.AutoFit();
range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
column++;
}
Excel.Range InternalCalculatedAmount = workSheet.Cells[1, 9] as Excel.Range;
InternalCalculatedAmount.EntireColumn.NumberFormat = "#0.00";
InternalCalculatedAmount.Columns.AutoFit();
workSheet.SaveAs(excelFilePath);
}
catch (System.Exception ex)
{
throw new System.Exception("Excel file could not be saved! Check filepath." + Environment.NewLine + ex.Message);
}
}
else // no filepath is given
{
excelApp.Visible = true;
}
}
catch (System.Exception ex)
{
throw new System.Exception("ex.Message + Environment.NewLine, ex.InnerException);
}
}
The exception thrown is a System.OutOfMemoryException when trying to execute the following piece of code:
workSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
My biggest frustration is that this method works 100% in a regular C# application.
The DataTable contains about 435000 rows. I know it's quite a bit of data but I use this very method, modified of course, to split data across multiple Excel worksheets in one of my other applications, and that DataSet contains about 1.1m rows. So less than half of my largest DataSet should be a walk-in-the-park...
Any light shed on this matter would be amazing!