Goal: I'm trying to add a feature to my Excel 2016 VSTO plugin. The feature will get 1 column from the active sheet, and iterate over it changing the background color based on string length.
Problem: I'm having trouble getting string length from the cells. I can not figure out the proper syntax I currently have var count = row.Item[1].Value2.Text.Length;
Code: Here is what I have
public void CharacterLengthCheck(int length = 24, int Column = 3)
{
Worksheet sheet = Globals.ThisAddIn.Application.ActiveSheet;
var RowCount = sheet.UsedRange.Rows.Count;
Range column = sheet.Range[sheet.Cells[1, Column], sheet.Cells[RowCount, Column]];
foreach (Range row in column)
{
var count = row.Item[1].Value2.Text.Length;
if (count > length)
{
row.Item[1].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Red);
}
else
{
row.Item[1].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Green);
}
}
}
I think your problem is here:
row.Item[1].Value2.Text.Length
The length function is Len(x), so try Len(row.Item[1].Value2.Text)
Thank you to #Jeeped for their advice on using conditional formatting. However the answer to my original question is.
Change
var count = row.Item[1].Value2.Text.Length;
to
string text = (row.Item[1].Value).ToString();
var count = text.length;
I believe this is because value is dynamic and needs to be explicitly cast to string
I want to get info like x, y, width, height, title of the chart. Here is my version for HSSFChart which works (It returns non-zero values):
HSSFChart chart
title = chart.getChartTitle();
x = chart.getChartX();
y = chart.getChartY();
width = chart.getChartWidth();
height = chart.getChartHeight();
The problem is that I can't get the same or any other info from XSSFChart.
XSSFDrawing drawing = sheet.createDrawingPatriarch();
List<XSSFChart> chartsList = drawing.getCharts();
for (XSSFChart chart : chartsList){
#ctChart
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
title = ctChart.getTitle.toString();
int size = plotArea.getScatterChartList().size();
for (int j = 0; j < size; j++){
List<CTScatterSer> seriesList = plotArea.getScatterChartList().get(j).getSerList();
for (int i = 0; i < seriesList.size(); i++){
CTScatterSer ser = seriesList.get(i);
XmlObject serieX = ser.getXVal();
XmlObject serieY = ser.getYVal();
System.out.println("x: " + serieX.xmlText() + " y: " + serieY.xmlText());
}
}
if (plotArea.getLayout() != null)
if (plotArea.getLayout().getManualLayout() != null)
System.out.println("x: " + plotArea.getLayout().getManualLayout().getX() + " y: " +
plotArea.getLayout().getManualLayout().getY());
#chart
chart.getManualLayout().getX(); // returns 0
chart.getManualLayout().getY(); // returns 0
chart.getManualLayout().getHeightRatio(); // returns 0.0
chart.getManualLayout().getWidthRatio(); // returns 0.0
It prints nothing even if there are many charts and series.
I am struggling with a similar problem, and I am stuck also in your code..
List<org.openxmlformats.schemas.drawingml.x2006.chart.CTLineChart> chartList=plotArea.getLineChartList();
int size = chartList.size();
Mine is a LineChart so I've changed accordingly, but I have this runtime error:
Exception in thread "main" java.lang.NoClassDefFoundError: org/openxmlformats/schemas/drawingml/x2006/chart/impl/CTPlotAreaImpl$1LineChartList
at org.openxmlformats.schemas.drawingml.x2006.chart.impl.CTPlotAreaImpl.getLineChartList(Unknown Source)
at ....
Seems something is missing from my poi-openxml-schemas-3.13-20150929.jar file.
Anyway, I've commented out the relevant part and I have NULL as a result of queries on ManualLayout. This is terrible.
I have a strong feeling that the Chart is not rendered in an XML file (as is XLSX) but it's only rendered where we open the file with Office (or OpenOffice), as if the layout is still not done and will be done by the GUI.
In this case I think one can investigate this solution using PUNO:
http://www.wstech2.net/?do=0a,01,05
This involevs a bridge between Php and a running instance of OpenOffice, which exposes the UNO interface.
I've looked at this but it seemed overly complicated to install, but maybe this is the only way.
I am using Epplus library to generate Excel 2010 and up compatible files in Asp.Net C#.
I am using version 3.1.2 which is the latest at this moment.
I am setting the row height first, before adding any pictures like this:
ExcelPackage pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("sheet 1");
while (i < dt.Rows.Count + offset)
{
ws.Row(i).Height = 84;
i++;
}
dt is my DataTable with DataRows.
After setting the height, I am looping again through the rows to add the pictures
while (i < dt.Rows.Count + offset)
{
var prodImg = ws.Drawings.AddPicture(dr["code"].ToString(), new FileInfo(path));
prodImg.SetPosition(i - 1, 0, 14, 0);
prodImg.SetSize(75);
}
This works, but this does not:
var prodImg = ws.Drawings.AddPicture(dr["code"].ToString(), new FileInfo(path));
int w = prodImg.Image.Width;
int h = prodImg.Image.Height;
if (h > 140) // because height of 84 is 140 pixels in excel
{
double scale = h / 140.0;
w = (int)Math.Floor(w / scale);
h = 140;
}
int xOff = (150 - w) / 2;
int yOff = (140 - h) / 2;
prodImg.SetPosition(i - 1, xOff, 11, yOff);
prodImg.SetSize(w, h);
This results in off center pictures and unresized images. And this code then which is in the same loop:
var prodImgDm = ws.Drawings.AddPicture("bcdm" + dr["code"].ToString(), new FileInfo(pathDm));
prodImgDm.SetPosition(i - 1, 25, 15, 40);
prodImgDm.SetSize(100);
This does work sometimes. the pictures prodImgDm are datamatrix images with a static width and height and do not need to be resized because they are always small/tiny. So also without the SetSize in some rows, it works and in some other rows, it does not work. Really strange because the code is the same. It might be something in the library and/or Excel. Perhaps I am using it wrong? Any epplus picture expert?
Thanks in advance!!
edit sometimes a picture is worth a thousand words, so here is the screenshot. As you can see the product images are not horizontal and vertical aligned in the cell. And the datamatrix on the far right is sometimes scaled about 120% even when I set SetSize(100) so it is really strange to me. So the last datamatrix has the correct size... I already found this SO thread but that does not help me out, I think.
edit 2013/04/09 Essenpillai gave me a hint to set
pck.DoAdjustDrawings = false;
but that gave me even stranger images:
the datamatrix is still changing on row basis. on row is ok, the other is not. and the ean13 code is too wide.
public static void CreatePicture(ExcelWorksheet worksheet, string name, Image image, int firstColumn, int lastColumn, int firstRow, int lastRow, int defaultOffsetPixels)
{
int columnWidth = GetWidthInPixels(worksheet.Cells[firstRow, firstColumn]);
int rowHeight = GetHeightInPixels(worksheet.Cells[firstRow, firstColumn]);
int totalColumnWidth = columnWidth * (lastColumn - firstColumn + 1);
int totalRowHeight = rowHeight * (lastRow - firstRow + 1);
double cellAspectRatio = Convert.ToDouble(totalColumnWidth) / Convert.ToDouble(totalRowHeight);
int imageWidth = image.Width;
int imageHeight = image.Height;
double imageAspectRatio = Convert.ToDouble(imageWidth) / Convert.ToDouble(imageHeight);
int pixelWidth;
int pixelHeight;
if (imageAspectRatio > cellAspectRatio)
{
pixelWidth = totalColumnWidth - defaultOffsetPixels * 2;
pixelHeight = pixelWidth * imageHeight / imageWidth;
}
else
{
pixelHeight = totalRowHeight - defaultOffsetPixels * 2;
pixelWidth = pixelHeight * imageWidth / imageHeight;
}
int rowOffsetPixels = (totalRowHeight - pixelHeight) / 2;
int columnOffsetPixels = (totalColumnWidth - pixelWidth) / 2;
int rowOffsetCount = 0;
int columnOffsetCount = 0;
if (rowOffsetPixels > rowHeight)
{
rowOffsetCount = (int)Math.Floor(Convert.ToDouble(rowOffsetPixels) / Convert.ToDouble(rowHeight));
rowOffsetPixels -= rowHeight * rowOffsetCount;
}
if (columnOffsetPixels > columnWidth)
{
columnOffsetCount = (int)Math.Floor(Convert.ToDouble(columnOffsetPixels) / Convert.ToDouble(columnWidth));
columnOffsetPixels -= columnWidth * columnOffsetCount;
}
int row = firstRow + rowOffsetCount - 1;
int column = firstColumn + columnOffsetCount - 1;
ExcelPicture pic = worksheet.Drawings.AddPicture(name, image);
pic.SetPosition(row, rowOffsetPixels, column, columnOffsetPixels);
pic.SetSize(pixelWidth, pixelHeight);
}
public static int GetHeightInPixels(ExcelRange cell)
{
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
float dpiY = graphics.DpiY;
return (int)(cell.Worksheet.Row(cell.Start.Row).Height * (1 / 72.0) * dpiY);
}
}
public static float MeasureString(string s, Font font)
{
using (var g = Graphics.FromHwnd(IntPtr.Zero))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
return g.MeasureString(s, font, int.MaxValue, StringFormat.GenericTypographic).Width;
}
}
public static int GetWidthInPixels(ExcelRange cell)
{
double columnWidth = cell.Worksheet.Column(cell.Start.Column).Width;
Font font = new Font(cell.Style.Font.Name, cell.Style.Font.Size, FontStyle.Regular);
double pxBaseline = Math.Round(MeasureString("1234567890", font) / 10);
return (int)(columnWidth * pxBaseline);
}
enter image description here
I have the same problem with Epplus library.
After I find no solution how to solve this in my code, I checked source code of this library.
Epplus create excel picture always as twoCellAnchor drawing. In xlsx files you can find drawingXYZ.xml with this code:
<xdr:twoCellAnchor editAs="oneCell">
<xdr:from> ... </xdr:from>
<xdr:to> ... </xdr:to>
<xdr:pic>
...
</xdr:twoCellAnchor>
So, picture is always connected to two cells, and this is not variable part of Epplus library. You can find this part of code in ExcelDrawing.cs file.
XmlElement drawNode = _drawingsXml.CreateElement(
"xdr", "twoCellAnchor", ExcelPackage.schemaSheetDrawings);
colNode.AppendChild(drawNode);
You can easy create your own copy of this dll. The good news is that you need to modify only two files to fix this problem. So..
Download your copy of source codes for Epplus library from this site and open in Visual Studio.
We need to generate code of drawing as oneCellAnchor, so we must remove <xdr:to> element for pictures and create element <xdr:ext /> with picture dimensions as parameters.
New xml structure will looks like:
<xdr:oneCellAnchor editAs="oneCell">
<xdr:from> ... </xdr:from>
<xdr:ext cx="1234567" cy="7654321" />
<xdr:pic>
...
</xdr:oneCellAnchor>
Ok, so, how to do this?
Changes in Epplus code
ExcelDrawings.cs (link to file here)
At first we modify method CreateDrawingXml() inside ExcelDrawings.cs. Order to preserve the original functionality we add an optional parameter (if create oneCellAnchor) with default value. And in method, based this parameter, we create one or tow cell anchor and create or not <xdr:to> element.
Important part of this method code:
private XmlElement CreateDrawingXml(bool twoCell = true) {
if (DrawingXml.OuterXml == "")
{ ... } // not changed
XmlNode colNode= _drawingsXml.SelectSingleNode("//xdr:wsDr", NameSpaceManager);
//First change in method code
XmlElement drawNode;
if (twoCell)
drawNode = _drawingsXml.CreateElement(
"xdr", "twoCellAnchor", ExcelPackage.schemaSheetDrawings);
else
drawNode = _drawingsXml.CreateElement(
"xdr", "oneCellAnchor", ExcelPackage.schemaSheetDrawings);
colNode.AppendChild(drawNode);
//Add from position Element; // Not changed
XmlElement fromNode = _drawingsXml.CreateElement(
"xdr", "from", ExcelPackage.schemaSheetDrawings);
drawNode.AppendChild(fromNode);
fromNode.InnerXml = "<xdr:col>0</xdr:col><xdr:colOff>0</xdr:colOff>"
+ "<xdr:row>0</xdr:row><xdr:rowOff>0</xdr:rowOff>";
//Add to position Element;
//Second change in method
if (twoCell)
{
XmlElement toNode = _drawingsXml.CreateElement(
"xdr", "to", ExcelPackage.schemaSheetDrawings);
drawNode.AppendChild(toNode);
toNode.InnerXml = "<xdr:col>10</xdr:col><xdr:colOff>0</xdr:colOff>"
+ "<xdr:row>10</xdr:row><xdr:rowOff>0</xdr:rowOff>";
}
return drawNode;
}
Then we modify two methods for AddPicture inside the same file:
public ExcelPicture AddPicture(string Name, Image image, Uri Hyperlink)
{
if (image != null) {
if (_drawingNames.ContainsKey(Name.ToLower())) {
throw new Exception("Name already exists in the drawings collection");
}
XmlElement drawNode = CreateDrawingXml(false);
// Change: we need create element with dimensions
// like: <xdr:ext cx="3857625" cy="1047750" />
XmlElement xdrext = _drawingsXml.CreateElement(
"xdr", "ext", ExcelPackage.schemaSheetDrawings);
xdrext.SetAttribute("cx",
(image.Width * ExcelDrawing.EMU_PER_PIXEL).ToString());
xdrext.SetAttribute("cy",
(image.Height * ExcelDrawing.EMU_PER_PIXEL).ToString());
drawNode.AppendChild(xdrext);
// End of change, next part of method is the same:
drawNode.SetAttribute("editAs", "oneCell");
...
}
}
And this method with FileInfo as input parameter:
public ExcelPicture AddPicture(string Name, FileInfo ImageFile, Uri Hyperlink)
{
if (ImageFile != null) {
if (_drawingNames.ContainsKey(Name.ToLower())) {
throw new Exception("Name already exists in the drawings collection");
}
XmlElement drawNode = CreateDrawingXml(false);
// Change: First create ExcelPicture object and calculate EMU dimensions
ExcelPicture pic = new ExcelPicture(this, drawNode, ImageFile, Hyperlink);
XmlElement xdrext = _drawingsXml.CreateElement(
"xdr", "ext", ExcelPackage.schemaSheetDrawings);
xdrext.SetAttribute("cx",
(pic.Image.Width * ExcelDrawing.EMU_PER_PIXEL).ToString());
xdrext.SetAttribute("cy",
(pic.Image.Height * ExcelDrawing.EMU_PER_PIXEL).ToString());
drawNode.AppendChild(xdrext);
// End of change, next part of method is the same (without create pic object)
drawNode.SetAttribute("editAs", "oneCell");
...
}
}
So, this are all important code. Now we must change code for searching nodes and preserve order in elements.
In private void AddDrawings() we change xpath from:
XmlNodeList list = _drawingsXml.SelectNodes(
"//xdr:twoCellAnchor", NameSpaceManager);
To this:
XmlNodeList list = _drawingsXml.SelectNodes(
"//(xdr:twoCellAnchor or xdr:oneCellAnchor)", NameSpaceManager);
It is all in this file, now we change
ExcelPicture.cs (link to file here)
Original code find node for append next code in constructor like this:
node.SelectSingleNode("xdr:to",NameSpaceManager);
Because we do not create <xdr:to> element always, we change this code:
internal ExcelPicture(ExcelDrawings drawings, XmlNode node
, Image image, Uri hyperlink)
: base(drawings, node, "xdr:pic/xdr:nvPicPr/xdr:cNvPr/#name")
{
XmlElement picNode = node.OwnerDocument.CreateElement(
"xdr", "pic", ExcelPackage.schemaSheetDrawings);
// Edited: find xdr:to, or xdr:ext if xdr:to not exists
XmlNode befor = node.SelectSingleNode("xdr:to",NameSpaceManager);
if (befor != null && befor.Name == "xdr:to")
node.InsertAfter(picNode, befor);
else {
befor = node.SelectSingleNode("xdr:ext", NameSpaceManager);
node.InsertAfter(picNode, befor);
}
// End of change, next part of constructor is unchanged
_hyperlink = hyperlink;
...
}
And the same for second constructor with FileInfo as input parameter:
internal ExcelPicture(ExcelDrawings drawings, XmlNode node
, FileInfo imageFile, Uri hyperlink)
: base(drawings, node, "xdr:pic/xdr:nvPicPr/xdr:cNvPr/#name")
{
XmlElement picNode = node.OwnerDocument.CreateElement(
"xdr", "pic", ExcelPackage.schemaSheetDrawings);
// Edited: find xdr:to, or xdr:ext if xdr:to not exists
XmlNode befor = node.SelectSingleNode("xdr:to", NameSpaceManager);
if (befor != null && befor.Name == "xdr:to")
node.InsertAfter(picNode, befor);
else {
befor = node.SelectSingleNode("xdr:ext", NameSpaceManager);
node.InsertAfter(picNode, befor);
}
// End of change, next part of constructor is unchanged
_hyperlink = hyperlink;
...
Now, pictures are created as oneCellAnchor. If you want, you can create multiple AddPicture methods for booth variants. Last step is build this project and create your own custom EPPlus.dll. Then close your project which use this dll and copy new files EPPlus.dll, EPPlus.pdb, EPPlus.XML inside your project (backup and replace your original dll files) at the same place (so you don't need do any change in your project references or settings).
Then open and rebuild your project and try if this solve your problem.
Maybe I am too late, but here is mine answer..
you can read it on codeplex issue as well
(https://epplus.codeplex.com/workitem/14846)
I got this problem as well.
And after some research I figured out where the bug is.
It's in the ExcelRow class on the 149 line of code (ExcelRow.cs file).
There is a mistake, when row's Height got changed it recalcs all pictures heights but uses pictures widths inplace of heights, so it's easy to fix.
Just change the line
var pos = _worksheet.Drawings.GetDrawingWidths();
to
var pos = _worksheet.Drawings.GetDrawingHeight();
See the code changes on image
P.S. Actual for version 4.0.4
Hi was wondering through internet for my solution. considering my excel sheet as array i want to get data based on row and column index, how can i do this. i am not getting a clue to implement this my excel sheet look like below
please help in this. i am getting mad of trying different methods.
my requirement is if i give R3,C2 in console ,i should get 4 as my answer.
i was using Excel.interop for getting this,you can suggest solution by other methods too.
thanking you in anticipation.
One solution would be to call Excel worksheet functions INDEX and MATCH (the basic Excel formula would be =INDEX($B$2:$D$4,MATCH("R3",$A$2:$A$4,0),MATCH("C2",$B$1:$D$1,0))
Or you could retrieve the data into an object array, look for C2 in the first row and R3 in the first column.
using System;
using Excel=Microsoft.Office.Interop.Excel;
namespace ReadingExcelBasedOnRowColumn
{
class Program
{
static void Main(string[] args)
{
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(#"C:\Users\SaiKiran\Desktop\MyExcl2.xlsx");
Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
Excel.Range xlRange = xlWorksheet.UsedRange;
Console.WriteLine("enter x and y value:");
int x = Convert.ToInt32(Console.ReadLine());
int y = Convert.ToInt32(Console.ReadLine());
if (xlRange != null)
{
int nRows = xlRange.Rows.Count;
int nCols = xlRange.Columns.Count;
for (int iRow = 1; iRow <= nRows; iRow++)
{
for (int iCount = 1; iCount <= nCols; iCount++)
{
xlRange = (Microsoft.Office.Interop.Excel.Range)xlWorksheet.Cells[x, y];
Console.WriteLine(xlRange.Text);
Console.ReadLine();
}
}
}
}
}
}
i got answer.. after struggling two days
I need to display the output from Axapta in Excel in the following manner.
Kindly assist.
I have added the following method in my base Excel Class and am able to get the solution.
public void setCellProperty(int _r1, int _c1,int _r2, int _c2,
int _orientation = 0,
int _horizontalAlignment = 1,
int _verticalAlignment = 1,
boolean _wrapText = False,
boolean _addIndent = False,
int _indentLevel = 0,
boolean _shrinkToFit = False,
int _readingOrder = 0,
boolean _mergeCells = False)
{
COM cell1,cell2;
str range;
;
cell1 = null;
cell2 = null;
cell1 = worksheet.cells();
this.variant2COM(cell1, cell1.item(_r1,_c1));
cell2 = worksheet.cells();
this.variant2COM(cell2, cell2.item(_r2,_c2));
range =strfmt("%1:%2",cell1.address(),cell2.address());
cellRange =worksheet.Range(range);
cellRange.Mergecells(_mergeCells);
cellRange.IndentLevel(_indentLevel);
cellRange.AddIndent(_addIndent);
cellRange.Orientation(_orientation);
cellRange.VerticalAlignment(_verticalAlignment);
cellRange.HorizontalAlignment(_horizontalAlignment);
}
the variable _orientation decides the inclination, for the case above pass orientation=90
See this blog on how to export to Excel.
"Rotate a text" is a text formatting option, others are struggling too.
It boils down to whether there is a Excel API, which do that.
Sorry, I can't help you futher.