Excel Worksheet.Copy method copies some links in chart as external - c#

I use following code to copy one Excel sheet to a different workbook. The source Excel sheet contains chart with series in hidden rows. (theese series are hidden by code in c# before following code)
Worksheet w; //My source worksheet with chart
w.Activate();
w.Name = CreateValidWorksheetName(targetSheetName);
w.get_Range("a1").EntireRow.EntireColumn.Copy();
w.get_Range("a1").EntireRow.EntireColumn.PasteSpecial(XlPasteType.xlPasteValues);
w.Range["A1:A1"].Select();
if (targetWorkbook != null)
{
w.Copy(targetWorkbook.Sheets[1], Type.Missing);
targetWorkbook.RefreshAll();
}
....
targetWorkbook.SaveAs(...
Now the visible series are copied correctly, but hidden series are copied as external links, e.g.:
='C:\X[sourceWorkbook.xlsx]PDK 0-32 kv'!$D$23:$D$100
Now comes the problematic part. When I open the "targetWorkbook" I see the ugly chart including hidden series. But as soon as I open manually in Excel also the "sourceWorkbook", the charts gets automatically fixed and hidden series disapear.
How to achieve this programatically?

I have now lost several hours of my life working around this bug in Worksheet.Copy method. Sometimes I wonder why I was not worse student and I could be doing something more useful now...
First I tried:
//Break links
System.Array links = (System.Array) ((object)targetWorkbook.LinkSources(XlLink.xlExcelLinks));
if (links != null)
{
for (int i = 1; i <= links.Length; i++)
{
try
{
targetWorkbook.UpdateLink((string)links.GetValue(i),
XlLinkType.xlLinkTypeExcelLinks);
targetWorkbook.BreakLink((string)links.GetValue(i),
XlLinkType.xlLinkTypeExcelLinks);
}
catch (Exception ex)
{
Tools.LogException(ex, "targetWorkbook.BreakLink");
}
}
}
No success and I got even HRESULT error. It was not possible to delete external links even using visual interface of Excel, which probably only swallowed this exception.
Finally this did the trick:
w.Activate();
w.Name = CreateValidWorksheetName(targetSheetName);
w.get_Range("a1").EntireRow.EntireColumn.Copy();
w.get_Range("a1").EntireRow.EntireColumn.PasteSpecial(XlPasteType.xlPasteValues);
w.Range["A1:A1"].Select();
if (targetWorkbook != null)
{
//Unhide all rows and then copy!!!
w.get_Range("a1").EntireRow.EntireColumn.Hidden = false;
w.get_Range("a1").EntireColumn.EntireRow.Hidden = false;
w.Copy(targetWorkbook.Sheets[1], Type.Missing);
//Then hide all rows again
HideRows(true, targetWorkbook.Sheets[1].UsedRange, "***HIDETHISROW***");
HideRows(false, targetWorkbook.Sheets[1].UsedRange, "***HIDETHISCOL***");
It is also important to check that the filetypes of the source workbook the target workbook match (both are xlsx), otherwise you may get:
"Excel cannot insert the sheets into the destination workbook, because it contains fewer rows and columns than the source workbook. To move or copy the data to the destination workbook, you can select the data, and then use the Copy and Paste commands to insert it into the sheets of another workbook."
_ExcelApp.DefaultSaveFormat = Microsoft.Office.Interop.Excel.XlFileFormat.xlOpenXMLWorkbook;

Related

Changing color of Excel cell throws "'System.__ComObject' does not contain a definition for 'Interior'"

I am currently facing when I want to change the color of my excel sheet.
With my code I am already inserting entries into my excel file. Some specific cells should receive a special color.
When I run my code, I always get the same error.
Analyzing the Google/Stackoverflow results, I did not find a solution, even though there have been some complaints about this.
Workbooks wbs = excel.Workbooks;
Workbook sheet = wbs.Open(fileName);
excel.DisplayAlerts = false;
Worksheet y = sheet.ActiveSheet;
y.Copy(y, Type.Missing);
int index = y.Index;
int addRow = 2;
Worksheet x = (Worksheet)excel.Worksheets[index];
//...
//this line throws the error
x.Cells[addRow++, 1].Interior.Color = System.Drawing.Color.Blue;
//...
I am using Microsoft Office Interop which was very useful and did its job...until now.
You have to use the XlRgbColor to implement Color (for Excel interop 14.0):
x.Cells[addRow++, 1].Interior.Color = XlRgbColor.xlBlue;
if you have older version : you have to use translator
x.Cells[addRow++, 1].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Silver

Generated Excel with ClosedXML does not use the specified font/size during printing

I am creating an offer generator for my company. The generated offer must contains the General Conditions of Sale in the second worksheet (the offer details are displayed in the first worksheet). The General Conditions of Sale are contained into a word document, so I have to inject them into the worksheet. Last constraint, I have to respect the word's two column layout.
I already have the logic (it can be improved but that's not the point of my question) to extract and insert them into the worksheet. I am using a predefined excel file (with header, footer, column sizes, etc, already defined) and I simply insert the terms in the right merged cells, using ClosedXml.
My issue concerns the printing of that offer. When I open the generated offer, directly click on Print and select print the entire workbook, I can then scrolldown in the preview to check if everything is good. When I arrive to the second worksheet, the font and/or the font size are not the ones I specified in the code.
If I open the second worksheet at least once, and then do the former procedure, everything is back to normal and I can print my offer with the right font and font size. I encounter the same issue when I try to export the offer to pdf.
Here is the preview when I do not open the second worksheet, with wrong font size. And Here is the preview if I open the second worksheet at least one before printing, with good font and font size.
Here is my code (notice in the PrintTerms function the specification of the font and the size):
class Program
{
const int nbRowByPage = 67;
const int nbCharByPage = 3650;
const string generalTermsFileName = "ConditionsGeneralSales_R5.docx";
const string baseTemplateFileName = "TemplateOffer.xlsx";
const string generatedExcelFileName = "Offer.xlsx";
static void Main(string[] args)
{
// Deletes any existing excel file
var fi = new FileInfo(generatedExcelFileName);
if (fi.Exists)
{
fi.Delete();
}
using (var workbookDocument = new XLWorkbook(baseTemplateFileName))
{
// Fills first worksheet
// [..]
// Gets the general terms from the word and injects them into the second worksheet
using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(generalTermsFileName, true))
{
// Extracts the paragraphs
var paragraphs = wordprocessingDocument.MainDocumentPart.Document.Body
.Descendants<Paragraph>()
.Where(p => !String.IsNullOrWhiteSpace(p.InnerText))
.ToList();
// Gets the second worksheet
var conditionsWS = workbookDocument.Worksheet(2);
// Injects the paragraphs in the worksheets
PrintTerms(conditionsWS, paragraphs);
}
// Saves the excel
workbookDocument.SaveAs(generatedExcelFileName, true);
}
// Opens the generated excel with MS Excel
Process.Start(generatedExcelFileName);
}
/// <summary>
/// Adds the specified paragraph into the specified worksheet according this methodology:
/// - Splits the paragraphs into two columns
/// - Fills the excel columns alternatively in order to looks like a two columns word text
/// - Manage the break page
/// </summary>
/// <param name="ws"></param>
/// <param name="generalTerms"></param>
static void PrintTerms(IXLWorksheet ws, List<Paragraph> generalTerms)
{
var currentParagraphIndex = 0;
var index = 1;
while (currentParagraphIndex < generalTerms.Count)
{
var nbChar = 0;
var currentParagraphs = generalTerms.Skip(currentParagraphIndex).TakeWhile(p => (nbChar += p.InnerText.Length) < nbCharByPage).ToList();
var row = ((int)Math.Ceiling(index / 2d) - 1) * nbRowByPage + 1;
var column = index % 2 == 0 ? 3 : 1;
var currentCell = ws.Cell(row, column);
currentCell.Style.Alignment.SetHorizontal(XLAlignmentHorizontalValues.Justify);
currentCell.Style.Alignment.SetVertical(XLAlignmentVerticalValues.Top);
currentCell.Style.Font.SetFontName("Arial");
currentCell.Style.Font.SetFontSize(8);
foreach (var paragraph in currentParagraphs)
{
foreach (var run in paragraph.Descendants<Run>())
{
var text = currentCell.RichText.AddText(run.InnerText);
if (run.RunProperties.Bold != null)
{
text.SetBold();
}
if (run.RunProperties.Italic != null)
{
text.SetItalic();
}
if (run.RunProperties.Underline != null)
{
text.SetUnderline();
}
}
// Break line
currentCell.RichText.AddNewLine();
currentCell.RichText.AddNewLine();
}
ws.Range(row, column, row + nbRowByPage - 1, column).Merge();
index++;
currentParagraphIndex += currentParagraphs.Count();
}
}
}
To run this code, you will need to put at the root of the console application the two following files: terms and template (check copy if newer in the files properties).
I also tried a lot of combination like:
Setting the workbook global style:
workbookDocument.Style
.Font.SetFontName("Arial")
.Font.SetFontSize(8);
Setting the worksheet style:
offerWS.Style
.Font.SetFontName("Arial")
.Font.SetFontSize(8);
Setting the cell's RichText style:
generalTermsCell.RichText
.SetFontName("Arial")
.SetFontSize(8);
All of this, at the begining or at the ending of the file modification. Nothing works.
I don't think it is a bug into ClosedXML. Excel does not ask me for saving the changes when leaving the app if I had opened the second worksheet. So the generated files do not seem corrupted. It looks like an Excel bug, but may be there is a way in ClosedXML to workaround that?

Paste Values in Excel C#

Hello I have been trying to use the PasteSpecial paste values on another sheet but every thing I've tried I keep receiving the same error.
PasteSpecial method of Range class failed
xl.Application xlApp = Globals.ThisWorkbook.Application;
xl.Workbook wb = xlApp.ActiveWorkbook;
xl.Worksheet data = wb.Sheets["Data"];
xl.Worksheet datasheet = wb.Sheets["DataSheet"];
xl.Worksheet pivot = wb.Sheets["Pivot"];
xl.Range dataRng = data.Range["A1:C" +
xlApp.WorksheetFunction.CountA(data.Range["A:A"])];
xl.Range pivotRng = pivot.Range["A1"];
addPivot ap = new addPivot();
ap.DataRange = dataRng;
ap.DestRng = pivotRng;
ap.PivotSheet = pivot;
ap.createPivot("FullName","Date");
pivot.PivotTables("PivotTable1").NullString = "0";
pivot.UsedRange.Copy();
datasheet.Range["A2:GT999"].ClearContents();
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone,
System.Type.Missing, System.Type.Missing);
datasheet.Range["2:2"].ClearContents();
I have Tried
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone)
and what you see above.
thanks!
Do the ClearContents before the Copy. Excel is removing your copied range (same as hitting ESC in the program) when you clear those cells out. Then you are trying to PasteSpecial but nothing is in the clipboard any longer.
//...code above
datasheet.Range["A2:GT999"].ClearContents();
pivot.UsedRange.Copy();
datasheet.Range["A2"].PasteSpecial(xl.XlPasteType.xlPasteValues,
xl.XlPasteSpecialOperation.xlPasteSpecialOperationNone,
System.Type.Missing, System.Type.Missing);
//code below...
If you are debugging this step-by-step, you would see the dashed border around the copied range disappear after the ClearContents call. Excel is a bit odd (but predictable) about when the copied range is forgotten. Editing cells anywhere in the Workbook is one sure way to make it forget.

Excel Interop, iterating workbook for worksheet and chart

I have a scenario while working with Microsoft Excel Interop.
System.Collections.IEnumerator wsEnumerator = excelApp.ActiveWorkbook.Worksheets.GetEnumerator();
while (wsEnumerator.MoveNext())
{
wsCurrent = (Excel.Worksheet)wsEnumerator.Current;
//Worksheet operation Follows
}
I am operating on Worksheets, so i can not have Chart in this. What i want to do achieve is operate on Sheets and check if it is a Worksheet or Chart, and act accordingly.
As sheets contain both Worksheet, Chart and "Excel 4.0 Macro", so what is the type of Sheets each entry as it can hold any of the type mentioned.
System.Collections.IEnumerator wsEnumerator = workBookIn.Sheets.GetEnumerator();
while (wsEnumerator.MoveNext())
{
//Identify if its a chart or worksheet
}
Solved it by checking type of Current Enumerator
var item = wsEnumerator.Current;
if (item is Excel.Chart)
{
//Do chart operations
}
else if (item is Excel.Worksheet)
{
//Do sheetoperations
}
You can re-write the entire code (without enumerator):
foreach (dynamic sheet in workBookIn.Sheets)
{
if (sheet is Chart)
// Write your code
else if (sheet is Worksheet)
// Write your code
}

Unable to cast transparent proxy to type 'Microsoft.Office.Interop.Excel.Worksheet'

Iterating over all sheets in a Spreadsheet I ran into this error message:
Unable to cast transparent proxy to type 'Microsoft.Office.Interop.Excel.Worksheet'.
This spreadsheet has some chart tabs that obviously cant be cast to a sheet. So when I run this code I get the above error:
foreach (Microsoft.Office.Interop.Excel.Worksheet activeSheet in xlApp.Sheets)
{
}
I've looked around to find a solution and a few people have encountered it - in different scenario's to me eg:
http://social.msdn.microsoft.com/forums/en-US/vsto/thread/ab2e917a-d3bf-4e6a-84dc-7f8a9440fe0a
I am not happy with the workaround I have so far. I feel that exceptions should be used exceptionally and this isn't one of those places:
for (int i = 0; i < xlApp.Sheets.Count; i++)
{
try
{
Worksheet wk = (Worksheet)xlApp.Sheets[i];
}
catch (Exception)
{
//This isn't a sheet - avoid sheet operations
}
}
I'm wondering if anyone knows how to iterate through the Sheets (skipping non-sheets) without the need for a Try-Catch? Is there some method or property in the Excel object model that can tell you if a sheet is not really a sheet?
What about:
foreach (object possibleSheet in xlApp.Sheets)
{
Microsoft.Office.Interop.Excel.Worksheet aSheet = possibleSheet as Microsoft.Office.Interop.Excel.Worksheet;
if (aSheet == null)
continue;
// your stuff here
}
Of course it would work only if you get the exception only for the chart tabs and not for the other sheets.

Categories