I can create PowerPoint tables with many cells, but the default font (28 points) is too large. Without access to the user's PowerPoint template, how can I set the font for a table I have created? All I can do at the moment is:
Create the table.
Fill every cell with a single space because otherwise font changes are ignored
(though it works using the PowerPoint UI).
Create a range containing all cells and set font:
List<string> names = new List<string>();
foreach (PowerPoint.Column column in table.Columns)
foreach (PowerPoint.Cell cell in column.Cells) {
cell.Shape.TextFrame.TextRange.Text = "";
names.Add(cell.Shape.Name);
}
PowerPoint.ShapeRange range = ppt.ActivePresentation.Slides.Item(1).Shapes.Range(names.ToArray());
range.TextFrame.TextRange.Font.Size = 12;
I am using C# to control PowerPoint 2003..2010 via its COM API. My experiments have been with PowerPoint 2003.
In PPT 2010, this works w/o having to enter text into the cells (vba, but should be easy enough to translate). For demo purposes, I'm working with the currently selected table; substitute any other reference to the table as oTbl:
Dim oTbl As Table
Dim lCol As Long
Dim lRow As Long
Set oTbl = ActiveWindow.Selection.ShapeRange(1).Table
With oTbl
For lCol = 1 To .Columns.Count
For lRow = 1 To .Rows.Count
.Cell(lRow, lCol).Shape.TextFrame.TextRange.Font.Size = 28
Next
Next
End With
In PPT2003, as you've seen, you need to add text to the cell for the formatting to "take" (which seriously slows things down, unfortunately).
Because there are other instances in PPT automation where you may need to temporarily fill in text then delete it, you might want to write a more general routine that you can pass a shape to.
With Shape.TextFrame.TextRange
If Len(.Text) = 0 Then
.Text = "!##$%" ' or some other lunatic string not likely to be found in nature
End If
End With
"Pre-treat" the shape with this, do whatever's necessary, then "Un-treat" it with a companion routine that tests to see if the text = "!##$%" and sets it back to "" if so, leaves it alone otherwise.
Related
I would like to convert a xlsx worksheet to HTML format. The worksheet has fewer Worksheet.MaxRows than the specified PageSetup.PrintArea. Therefore when converting to HTML I would like to include empty rows beyond the MaxRows in the HTML output. When changing PageSetup.PrintArea range to be lower than MaxRows (e.g. only print rows 3-15, when the final row in the excel file is 20). But when I try to increase the range (print rows 3-25, when the final row is 20), it only prints up to row 20, instead of including the extra 5 empty rows.
I am using HtmlSaveOptions.ExportPrintAreaOnly to specify the range to print.
Here's a snippet of my code:
FilePath htmlFilePath = output.GetDirPath() + output.GetFileNameWithoutExtension() + ".html";
var htmlSaveOptions = new HtmlSaveOptions
{
ExportHiddenWorksheet = false,
ExportActiveWorksheetOnly = true,
ExportPrintAreaOnly = true
};
sheet.Workbook.Save(htmlFilePath, htmlSaveOptions);
Another option I could go for is simply inserting the rows manually and removing their styling (though I seem to be having an issue applying styling but that is for another question post), but I was wondering if there is a neater, in-built way this could be done using HtmlSaveOptions.
Since you have set ExportPrintAreaOnly attribute to "true". so Aspose.Cells would print up to maximum row (data) only and do not include empty or null rows (even you have specified increased printable area). For your specific case, please either remove or comment out the following line of code, it will work ok.
ExportPrintAreaOnly = true
Moreover, if you still persist with using the above attribute for your needs, a workaround can be: you may add an empty string into your last row cell (e.g. sheet.Cells["A25"].PutValue(" ");)
PS. I am working as Support developer/ Evangelist at Aspose.
I am populating a ListObject with data from a database, and am pre-formatting ListColumns which come from VarChar (& similar) as Text before inserting the data.
This works well, but some affected cells now are showing the 'Number Stored As Text' error.
The answer https://stackoverflow.com/a/21869098/1281429 suppresses the error correctly, but requires looping through all cells (as it is not possible to perform the action on a range).
Unfortunately for large ranges this is unacceptably slow.
(n.b. - if you do it manually in Excel it's lightning fast)
Here is a code snippet in C# (for a particular column):
var columnDataRange = listColumn.DataBodyRange
var cells = columnDataRange.Cells;
for (var i = 1; i < cells.Count; i++)
{
InteropExcel.Range cell = cells[i, 1];
if (cell.Count > 1) break;
if (cell.Errors != null)
{
var item = cell.Errors.Item[InteropExcel.XlErrorChecks.xlNumberAsText];
item.Ignore = true;
}
}
Does anyone know of a faster way of doing this?
(Or, more generally, a faster way of iterating through cells in a range?)
Hope someone can help - thanks.
Edit: this is a VSTO Application-Level add-in for Excel 2010/2013.
Just to be sure - you are going from a database to an Excel export? Are you creating a new, clean spreadsheet or overwriting existing data in an existing spreadsheet?
If you are overwriting data in an existing spreadsheet, I would first clear the columns and format the columns in Excel (programmatically of course). It is likely old data and new data going into the same space are causing type issues.
So something like:
thisExcel.xlWorksheet.Range[yourrange].Value = ""
thisExcel.xlWorksheet.Range[yourrange].NumberFormat = choseyourformat
http://msdn.microsoft.com/en-us/library/office/ff196401(v=office.15).aspx
You should be able to apply that to a larger area.
How do I set the source data of an excel interop chart to several entire rows?
I have a .csv file that is created by my program to display some results that are produced. For the sake of simplicity let's say these results and chart are displayed like this: (which is exactly how I want it to be)
Now the problem I am having is that the number of people is variable. So I really need to access the entire rows data.
Right now, I am doing this:
var range = worksheet.get_range("A1","D3");
xlExcel.ActiveChart.SetSourceData(range);
and this works great if you only have three Persons, but I need to access the entire row of data.
So to restate my question, how can I set the source data of my chart to several entire rows?
I tried looking here but couldn't seem to make that work with rows instead of columns.
var range = worksheet.get_range("A1").CurrentRegion;
xlExcel.ActiveChart.SetSourceData(range);
EDIT: I am assuming that the cells in the data region won't be blank.
To test this,
1) place cursor on cell A1
2) press F5
3) click on "Special"
4) choose "Current Region" as option
5) click "OK"
This will select the cells surrounding A1 which are filled, which I believe is what you are looking for.
The translation of that in VBA code points to CurrentRegion property. I think, that should work.
Check Out the option Range.EntireRow I'm not 100% on how to expand that to a single range containing 3 entire rows, but it shouldn't be that difficult to accomplish.
Another thing you can do is scan to get the actual maximum column index you need (this is assuming that there are guaranteed to be no gaps in the names), then use that index as you declare your range.
Add Code
int c = 2;//column b
while(true)
{
if (String.IsNullOrEmpty(worksheet.GetRange(1,c).Value2))
{
c--;
break;
}
c++;
}
Take a column from A to D that you're sure has no empty cells.
Do some loop to find the first empty one in that column and it will be one after the last.
Range Cell = SHeet.Range["A1"]; //or another column you're sure there's no empty data
int LineOffset = 0;
while (Cell.Offset[LineOffset, 0].Value != "") //maybe you should cast the left side to string, not sure.
{
LineOffset++;
}
int LastLine = LineOffset - 1;
Then you can get Range[Sheet.Cells[1,1], Sheet.Cells[LastLine, 4]]
Out of the box here, but why not transpose the data? Three columns for Name, Height, Weight. Convert this from an ordinary range to a Table.
When any formula, including a chart's SERIES formula references a column of a table, it always references that column, no matter how long the table gets. Add another person (another row) and the chart displays the data with the added person. Remove a few people, and the chart adjusts without leaving blanks at the end.
This is illustrated in my tutorial, Easy Dynamic Charts Using Lists or Tables.
Using C# 2010 I need to open a word 2010 template, search for a bookmark and insert a table there. Actually it should be a 'three-part-table': one row with two columns, after that multiple rows with five columns and finall three rows as single columns. And it should look as one table without paragraphs or empty lines between.
My experience with word automation is quite limited. I can find examples how to create a table at a bookmark - no problem so far - but how can I add a new table immediately after the one before...
Thanks a lot for any help!!
This should do the trick. You don't need multiple tables in order to have different columns; Word lets you have a single table where the first row has 2 columns, the next 3 rows have 5 columns, and the last 3 rows only have 1 column. (You didn't say how many rows you needed with five columns, so I just went with 3.)
//Be sure to add this reference:
//Project>Add Reference>.NET tab>Microsoft.Office.Interop.Word
//open Word App
Microsoft.Office.Interop.Word.Application msWord = new Microsoft.Office.Interop.Word.Application();
//make it visible or it'll stay running in the background
msWord.Visible = true;
//open a new document based on the Word template.
//You shouldn't open the template directly using msWord.Documents.Open(path) unless you want to edit the template itself.
Microsoft.Office.Interop.Word.Document wordDoc = msWord.Documents.Add(#"c:\MyTemplate.dotx");
//find the bookmark
string bookmarkName = "BookmarkToFind";
if (wordDoc.Bookmarks.Exists(bookmarkName))
{
Microsoft.Office.Interop.Word.Bookmark bk = wordDoc.Bookmarks[bookmarkName];
//set the document's range to immediately after the bookmark.
//If you want to add the table *into* the bookmark, it needs to be done differently.
//This page has a good explanation of the differences between adding to the bookmark's range vs adding after the bookmark's range.
//http://gregmaxey.mvps.org/word_tip_pages/insert_text_at_or_in_bookmark.html
//It's a little more hassle because you have to re-add the bookmark after inserting into it,
//so inserting after the bookmark is usually fine less you're going to be inserting text programmatically at the same bookmark a second time.
Microsoft.Office.Interop.Word.Range rng = wordDoc.Range(bk.Range.End, bk.Range.End);
//create a table with 8 rows and 5 columns into the range.
Microsoft.Office.Interop.Word.Table tbl = wordDoc.Tables.Add(rng, 8, 5);
//set the table's borders.
tbl.Borders.InsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle;
tbl.Borders.OutsideLineStyle = Microsoft.Office.Interop.Word.WdLineStyle.wdLineStyleSingle;
//merge the cells in the first row down to 2 columns (Word's cells start at 1, not at 0).
tbl.Cell(1, 1).Merge(tbl.Cell(1, 3));
//distribute the columns evenly
tbl.Rows[1].Select();
msWord.Selection.Cells.DistributeWidth();
//rows 2-5 already have 5 columns so don't touch them.
//merge rows 6-8 into single-columns rows.
for (int x = 6; x < 9; x++)
{
tbl.Cell(x,1).Merge(tbl.Cell(x,5));
}
//put the cursor in the table's first cell.
rng=wordDoc.Range(tbl.Cell(1,1).Range.Start, tbl.Cell(1,1).Range.Start);
rng.Select();
I'm just starting to work on a project that uses SpreadsheetGear. CopyFromDataTable is used to get all the data from a database. There are a lot of fields to work with; we're formatting them, hiding certain ranges if they're empty, etc. As an example:
SpreadsheetGear.IRange cells = worksheet.Cells;
cells["G1"].EntireColumn.NumberFormat = "m/d/yyyy";
So if the the columns are rearranged or one is removed, it appears I'd have to go back and adjust all of the hardcoded values to reflect the cell shift. Is there any way to reference the column by its first cell's name to possibly make things more modular?
I found this, but it still requires hardcoding the column.
A great alternative for the spreadsheetgear is http://officehelper.codeplex.com. Using this library you can create a template xlsx, where you do all your formatting and then you can use parts from this "template" document to create your final, to be reported xlsx files, whitout the need of programtically setting the number format, like in your example.
One possibility is to search for the heading/fieldnames
Sub test()
Dim strFieldName As String
Dim rngHeading As Range
With Sheet1
strFieldName = "Heading 3" 'find Heading Name
With .Rows("1:1") 'search row 1 headings
Set rngHeading = .Find(What:=strFieldName, LookIn:=xlValues, lookAt:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
'check if found
If Not rngHeading Is Nothing Then
MsgBox rngHeading.Address
MsgBox rngHeading.Row
MsgBox rngHeading.Column
rngHeading.EntireColumn.NumberFormat = "m/d/yyyy"
End If
End With
End With
End Sub