#region // CN_createXL
///
/// 1- Creates an excel file from an xltm or xlsm and save it as an xlsm file with an incremented filename if needed.
/// 2- It opens a specified sheet or creates it if it does not exists, then enter the data provided as rows and columns.
/// 3- Data will always overwrite the content of the file.
/// 4- It uses openXML only. so need to reference these files using nugetpackages:
/// DocumentFormat.OpenXml
///
/// 5- Also you need these reference:
// using DocumentFormat.OpenXml;
// using DocumentFormat.OpenXml.Packaging;
// using DocumentFormat.OpenXml.Spreadsheet;
// using System;
// using System.Collections.Generic;
// using System.Data;
// using System.IO;
// using System.Linq;
// using System.Text.RegularExpressions;
// using System.Windows.Forms;
///
///
//namespace NS_createXL
//{
public class CN_createXL
{
// Output Port names and their order as you wish them to appear in the Dynamo node.
[Autodesk.DesignScript.Runtime.MultiReturn(new[] { "filePath", "sheetName", "startRow", "startCol", "rc_data" })]
public static Dictionary createXL(string filePath, string sheetName, int startRow, int startCol, List> rc_data)
{
#region // Check if FilePath Exists if not open filedialog foe user to choose a template file
if (File.Exists(filePath))
{
//file exist
}
else
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = "C:\\";
openFileDialog.Filter = "Excel files (*.xltm)|*.xltm|All files (*.*)|*.*";
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
//Get the path of specified file
filePath = openFileDialog.FileName;
}
}
}
#endregion // Check if FilePath Exists if not open filedialog foe user to choose a template file
// Load the file into a byteArry to transfer to MemoryStream Later
byte[] xltmDoc_byteArray = File.ReadAllBytes(filePath);
using (MemoryStream xltmDoc_mem = new MemoryStream())
{
xltmDoc_mem.Write(xltmDoc_byteArray, 0, (int)xltmDoc_byteArray.Length);
using (SpreadsheetDocument xltmDoc = SpreadsheetDocument.Open(xltmDoc_mem, true))
{
// Modify the document as necessary.
// For this example, we insert a new paragraph at the
// beginning of the document.
//wordDoc.MainDocumentPart.Document.Body.InsertAt( new Paragraph(new Run(new Text("Newly inserted paragraph."))), 0);
xltmDoc.ChangeDocumentType(DocumentFormat.OpenXml.SpreadsheetDocumentType.MacroEnabledWorkbook);
#region // Writing to Excel
WriteExcelFile(xltmDoc, sheetName, startRow, startCol, rc_data);
#endregion // Writing to Excel
#region // Set filepath to unique value
filePath = filePath.Replace("xltm", "xlsm");
// Check if filePath does not exist and increment name if it does.
filePath = AppendFileNumberIfExists(filePath, ".xlsm");
#endregion // Set filepath to unique value
} // END using SpreadSheet
#region // Change File Type and Save (XLTM to XLSM)
// At this point, the memory stream contains the modified document.
// We could write it back to a SharePoint document library or serve
// it from a web server.
// In this example, we serialize back to the file system to verify
// that the code worked properly.
using (FileStream fileStream = new FileStream(filePath, System.IO.FileMode.CreateNew))
{
xltmDoc_mem.WriteTo(fileStream);
} // END using FileStream
#endregion // Change File Type and Save (XLTM to XLSM)
} // END using MemoryStream
return new Dictionary {
{ "filePath", filePath},
{ "sheetName", sheetName},
{ "startRow", startRow},
{ "startCol", startCol },
{ "rc_data", rc_data }
};
} // END of createXL
#region // write to excel
private static void WriteExcelFile(SpreadsheetDocument xltmDoc, string sheetName, int startRow, int startCol, List> rc_data)
{
using (xltmDoc)
{
// Get the SharedStringTablePart. If it does not exist, create a new one.
SharedStringTablePart shareStringPart;
if (xltmDoc.WorkbookPart.GetPartsOfType().Count() > 0)
{
shareStringPart = xltmDoc.WorkbookPart.GetPartsOfType().First();
}
else
{
shareStringPart = xltmDoc.WorkbookPart.AddNewPart();
}
#region // Find desired worksheet by name or create one if it does not exist
WorksheetPart worksheetPart = GetWorksheetPartByName(xltmDoc, sheetName);
if (worksheetPart == null)
{
// Insert a new worksheet.
worksheetPart = InsertWorksheet(xltmDoc.WorkbookPart, sheetName);
}
#endregion // Find desired worksheet by name or create one if it does not exist
int index = 0;
Cell cell = null;
#region Loop thru list of Lists
for (int r = 0; r < rc_data.Count; r++)
{
for (int c = 0; c < rc_data[r].Count; c++)
{
#region Insert Values in Cells
// Insert the text into the SharedStringTablePart.
index = CN_createXL.InsertSharedStringItem(rc_data[r][c].ToString(), shareStringPart);
// Insert a cell into the worksheet.
cell = InsertCellInWorksheet(GetExcelColumnName(c + startCol), Convert.ToUInt32(r + startRow), worksheetPart);
// Set the value of cell.
cell.CellValue = new CellValue(index.ToString());
cell.DataType = new EnumValue(CellValues.SharedString);
#endregion Insert Values in Cells
}
}
#endregion Loop thru list of Lists
// Save the new worksheet.
worksheetPart.Worksheet.Save();
} // END using xltmDoc
}
#endregion // write to excel
// Given text and a SharedStringTablePart, creates a SharedStringItem with the specified text
// and inserts it into the SharedStringTablePart. If the item already exists, returns its index.
private static int InsertSharedStringItem(string text, SharedStringTablePart shareStringPart)
{
// If the part does not contain a SharedStringTable, create one.
if (shareStringPart.SharedStringTable == null)
{
shareStringPart.SharedStringTable = new SharedStringTable();
}
int i = 0;
// Iterate through all the items in the SharedStringTable. If the text already exists, return its index.
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements())
{
if (item.InnerText == text)
{
return i;
}
i++;
}
// The text does not exist in the part. Create the SharedStringItem and return its index.
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(text)));
shareStringPart.SharedStringTable.Save();
return i;
}
// Given a WorkbookPart, inserts a new worksheet.
private static WorksheetPart InsertWorksheet(WorkbookPart workbookPart, string sheetName)
{
// Add a new worksheet part to the workbook.
WorksheetPart newWorksheetPart = workbookPart.AddNewPart();
newWorksheetPart.Worksheet = new Worksheet(new SheetData());
newWorksheetPart.Worksheet.Save();
Sheets sheets = workbookPart.Workbook.GetFirstChild();
string relationshipId = workbookPart.GetIdOfPart(newWorksheetPart);
// Get a unique ID for the new sheet.
uint sheetId = 1;
if (sheets.Elements().Count() > 0)
{
sheetId = sheets.Elements().Select(s => s.SheetId.Value).Max() + 1;
}
// Append the new worksheet and associate it with the workbook.
Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = sheetName };
sheets.Append(sheet);
workbookPart.Workbook.Save();
return newWorksheetPart;
}
// Given a column name, a row index, and a WorksheetPart, inserts a cell into the worksheet.
// If the cell already exists, returns it.
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart)
{
Worksheet worksheet = worksheetPart.Worksheet;
SheetData sheetData = worksheet.GetFirstChild();
string cellReference = columnName + rowIndex;
// If the worksheet does not contain a row with the specified row index, insert one.
Row row;
if (sheetData.Elements().Where(r => r.RowIndex == rowIndex).Count() != 0)
{
row = sheetData.Elements().Where(r => r.RowIndex == rowIndex).First();
}
else
{
row = new Row() { RowIndex = rowIndex };
sheetData.Append(row);
}
// If there is not a cell with the specified column name, insert one.
if (row.Elements().Where(c => c.CellReference.Value == columnName + rowIndex).Count() > 0)
{
return row.Elements().Where(c => c.CellReference.Value == cellReference).First();
}
else
{
// Cells must be in sequential order according to CellReference. Determine where to insert the new cell.
Cell refCell = null;
foreach (Cell cell in row.Elements())
{
if (string.Compare(cell.CellReference.Value, cellReference, true) > 0)
{
refCell = cell;
break;
}
}
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, refCell);
worksheet.Save();
return newCell;
}
}
private static WorksheetPart GetWorksheetPartByName(SpreadsheetDocument xltmDoc, string sheetName)
{
IEnumerable sheets =
xltmDoc.WorkbookPart.Workbook.GetFirstChild().
Elements().Where(s => s.Name == sheetName);
if (sheets?.Count() == 0)
{
// The specified worksheet does not exist.
return null;
}
string relationshipId = sheets?.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)xltmDoc.WorkbookPart.GetPartById(relationshipId);
return worksheetPart;
}
public static string GetExcelColumnName(int columnNumber)
{
int dividend = columnNumber;
string columnName = String.Empty;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (int)((dividend - modulo) / 26);
}
return columnName;
}
//
/// A function to add an incremented number at the end of a file name if a file already exists.
///
/// The file. This should be the complete path.
/// This can be empty.
/// An incremented file name.
private static string AppendFileNumberIfExists(string file, string ext)
{
// This had a VB tidbit that helped to get this started.
// http://www.codeproject.com/Questions/212217/increment-filename-if-file-exists-using-csharp
// If the file exists, then do stuff. Otherwise, we just return the original file name.
if (File.Exists(file))
{
string folderPath = Path.GetDirectoryName(file); // The path to the file. No sense in dealing with this unecessarily.
string fileName = Path.GetFileNameWithoutExtension(file); // The file name with no extension.
string extension = string.Empty; // The file extension.
// This lets us pass in an empty string for the file extension if required. i.e. It just makes this function a bit more versatile.
if (ext == string.Empty)
{
extension = Path.GetExtension(file);
}
else
{
extension = ext;
}
// at this point, find out if the fileName ends in a number, then get that number.
int fileNumber = 0; // This stores the number as a number for us.
// need a regex here - \(([0-9]+)\)$
Regex r = new Regex(@"\(([0-9]+)\)$"); // This matches the pattern we are using, i.e. ~(#).ext
Match m = r.Match(fileName); // We pass in the file name with no extension.
string addSpace = " "; // We'll add a space when we don't have our pattern in order to pad the pattern.
if (m.Success)
{
addSpace = string.Empty; // We have the pattern, so we don't add a space - it has already been added.
string s = m.Groups[1].Captures[0].Value; // This is the single capture that we are looking for. Stored as a string.
// set fileNumber to the new number.
fileNumber = int.Parse(s); // Convert the number to an int.
// remove the numbering from the string as we're constructing it again below.
fileName = fileName.Replace("(" + s + ")", "");
}
// Start looping.
do
{
fileNumber += 1; // Increment the file number that we have above.
file = Path.Combine(folderPath, // Combine it all.
string.Format("{0}{3}({1}){2}", // The pattern to combine.
fileName, // The file name with no extension.
fileNumber, // The file number.
extension, // The file extension.
addSpace)); // A space if needed to pad the initial ~(#).ext pattern.
}
while (File.Exists(file)); // As long as the file name exists, keep looping.
}
return file;
}
} // END of CN_createXL
//} // End of Namespace NS_createXL
#endregion // CN_createXL
| | |