I have a problem with opening and saving Excel file which my C# program generated. Every time I made some changes manually and try to save the Excel file, the popup message ask me to save a copy of this file because it is read-only. It is ok but annoying. My excel file is produced by my C# program. Here is my code snippet:
/**
* Save the matched data
* */
public void saveMatch(List<String> saveB, List<String> saveG, Excel.Worksheet bellSheet, Excel.Worksheet GSMSheet, String fileurl, String mCode, String prioName,int sNumber = 0)
{
object misValue = System.Reflection.Missing.Value;
Excel.Application newApp = new Excel.Application();
Excel.Workbook newWB = newApp.Workbooks.Add(misValue);
Excel.Worksheet newWS = newWB.Worksheets.get_Item(1);
String colName1 = bSheet.get_Range("A1").Cells.Value;
String colName2 = GSheet.get_Range("A1").Cells.Value;
int i = 2;//start copy from row two of the Sheet, row one is the column name
newWS.Cells[1, 2] = colName2;//copy the column name
newWS.Cells[1, 1] = colName1;//copy the column name
//Copy excatly matching data
for (int j = 0; j < saveB.Count; j++)
{
newWS.Cells[i, 1] = saveB[j];
newWS.Cells[i, 2] = saveG[j];
//Console.WriteLine(saveG[j] + " : " + saveB[j]);
i++;
}
if (sNumber==0)
{
if (prioName.Equals("None"))
{
newWB.SaveAs(fileurl + #"\MdResults_" +"None_"+ mCode + ".xlsx");
}
else
{
newWB.SaveAs(fileurl + #"\MdResults_" + prioName+"_"+mCode + ".xlsx");
}
}
else
{
if (prioName.Equals("None"))
{
newWB.SaveAs(fileurl + #"\MdResults_" + "None_"+mCode + "_" + sNumber + ".xlsx");
}
else
{
newWB.SaveAs(fileurl + #"\MdResults_" +prioName + "_"+mCode + "_" +sNumber + ".xlsx");
}
}
newWB.Close(0);
newApp.Quit();
}
Program runs ok and I can open the saved Excel file successfully. I just wondering am I missing something in the C# code or I just need to modify something in Excel file itself? I want the excel file which my program generated can be modified and saved as normal without a popup message to ask me to save as a copy. Thanks for the help.
If you don't close newApp, an Excel process will stick around in memory and keep your file locked.
Check your task list to confirm this is happening.
Try adding the following after saving your file
newApp.Close(0);
newApp.Quit();
Related
I have written some code that reads every row in an excel file (for two specific columns) which I will be using later to execute an update SQL Query for each of the rows with a value.
I have displayed these values in a listbox, and I am getting far more nulls than expected when comparing with the stock codes in the excel file.
I have tried changing the formatting of the excel file, but this did not make any difference. There are rows where there definitely are stock codes at that position, but when the program does the cell comparison the program identifies them as nulls when they actually have values.
Does anyone know what the problem is with my code?
private void btnStockCodes_Click(object sender, RoutedEventArgs e)
{
string file = #"\\amn-fs-01\users$\Shanel\Desktop\Stock Codes.xlsx";
Microsoft.Office.Interop.Excel.Application ExcelApp = new Microsoft.Office.Interop.Excel.Application();
Workbook ExcelWorkbook = ExcelApp.Workbooks.Open(file);
Worksheet ews = ExcelApp.ActiveWorkbook.Sheets[1];
Microsoft.Office.Interop.Excel.Range usedRange = ews.UsedRange;
int TotalCounter = 0;
string StockCode = "";
string ReserveID = "";
int nullcounter = 0;
int foundcounter = 0;
foreach (Microsoft.Office.Interop.Excel.Range row in usedRange.Rows)
{
StockCode = "";
ReserveID = "";
TotalCounter = TotalCounter + 1;
if (row.Cells[TotalCounter,7].Value == null)
{
Listbox1.Items.Add(TotalCounter + " null");
nullcounter = nullcounter + 1;
}
else
{
StockCode = row.Cells[TotalCounter,7].Value.ToString();
ReserveID = row.Cells[TotalCounter, 3].Value.ToString();
Listbox1.Items.Add(TotalCounter + " " + StockCode + " " + ReserveID);
foundcounter = foundcounter + 1;
}
}
txtTotal1.Text = foundcounter.ToString() + " Found";
txtTotal2.Text = nullcounter.ToString() + " Null Values";
txtTotal3.Text = TotalCounter.ToString() + " Total Records";
}
I would not trust that Worksheet.UsedRange always works correctly, sometimes it contains more cells than it should, or less. My suggestion is to read all rows in worksheet, while you have any values. Once there are no more values, just stop reading it.
And if you have too many rows, you can read all values at the same time into an array, like here and work with the array.
Thanks for your contributions, I have resolved the error!
It occurs in the row.Cells[TotalCounter,7].Value.ToString()
It should have been row.Cells[7].Value.ToString()
There was no need for me to specify a row index as that's taken care of in the Foreach loop. I will look into alternative ways of writing the code as Worksheet.UsedRange might not work in all cases as Alex suggested.
I'm having a problem creating a txt file on C#. I am trying to create this file in memory (I don't want to create it in a physical path) and then open this file programmatically with the defaul app. The PC must detect the file extension (in this case .txt) and choose the right program to display the file (in this case maybe Notepad, Word, Wordpad...).
I got this now:
using (var writer = new StreamWriter("file.txt"))
{
writer.WriteLine(
grr[0].Keys.ToArray()[0] + "," + grr[0].Keys.ToArray()[1] + "," +
grr[0].Keys.ToArray()[2] + "," + grr[0].Keys.ToArray()[3]);
for (int r = 0; r < row - 1; r++)
{
writer.WriteLine(
grr[r].Values.ToArray()[0] + "," + grr[r].Values.ToArray()[1] + "," +
grr[r].Values.ToArray()[2] + "," + grr[r].Values.ToArray()[3]);
}
}
But I don't know how to open this file.
You want a file system in memory that contains filename and data. So use something like this:
public class MyFolder
{
string folderName { get; set;}
List<MyFolder> childFolders { get; set; }
Dictionary<string, List<byte>> files { get; set; }
}
Ok, the only solution I see is quite a hack:
Write the data on a file;
Open the file with the default app using Process.Start;
Delete the file with File.Delete.
Code:
// test data that I'll write on the file
var text = Enumerable.Range(0, 1000000).Select(x => x.ToString()).ToArray();
// choose the Desktop folder to verify that
// the file is deleted at the end of the method
var tempDir = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
// choose a random file name that will be unique 99.999999999% of the times
var filePath = Path.Combine(tempDir, Guid.NewGuid().ToString() + ".txt");
// write the file. Here I use WriteAllLines, you can use StreamWriter
File.WriteAllLines(filePath, text);
// start the default app for this path
Process.Start(filePath);
// wait to let the default app to open the file;
// otherwise the app will crash, not finding the file anymore
// just in the middle of the read
// (I put 2 sec, but the time must be verified
// depending on your system and the file size)
Thread.Sleep(2000);
// this will totally delete the file
File.Delete(filePath);
If you have Notepad as default app for txt files, that's what you'll see: Notepad opens up with your data, but the file doesn't exist anymore. That's quite what you wanted, isn't it? You won't find the file in the Recycle Bin neither, so you won't have disk space leaks.
The only defect of this trick is: if you click "Save" on your app, it won't ask you the path where you want to save the file. Instead, it will simply re-create the file as it was before deletion, and will save the data directly. That's because it opened a physical file, it didn't created a new one, so it remembers the filePath and will use it to save.
If you don't find more correct/professional solutions, this one can do its job.
ASIDE:
I'd suggest to you a little refactoring.
First step, avoid repetitions:
using (var writer = new StreamWriter("file.txt"))
{
var array = grr[0].Keys.ToArray();
writer.WriteLine(array[0] + "," + array[1] + "," + array[2] + "," + array[3]);
for (int r = 0; r < row - 1; r++)
{
var grrr = grr[r].Values.ToArray();
writer.WriteLine(grrr[0] + "," + grrr[1] + "," + grrr[2] + "," + grrr[3]);
}
}
Second step, use more advanced built-in functions:
using (var writer = new StreamWriter("file.txt"))
{
writer.WriteLine(string.Join(",", grr[0].Keys.ToArray()));
for (int r = 0; r < row - 1; r++)
{
writer.WriteLine(string.Join(",", grr[r].Values.ToArray()));
}
}
I'm attempting to copy excel graphs to excel. It works fine for the first 10, then it doesn't work for the rest. Any suggestions for how to fix it are appreciated.
// create excel spreadsheet and populate
Microsoft.Office.Interop.Excel.Application appExl = new Microsoft.Office.Interop.Excel.ApplicationClass();// .ApplicationClass();
// add tons of data and create a bunch of graphs
// <... c# stuff ...>
appExl.Visible = true; // shows correct excel spreadsheet populated with all my graphs
// open powerpoint from template containing exactly 1 blank slide
Microsoft.Office.Interop.PowerPoint.Application appPpt = new Microsoft.Office.Interop.PowerPoint.Application();
string pPath = #"C:\Program Files (x86)\Encompass\Sdk\Samples\C#\CustomProjects\PowerPointTemplates\blankPowerPoint.potx";
Microsoft.Office.Interop.PowerPoint.Presentation presentation = appPpt.Presentations.Open(pPath, Microsoft.Office.Core.MsoTriState.msoFalse);
Microsoft.Office.Interop.PowerPoint.Slides slides = presentation.Slides;
Microsoft.Office.Interop.Excel.ChartObjects chartObjects = worksheet.ChartObjects(Missing.Value) as Microsoft.Office.Interop.Excel.ChartObjects;
Console.WriteLine("chart objects count: " + chartObjects.Count);
for (int i = 0; i < chartObjects.Count; i++)
{
Console.WriteLine("6." + i.ToString() + ".1");
Microsoft.Office.Interop.PowerPoint.CustomLayout customLayout = presentation.Slides[1].CustomLayout;
Console.WriteLine("6." + i.ToString() + ".2");
Microsoft.Office.Interop.PowerPoint.Slide slide = slides.AddSlide(i + 2, customLayout);
Console.WriteLine("6." + i.ToString() + ".3");
slide.Layout = Microsoft.Office.Interop.PowerPoint.PpSlideLayout.ppLayoutBlank;
Console.WriteLine("6." + i.ToString() + ".4");
Microsoft.Office.Interop.Excel.ChartObject existingChartObject = chartObjects.Item(i + 1) as Microsoft.Office.Interop.Excel.ChartObject;
Console.WriteLine("6." + i.ToString() + ".5"); // <-- last output here on the 11th iteration (starts at 0), thus it errors on the '.Copy()'
existingChartObject.Copy();
Console.WriteLine("6." + i.ToString() + ".6");
Microsoft.Office.Interop.PowerPoint.ShapeRange shapeRange = slide.Shapes.Paste();//Paste it to your Current Slide
Console.WriteLine("6." + i.ToString() + ".7");
// move copied graphs to the upper left corner of the powerpoint slide
shapeRange.Left = 0;
shapeRange.Top = 0;
// reshape copied graphs to size of powerpoint slide
shapeRange.Width = 960;
shapeRange.Height = 540;
Console.WriteLine("6." + i.ToString() + ".8");
}
appPpt.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
I end up with a powerpoint that is populated with the first 10 graphs on slides 2-11, and slide 1 and slide 12 are blank. I would like to have all 123 graphs in the powerpoint on separate slides rather than just the first 10, ideas where I went wrong?
Output:
chart objects count: 123
6.0.1
6.0.2
6.0.3
6.0.4
6.0.5
6.0.6
6.0.7
6.0.8
6.1.1
6.1.2
<...>
6.8.4
6.8.5
6.8.6
6.8.7
6.8.8
6.9.1
6.9.2
6.9.3
6.9.4
6.9.5
6.9.6
6.9.7
6.9.8
6.10.1
A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll
6.10.2
6.10.3
6.10.4
6.10.5
A first chance exception of type 'System.ObjectDisposedException' occurred in System.Windows.Forms.dll
I think these runtime errors indicate that I'm accessing objects that have been deleted? but how could that be if they exist in the excel spreadsheet and I'm accessing from the 'chartObject' that is connected to the excel worksheet?
I have the code below to save data into a excel file(.csv).
private void SavedataToolStripMenuItem_Click(object sender, EventArgs e)
{
now_status.Text = "save data to excel";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{[![enter image description here][1]][1]
selectedFileName = saveFileDialog1.FileName + ".csv";
System.Text.Encoding enc = System.Text.Encoding.GetEncoding("Shift_JIS");
System.IO.StreamWriter sr = new System.IO.StreamWriter(selectedFileName, false, enc);
int rowCountA = int.Parse(objA_n.Text);
int rowCountB = int.Parse(objB_n.Text);
string field;
field = "Saved Data" + "\r\n" + "Numb,Time(S),AX,AY,BX,BY" + "\r\n";
sr.Write(field);
for (int i = 1; i < rowCountA + 1; i++)
{
string fieldA;
fieldA = "A" + dn_objA[i].ToString() + "," + dtime_objA[i].ToString() + "," + dx_objA[i].ToString() + "," + dy_objA[i].ToString() + ",," + "\r\n";
sr.Write(fieldA);
}
for (int i = 1; i < rowCountB + 1; i++)
{
string fieldB;
fieldB = "B" + dn_objB[i].ToString() + "," + dtime_objB[i].ToString() + ",,," + dx_objB[i].ToString() + "," + dy_objB[i].ToString() + "\r\n";
sr.Write(fieldB);
}
sr.Close();
sr.Close();
}
}
This Generates a simple excel file like this below. Object values will be inserted below the second row.
Is there a way to do the same thing, but into a template excel file? I have an excel file like the one below.
I would like the imported data to show on the left hand side of the excel file.
Also, I want to use macros that I made in the second excel file shown.
Hi I am having an excel file containing multiple sheets.
One of the sheets will contain the hyperlink for other sheets.
While using the following code I have successfully added the link but
when I click on the link it gives error **"Refrence not valid"**
Code snippet:
private void AddHyperLink(Workbook xlWorkBook,int nfaultCount)
{
Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
DateTime now = DateTime.Now;
xlWorkSheet.Name = "Summary"; //name of the sheet containing hyperlink
xlWorkSheet.Select(Type.Missing); //code to select the summary sheet
for (int i = 1; i <= nfaultCount; i++)
{
int x = i + 9;
string str = "Fault Code "+i + " of " + nfaultCount;
string strRange1 = "A" + x;
string strRange2 = "A" + x;
string strRange = strRange1 + ":" + strRange2; //location of the link
var Range = xlWorkSheet.get_Range(strRange);
// strRange1 = "!" + strRange1;
string strp="#" + i + " of " +nfaultCount + strRange1;
i +" of " nfaultCount==> name of the target worksheet
Range.Cells.Hyperlinks.Add(Range,strp, Type.Missing, "Fault Code Link", str);
}
xlWorkSheet.Columns["A:B"].AutoFit();
}
I have used the following link for my refrence
http://www.experts-exchange.com/Programming/Languages/.NET/Q_27912390.html
but its giving error Refrence is not valid.
The error message says your hyperlink address is invalid.
Try generating it as:
string strp="#'" + i + " of " +nfaultCount + "'!" + strRange1;
// Sample result: "#'4 of 5'!A3
instead of
string strp="#" + i + " of " +nfaultCount + strRange1;
// Sample result: #4 of 5A3
The above assumes the target worksheet name is "4 of 5".
Note that you need a ! character between the worksheet name and the range name.
You also need to enclose the worksheet name in single quotes if it contains one or more spaces.