I am looking to obtain the name of a cell given a row/column pair. I need a name to use in a formula. For example, I would like to set cell (3, 4) to a formula that adds the values of cells (4, 4) and (5, 4).
Cell (3, 4) is D5; (4, 4) is E5, and (5, 4) is F5. Therefore, my formula should look like this:
=E5+F5
I can format my formula like this:
const int col = 3;
const int row = 4;
worksheet.Cells[row, col].Formula = string.Format(
"={0}{1}+{2}{3}"
, (char)('A'+col+1) // <<== Broken, do not use
, row+1
, (char)('A'+col+2) // <<== Broken, do not use
, row+1
);
This works fine for columns A..Z, but breaks for columns with names further to the right. I could use a trick described in this Q&A, but the problem looks so basic that it shouldn't require coding on my part.
You do not need to do this the hard way, because Aspose did it for you. Their CellsHelper class has CellIndexToName method, which does exactly what you need:
const int col = 3;
const int row = 4;
worksheet.Cells[row, col].Formula =
$"={CellsHelper.CellIndexToName(row, col+1)}+{CellsHelper.CellIndexToName(row, col+2)}";
Related
I am wanting to write a C# program to copy a specific range of cells below a specific keyword. The code will identify a keyword in Excel and then copy the values of all the cells below the keyword to copy into another range.
I am using Aspose. I attempted to write code to find the keyword and can successfully return the cell the keyword is located in. What I am trying to figure out how to do is copy the range specifically below any keyword into another range. I can successfully copy one range to another range but cannot do it from below a specific keyword.
Cells cellsOne = worksheet.Cells;
FindOptions findOptions = new FindOptions();
findOptions.LookAtType = LookAtType.StartWith;
Cell cell = cellsOne.Find("Accounting", null, findOptions);
//Printing the name of the cell found after searching worksheet
Console.WriteLine("Name of the cell containing String: " + cell.Name);
//if cell is found/value is returned
if (cell.Name.Contains("Accounting"))
{
//return cell value ?
//copy all below values (will need the cell keyword is in to do that)
//paste below values into specific columns
//doing it manually
Aspose.Cells.Range range1 = cellsOne.CreateRange("A2:A10");
Aspose.Cells.Range range2 = cellsOne.CreateRange("B28:B34");
range1.Copy(range2);
}
I have visited Aspose website but am struggling to copy a range BELOW a specific keyword. Thank you.
I don't know how to do it in C#, but I would advise you to find the cell which has the value, then in your "Range" statement below you could start at that cell you found before.
For example,
Cell cell = cellsOne.Find("Accounting", null, findOptions).Row;
You get for example "320", then your next line goes to that 320. Using your code:
Aspose.Cells.Range range1 = cellsOne.CreateRange("A2:A10");
Aspose.Cells.Range range2 = cellsOne.CreateRange("B" & cell & ":B34");
range1.Copy(range2);`
That's what I would do in VBA.
Hope it helps!
Checking your code segment a bit, I thought your destination range is "A2:A10". Your code needs some tweaks. See the updated (complete) sample code with comments to accomplish your task for your reference. I evaluated the source range (below the searched keyword) dynamically using CellsHelper static class.
e.g.
Sample code:
Workbook workbook = new Workbook("e:\\test2\\Book1.xlsx");
Worksheet worksheet = workbook.Worksheets[0];
Cells cellsOne = worksheet.Cells;
FindOptions findOptions = new FindOptions();
findOptions.LookAtType = LookAtType.StartWith;
Cell cell = cellsOne.Find("Accounting", null, findOptions);
//Printing the name of the cell found after searching worksheet
Console.WriteLine("Name of the cell containing String: " + cell.Name);
//if cell is found/value is returned
if (cell != null)
{
//I thought this is your destination range
Aspose.Cells.Range range1 = cellsOne.CreateRange("A2:A10");
//Evaluate the the next cell after (found) cell's row and column indices.
int startRow = cell.Row +1; //we add "1" to get the next in the same column
int startCol = cell.Column;
string startCell = CellsHelper.CellIndexToName(startRow, startCol);
//Set and evaluate your end cell for the range.
string endCell = CellsHelper.CellIndexToName(startRow + 6, startCol);
//Create your dynamic source range based on your startCell and endCell values
Aspose.Cells.Range range2 = cellsOne.CreateRange(startCell, endCell);
//Copy the source range to destination range
range1.Copy(range2);
}
workbook.Save("e:\\test2\\out1.xlsx");
Hope, this helps a bit.
You may also see the document on copying ranges for your further reference.
PS. I am working as Support developer/ Evangelist at Aspose.
I'm horrible with Math, however I'm working diligently to change that. I hate writing ugly, inefficient code. I'm currently developing a small hobby game and I need to be able to calculate adjacent tiles. I decided against using x,y coords and instead each tile has a unique id. I have the following functional code, however I'm struggling to find a way to make it not so ugly:
this.AdjacentTiles = new int[8];
this.AdjacentTiles[0] = (id - 9) - 1;
this.AdjacentTiles[1] = (id - 9);
this.AdjacentTiles[2] = (id - 9) + 1;
this.AdjacentTiles[3] = (id - 1);
this.AdjacentTiles[4] = (id + 1);
this.AdjacentTiles[5] = (id + 9) - 1;
this.AdjacentTiles[6] = (id + 9);
this.AdjacentTiles[7] = (id + 9) + 1;
I know it's possible to calculate all this within a single, tidy for loop, however I just can't wrap my head around how to do so. I don't necessarily want to be spoon fed, because like I said I'm trying to improve myself, I just need some direction.
Thank you very much.
The math is relatively easy, with something like (pseudo-code):
index = 0
for row = -1 to 1 inclusive: # prev/this/next row
for col = -1 to 1 inclusive: # prev/this/next col
if row != 0 or col != 0: # ignore current cell
array[index++] = id + row * 3 + col
That will set the elements to what you need. However, you may want to consider the edge cases such as when id is in the left-most column. In that case, there are no adjacent cells to the left, unless you've created an array with empty edges.
I'm trying to evaluate all the cell columns value from H to Y, I don't know the number of rows, I only know the number of cells.
What I tried:
var address = new ExcelAddress("H:Y");
var condition = ws.ConditionalFormatting.AddExpression(address);
condition.Style.Font.Color.Color = Color.Red;
condition.Formula = "IF(H2 < 25, 1, 0)";
As you can see I set as address H to Y and this working well but, there is a problem. I've an header on the first row H1, and I don't need to evaluate it, so I need to start from the second row H2 until Y2, but if I change the interval as: var address = new ExcelAddress("H2:Y2"); I get the color applied only on the second row and not for the other rows.
How can I manage this situation?
If I got your question well you can do the following:
First, know the rows count using:
int row = workSheet.Dimension.End.Row;
Then you can achieve what you want using the following address:
var address = new ExcelAddress($"H2:Y{rows}");
After much gnashing of teeth, &c, I was able to achieve a modicum of success with generating a PivotTable. It now sports both a "row filter" (on the Descriptions) and a "column filter" (on the "month year" columns), like so:
This is accomplished with the following code:
var pch = _xlBook.PivotCaches();
Range sourceData = _xlBook.Worksheets["PivotData"].Range["A1:G318"]; // TODO: Make range dynamic
PivotCache pc = pch.Create(XlPivotTableSourceType.xlDatabase, sourceData);
PivotTable pvt = pc.CreatePivotTable(_xlPivotTableSheet.Range["A8"], "PivotTable");
pvt.PivotFields("Description").Orientation = XlPivotFieldOrientation.xlRowField;
pvt.PivotFields("MonthYr").Orientation = XlPivotFieldOrientation.xlColumnField;
The source data ("PivotData") looks like this:
What the PivotTable should look like, when all is said and coded, is this:
What do I need to do to achieve this look (this data, and in these locations)? I reckon part of it is has to do with the assignments of more XlPivotFieldOrientation values to more PivotFields, but don't know just what they should be.
And first but possibly least (but still important), "Row Labels" should say "Description" and "Column Labels" should say "Month"
UPDATE
I am trying to add, piece by piece, the code needed to make the various columns display. I tried this:
pvt.AddDataField(pvt.PivotFields("TotalQty"), "Total Packages", XlConsolidationFunction.xlSum).NumberFormat = "###,##0";
...hoping that the data from the source data's "TotalQty" column would display in a column headed "Total Packages"
It does indeed display, but the label "Total Packages", appears in an odd/out-of-the-way place:
How can I get the "Total Packages" label to display where it does in the "model" screenshot?
UPDATE 2
Hambone mentioned my TODO in his answer; I got back to it and made that dynamic this way:
int rowsUsed = _xlBook.Worksheets["PivotData"].UsedRange.Rows.Count;
int colsUsed = _xlBook.Worksheets["PivotData"].UsedRange.Columns.Count;
string colsUsedAsAlpha = GetExcelColumnName(colsUsed);
string endRange = string.Format("{0}{1}", colsUsedAsAlpha, rowsUsed);
Range sourceData = _xlBook.Worksheets["PivotData"].Range[string.Format("A1:{0}",
endRange)];
// Pass "1", get "A", etc.; from http://stackoverflow.com/questions/181596/how-
to-convert-a-column-number-eg-127-into-an-excel-column-eg-aa
public static string GetExcelColumnName(int columnNumber)
{
int dividend = columnNumber;
string columnName = String.Empty;
while (dividend > 0)
{
var modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (dividend - modulo) / 26;
}
return columnName;
}
BTW, I can't find my bronze badges.
A couple of things... you didn't ask this specifically, but in your code you had a "TODO" as making the range dynamic. A really nice way to do this is to convert your range to a table. Once you do this, you can pass the table instead of the range. The table, as you probably know from previous posts would be:
ListObject tab = _xlPivotTableSheet.ListObjects["Table1"];
PivotCache pc = pch.Create(XlPivotTableSourceType.xlDatabase, tab);
If you do this, boundaries don't matter -- the table goes all as one data source.
Back to your issue at hand. You added your data fields okay:
pvt.AddDataField(pvt.PivotFields("TotalQty"), "Total Packages",
XlConsolidationFunction.xlSum);
pvt.AddDataField(pvt.PivotFields("TotalSales"), "Total Purchases",
XlConsolidationFunction.xlSum);
The calculated field is a bit more tricky. I didn't quite understand the calculation for "% of total," but for illustration purposes this is how you would do average price:
PivotField avg = pvt.CalculatedFields().Add("Average Price", "=TotalSales/TotalQty", true);
avg.Orientation = XlPivotFieldOrientation.xlDataField;
And you're right -- the orientation is not what you expect. This setting is done at the pivot table level -- it's either rows or columns. To get the view you're looking for you would change it from the default columns to rows:
pvt.DataPivotField.Orientation = XlPivotFieldOrientation.xlRowField;
Once you do this, I think the pivot table will look more the way you expect.
This is how to merge cell using ClosedXML based on the documentation.
worksheet.Range("B2:D3").Row(1).Merge();
My problem is my column count is dynamic, I can't set column letter value to merge because i will based the merging of cell on my gridview column count.
Anyone who can help me to merge cell using closedXML?
I do it this way
int row = 1;
int col = 1;
worksheet.Range(worksheet.Cell(row, col++), worksheet.Cell(row, col++)).Merge();
Build up the range as a variable based on your column count and pass the variable to the Range method. You don't need to HAVE to pass a hard coded value.
Have you tried the code below?
worksheet.Cell("A1").Value = "Title";
var range = worksheet.Range("A1:F1");
range.Merge().Style.Font.SetBold().Font.FontSize = 16;
It works for me.