how to select data from excel automatically using Microsoft.Office.Interop - c#

I am using
using Microsoft.Office.Core;
using Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.PowerPoint;
I have charts in PowerPoint like
and excel like
i need make select data as dynamic when add data more than 7 use 7 only because only charts select 7 rows but need this dynamic when excel data have more or less than 7 select automatically .
when add more than 7 data already exist in excel but select 7 rows only
foreach (Microsoft.Office.Interop.PowerPoint.Shape textShape in slide.Shapes)
{
if (textShape.HasChart == MsoTriState.msoTrue || textShape.HasSmartArt == MsoTriState.msoTrue)
{
ChartData chartData = textShape.Chart.ChartData;
//textShape.Chart.Legend.te = JsonData.twitter_account_analysis.content.content_type[0].name;
chartData.Activate();
Workbook workbook = chartData.Workbook;
Microsoft.Office.Interop.PowerPoint.ChartArea chartArea = textShape.Chart.ChartArea;
workbook.Application.Visible = false;
//workbook.Application.Calculate();
Worksheet dataSheet = workbook.Worksheets[1];
//dataSheet.TableUpdate();
//System.Threading.Thread.Sleep(50);
int firstcolNumber = 2;
int rowNumber = 2;
// Clearing previous data
//dataSheet.UsedRange.Columns[1, Type.Missing].Clear();
//dataSheet.UsedRange.Columns[2, Type.Missing].Clear();
//Dount chart
for (int i = 0; i <99; i++)
{
if (i < JsonData.twitter_metrics.tweets_over_time.Length)
{
dataSheet.Cells[firstcolNumber + i, rowNumber].Clear();
dataSheet.Cells[firstcolNumber + i, 1].Clear();
dataSheet.Cells[firstcolNumber + i, rowNumber] = JsonData.twitter_metrics.tweets_over_time[i].stats_count.ToString();
dataSheet.Cells[firstcolNumber + i, 1] = JsonData.twitter_metrics.tweets_over_time[i].id;
}
else
{
dataSheet.Cells[firstcolNumber + i, rowNumber].Clear();
dataSheet.Cells[firstcolNumber + i, 1].Clear();
}
}
//dataSheet.get_Range("A1", $"B{JsonData.twitter_metrics.tweets_over_time.Length}").Select();
//Marshal.FinalReleaseComObject(dataSheet);
var yAxis = (Microsoft.Office.Interop.PowerPoint.Axis)textShape.Chart.Axes(Microsoft.Office.Interop.PowerPoint.XlAxisType.xlValue, Microsoft.Office.Interop.PowerPoint.XlAxisGroup.xlPrimary);
var xAxis = (Microsoft.Office.Interop.PowerPoint.Axis)textShape.Chart.Axes(Microsoft.Office.Interop.PowerPoint.XlAxisType.xlCategory, Microsoft.Office.Interop.PowerPoint.XlAxisGroup.xlPrimary);
}
Marshal.ReleaseComObject(textShape);
}
Marshal.ReleaseComObject(slide);
//System.Threading.Thread.Sleep(50);
break;

Related

Reading shapes and textboxes in EPPlus 4.5.3

I am trying to read an excel file using EPPlus version 4.5.3, which I am able to do using the code below:
FileInfo existingFile = new FileInfo(FilePath);
using (ExcelPackage package = new ExcelPackage(existingFile))
{
//get the first worksheet in the workbook
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
int colCount = worksheet.Dimension.End.Column; //get Column Count
int rowCount = worksheet.Dimension.End.Row; //get row count
for (int row = 1; row <= rowCount; row++)
{
for (int col = 1; col <= colCount; col++)
{
Console.WriteLine(" Row:" + row + " column:" + col + " Value:" + worksheet.Cells[row, col].Value?.ToString().Trim());
}
}
}
Now, the place I am getting stuck at is with shapes. So the excel file that I need to read have shapes in it, these shapes have text inside it that I am trying to read. I have tried searching on the internet for this problem but I cant seem to find anything on it.
How can I read this data? The code I have tried thus far:
foreach (var drawing in sheet.Drawings)
{
var type = drawing.GetType();
var data = drawing.ToString();
Console.WriteLine("Drawing Type:" + type + " Data: " + data);
}
I was just having this issue today and figured it out. You have to iterate the worksheet.Drawings collection first to determine if any drawings are "shapes". From what I know of Excel VBA, you can not put text on an image/picture it HAS to be a shape. Someone can correct me if I am wrong.
using (ExcelPackage excelPackage = new ExcelPackage(stream))
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];
foreach (ExcelDrawing dw in worksheet.Drawings)
{
if (dw.GetType().ToString() == "OfficeOpenXml.Drawing.ExcelShape")
{
ExcelShape shape = (ExcelShape)dw;
if (shape.RichText != null && shape.RichText.Count > 0)
{
foreach (ExcelParagraph item in shape.RichText)
{
Console.WriteLine("{0} - Rich Text Line: {1}", dw.Name, item.Text);
}
}
}
else
{ Console.WriteLine("{0} is not a shape, its a {1}", dw.Name, dw.GetType().ToString()); }
}
}
From there it should be rather easy jump to modify the text in the picture:
item.RichText[1].Text = "Updated Text";
Output:
Picture 1 is not a shape, its a OfficeOpenXml.Drawing.ExcelPicture
TextBox 1 - Rich Text Line: Inventory List
TextBox 1 - Rich Text Line: Some Company

When converting csv to xls in a formatted template why is the first sheet not formatted?

Building an application that reads a CSV file and converts it into xls with c# in VS 2017.
I'm using CsvHelper and Microsoft.Office.Interop.Excel to accomplish this.
The application can read a CSV file in a windows form, have the program set up a template and insert into that formatted template all the values in the correct cells HOWEVER the very first page created no matter which file is used is not formatted and gets inserted in a un-formatted excel page.
I have attempted:
changing the arguments of the "Workbook.Sheets.Add"
where in the logic the sheets get added
changing the arguments in the SaveAs function
Changing various indexes from 1 to 0 and visa versa
I am new to working with the (Interop.Excel) namespace and have spent much time reading the DOCS on the MS web-page yet I still cannot resolve this issue.
Here is how I am adding the pages to the workbook:
if (!backgroundWorker.CancellationPending)
{
backgroundWorker.ReportProgress(index++ * 100 / pageCount);
Thread.Sleep(delay);
wb.Sheets.Add(missing,After:wb.Sheets[wb.Sheets.Count],Count:missing,Type:template);
Worksheet ws = (Worksheet)wb.Sheets[wb.Sheets.Count];
}
Here is how I am saving the pages:
wb.SaveAs(fileName, XlFileFormat.xlWorkbookDefault, missing, missing, true, false, XlSaveAsAccessMode.xlNoChange,
XlSaveConflictResolution.xlLocalSessionChanges,
missing, missing);
excel.Quit();
Here is a reference to the whole method:
namespace csvReader
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
struct DataParameter
{
public List<material> materialList;
public List<material> smallMats;
public Workbook wbData;
public string Filename { get; set; }
public int Delay;
}
DataParameter _inputParameter;
private void btnWrite_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy)
return;
using (SaveFileDialog sfd = new SaveFileDialog() { Filter = "Excel Workbook|*.xls" })
{
if (sfd.ShowDialog() == DialogResult.OK)
{
_inputParameter.Filename = sfd.FileName;
_inputParameter.materialList = materialBindingSource2.DataSource as List<material>;
_inputParameter.Delay = 100;
progressBar.Minimum = 0;
progressBar.Value = 0;
backgroundWorker.RunWorkerAsync(_inputParameter);
}
}
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object missing = Type.Missing;
List<material> list = ((DataParameter)e.Argument).materialList;
List<material> cellM = ((DataParameter)e.Argument).smallMats;
string fileName = ((DataParameter)e.Argument).Filename;
int pageCount = 1;
int process = list.Count;
int setRows = 19;
int delay = 100;
if (list.Count > setRows)
{
pageCount = process / setRows;
}
Microsoft.Office.Interop.Excel.Application excel = new
Microsoft.Office.Interop.Excel.Application();
string template = "(mytemplatefilepath)";
Workbook wb = ((DataParameter)e.Argument).wbData;
wb = excel.Workbooks.Add();
excel.Visible = false;
int index = 1;
try
{
for (int i = 1; i < pageCount; i++)
{
if (!backgroundWorker.CancellationPending)
{
backgroundWorker.ReportProgress(index++ * 100 / pageCount);
Thread.Sleep(delay);
wb.Sheets.Add(missing,After:wb.Sheets[wb.Sheets.Count],Count:missing,Type:template);
Worksheet ws = (Worksheet)wb.Sheets[wb.Sheets.Count];
}
}
int range = 1;
int sheetIndex = 1;
foreach (Worksheet w in wb.Sheets)
{
w.Name = "Sheet" + sheetIndex++;
//w.Cells["L", 3] = tbSpecial.Text;
cellM = list.GetRange(range, 19);
int startCell = 7;
foreach (material m in cellM)
{
if (!backgroundWorker.CancellationPending)
{
backgroundWorker.ReportProgress(index++ * 100 / process);
Thread.Sleep(delay);
Microsoft.Office.Interop.Excel.Range newInput = w.get_Range("C" + startCell, "L" + startCell) as Microsoft.Office.Interop.Excel.Range;
w.Cells[startCell, 2] = m.Qty.ToString();
w.Cells[startCell, 3] = m.Section.ToString();
w.Cells[startCell, 4] = m.Length.ToString();
w.Cells[startCell, 5] = m.Camber.ToString();
w.Cells[startCell, 6] = m.Ends.ToString();
w.Cells[startCell, 7] = m.Grade.ToString();
w.Cells[startCell, 8] = m.Seq.ToString();
w.Cells[startCell, 9] = m.Member.ToString();
//w.Cells["L", 3] ="700";
startCell++;
}
}
range = range + 19;
}
wb.SaveAs(fileName, XlFileFormat.xlWorkbookDefault, missing, missing, true, false, XlSaveAsAccessMode.xlNoChange,
XlSaveConflictResolution.xlLocalSessionChanges,
missing, missing);
excel.Quit();
}
catch (Exception ex)
{
backgroundWorker.CancelAsync();
MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Here is the first page of a data set of 203 pages
https://imgur.com/t6dd2ip
and the other 202 pages appear as so
https://imgur.com/jMuNMeb
I feel like it's syntax error I'm making somewhere.
Although it is easy to fill in one excel page I'd rather have the program complete 100% of this work with the technologies I am using.
The reason the first sheet did not have the template layout and that if 'i = 0' bombs is that by default when instantiating a workbook with Workbooks.Add() a new Worksheet is added. This sheet will not be formatted however a template can be added in the parenthesis of the Add method to give that first sheet that format.
string template = "C:/whereEverMyTemplateIs/template"
wb = excel.Workbooks.Add(template);
Then just remember that it is added and make as many more as needed
for (int i = 1; i < pageCount; i++)
{
if (!backgroundWorker.CancellationPending)
{
backgroundWorker.ReportProgress(index++ * 100 / pageCount);
Thread.Sleep(delay);
wb.Sheets.Add(missing,After:wb.Sheets[wb.Sheets.Count],Count:missing,
Type:template);
//Worksheet ws = (Worksheet)wb.Sheets[wb.Sheets.Count];
}
}

Unmerge and clear cells in epplus 4.1

I had no luck deleting rows in excel so now I try to clear their content and still get the error:
"Can't delete/overwrite merged cells. A range is partly merged with the another merged range. A57788:J57788".
Columns 1-10 are really merged, but how do I unmerge them?
Here's my code:
cntr = 0;
while (ws.Cells[RowNum + cntr, 1].Value == null || !ws.Cells[RowNum + cntr, 1].Value.ToString().StartsWith("Report generation date"))
{
ws.Cells[RowNum + cntr, 1, RowNum + cntr, 18].Value = "";
ws.Cells[RowNum + cntr, 1, RowNum + cntr, 10].Merge = false;
for (int i = 1; i < 17; i++)
{
ws.Cells[RowNum + cntr, i].Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.None);
ws.Cells[RowNum + cntr, i].Clear();
}
cntr++;
}
//ws.DeleteRow(RowNum, cntr);
The thing is you can not unmerge a single cell in a range, you have to unmerge the whole range.
To do that you can get the merged range that a cell belongs to using my solution here:
public string GetMergedRange(ExcelWorksheet worksheet, string cellAddress)
{
ExcelWorksheet.MergeCellsCollection mergedCells = worksheet.MergedCells;
foreach (var merged in mergedCells)
{
ExcelRange range = worksheet.Cells[merged];
ExcelCellAddress cell = new ExcelCellAddress(cellAddress);
if (range.Start.Row<=cell.Row && range.Start.Column <= cell.Column)
{
if (range.End.Row >= cell.Row && range.End.Column >= cell.Column)
{
return merged.ToString();
}
}
}
return "";
}
The second step is unmerging the whole range using:
public void DeleteCell(ExcelWorksheet worksheet, string cellAddress)
{
if (worksheet.Cells[cellAddress].Merge == true)
{
string range = GetMergedRange(worksheet, cellAddress); //get range of merged cells
worksheet.Cells[range].Merge = false; //unmerge range
worksheet.Cells[cellAddress].Clear(); //clear value
}
}
This way will cost you to lose merging of the other cells, and their value, to overcome this you can save value before clearing and unmerging then you can write it back, something like:
public void DeleteCell(ExcelWorksheet worksheet, string cellAddress)
{
if (worksheet.Cells[cellAddress].Merge == true)
{
var value = worksheet.Cells[cellAddress].Value;
string range = GetMergedRange(worksheet, cellAddress); //get range of merged cells
worksheet.Cells[range].Merge = false; //unmerge range
worksheet.Cells[cellAddress].Clear(); //clear value
//merge the cells you want again.
//fill the value in cells again
}
}

Try...catch returning nothing but code is still breaking

UPDATE: So this code is collection a SQL Query into a DataSet prior to this method. This data set is then dropped into excel in the corresponding tab at a specific cell address(which is loaded from the form) but the code below is the exporting to excel method. I am getting the following error:
An unhandled exception of type 'System.AccessViolationException' occurred in SQUiRE (Sql QUery REtriever) v1.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I have been tracking this for a while and thought I fixed it, but my solution was a false positive. So I am using a try...catch block that is breaking but not returning anything. Let me know if you all see anything that I am missing. I usually break on this line (templateSheet = templateBook.Sheets[tabName];) and on the same tabName. The tab is not locked or restricted so It can be written to and works more than half of the time.
public void ExportToExcel(DataSet dataSet, Excel.Workbook templateBook, int i, int h, Excel.Application excelApp) //string filePath,
{
try
{
lock (this.GetType())
{
Excel.Worksheet templateSheet;
//check to see if the template is already open, if its not then open it,
//if it is then bind it to work with it
//if (!fileOpenTest)
//{ templateBook = excelApp.Workbooks.Open(filePath); }
//else
//{ templateBook = (Excel.Workbook)System.Runtime.InteropServices.Marshal.BindToMoniker(filePath); }
//Grabs the name of the tab to dump the data into from the "Query Dumps" Tab
string tabName = lstQueryDumpSheet.Items[i].ToString();
templateSheet = templateBook.Sheets[tabName];
// Copy DataTable
foreach (System.Data.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 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, col] = dt.Rows[row].ItemArray[col]; }
}
// Calculate the final column letter
string finalColLetter = string.Empty;
string colCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int colCharsetLen = 26;
if (dt.Columns.Count > colCharsetLen)
{ finalColLetter = colCharset.Substring((dt.Columns.Count - 1) / colCharsetLen - 1, 1); }
finalColLetter += colCharset.Substring((dt.Columns.Count - 1) % colCharsetLen, 1);
/*Grabs the full cell address from the "Query Dump" sheet, splits on the '=' and
*pulls out only the cell address (i.e., "address=a3" becomes "a3")*/
string dumpCellString = lstQueryDumpText.Items[i].ToString();
string dumpCell = dumpCellString.Split('=').Last();
/*Refers to the range in which we are dumping the DataSet. The upper right hand cell is
*defined by 'dumpCell'and the bottom right cell is defined by the final column letter
*and the count of rows.*/
string firstRef = "";
string baseRow = "";
//Determines if the column is one letter or two and handles them accordingly
if (char.IsLetter(dumpCell, 1))
{
char[] createCellRef = dumpCell.ToCharArray();
firstRef = createCellRef[0].ToString() + createCellRef[1].ToString();
for (int z = 2; z < createCellRef.Count(); z++)
{ baseRow = baseRow + createCellRef[z].ToString(); }
}
else
{
char[] createCellRef = dumpCell.ToCharArray();
firstRef = createCellRef[0].ToString();
for (int z = 1; z < createCellRef.Count(); z++)
{ baseRow = baseRow + createCellRef[z].ToString(); }
}
int baseRowInt = Convert.ToInt32(baseRow);
int startingCol = ColumnLetterToColumnIndex(firstRef);
int endingCol = ColumnLetterToColumnIndex(finalColLetter);
int finalCol = startingCol + endingCol;
string endCol = ColumnIndexToColumnLetter(finalCol - 1);
int endRow = (baseRowInt + (dt.Rows.Count - 1));
string cellCheck = endCol + endRow;
string excelRange;
if (dumpCell.ToUpper() == cellCheck.ToUpper())
{ excelRange = string.Format(dumpCell + ":" + dumpCell); }
else
{ excelRange = string.Format(dumpCell + ":{0}{1}", endCol, endRow); }
//Dumps the cells into the range on Excel as defined above
templateSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
/*Check to see if all the SQL queries have been run from
if (i == lstSqlAddress.Items.Count - 1)
{
//Turn Auto Calc back on
excelApp.Calculation = Excel.XlCalculation.xlCalculationAutomatic;
/*Run through the value save sheet array then grab the address from the corresponding list
*place in the address array. If the address reads "whole sheet" then save the whole page,
*else set the addresses range and value save that.
for (int y = 0; y < lstSaveSheet.Items.Count; y++)
{
MessageBox.Show("Save Sheet: " + lstSaveSheet.Items[y] + "\n" + "Save Address: " + lstSaveRange.Items[y]);
}*/
//run the macro to hide the unused columns
excelApp.Run("ReportMakerExecute");
//save excel file as hospital name and move onto the next
SaveTemplateAs(templateBook, h);
}
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}

How to get text from slide in C# using Aspose

i am getting all shapes in slides of ppt file now i want to get text from those shapes how can i do this
here is my method where i am getting shapes of all slides in ppt file
public void Main(string[] args)
{
// The path to the documents directory.
string dataDir = Path.GetFullPath(#"C:\Users\Vipin\Desktop\");
//Load the desired the presentation
Presentation pres = new Presentation(dataDir + "Android.ppt");
using (Presentation prestg = new Presentation(dataDir + "Android.ppt"))
{
//Accessing a slide using its slide index
int slideCount = prestg.Slides.Count();
for (int i = 0; i <= slideCount - 1; i++)
{
ISlide slide = pres.Slides[i];
foreach (IShape shap in slide.Shapes)
{
int slideCountNumber = i + 1;
float shapeHeight = shap.Frame.Height;
float shapeWidth = shap.Frame.Width;
Debug.Write("slide Number: " + slideCountNumber + " shape width = " + shapeWidth + " shapeHeight = " + shapeHeight);
}
}
}
}
now ho can i get the text from it
aspose will give u truncated text if u don't have the license of it. so it will be better for you if you will use Microsoft.Office.Interop.PowerPoint
use as below
public void ReadSlide(){
string filePath= #"C:\Users\UserName\Slide.pptx";
Microsoft.Office.Interop.PowerPoint.Application PowerPoint_App = new Microsoft.Office.Interop.PowerPoint.Application();
Microsoft.Office.Interop.PowerPoint.Presentations multi_presentations = PowerPoint_App.Presentations;
Microsoft.Office.Interop.PowerPoint.Presentation presentation = multi_presentations.Open(filePath, MsoTriState.msoFalse, MsoTriState.msoFalse, MsoTriState.msoFalse);
string presentation_textforParent = "";
foreach (var item in presentation.Slides[1].Shapes)
{
var shape = (Microsoft.Office.Interop.PowerPoint.Shape)item;
if (shape.HasTextFrame == MsoTriState.msoTrue)
{
if (shape.TextFrame.HasText == MsoTriState.msoTrue)
{
var textRange = shape.TextFrame.TextRange;
var text = textRange.Text;
presentation_textforParent += text + " ";
}
}
}
}
You may want to extract text not from all shapes, but from text frames instead. In order to do this use the GetAllTextFrames static method exposed by the PresentationScanner class
using (Presentation prestg = new Presentation(dataDir + "Android.ppt"))
{
//Get an Array of ITextFrame objects from all slides in the PPTX
ITextFrame[] textFramesPPTX = Aspose.Slides.Util.SlideUtil.GetAllTextFrames(pptxPresentation, true);
//Loop through the Array of TextFrames
for (int i = 0; i < textFramesPPTX.Length; i++)
//Loop through paragraphs in current ITextFrame
foreach (IParagraph para in textFramesPPTX[i].Paragraphs)
//Loop through portions in the current IParagraph
foreach (IPortion port in para.Portions)
{
//Display text in the current portion
Console.WriteLine(port.Text);
//Display font height of the text
Console.WriteLine(port.PortionFormat.FontHeight);
//Display font name of the text
if (port.PortionFormat.LatinFont != null)
Console.WriteLine(port.PortionFormat.LatinFont.FontName);
}
See documentation

Categories