I am creating a MS Word document entirely through C# in VS 2008. I am trying to insert a page break, and when the page break is inserted, instead of inserting it and adding a new page at the bottom, it is inserting a break and adding a page at the top. This results in the first page being the last page.
Here is the code for inserting the page break:
start = 0;
end = 0;
Word.Range rngDoc = Range(ref start, ref end);
rngDoc.Collapse(ref CollapseEnd);
rngDoc.InsertBreak(ref pageBreak);
rngDoc.Collapse(ref CollapseEnd);
Also, each page is consumed by a table, if this helps with the diagnostics
InsertBreak never inserts after the selection. Note the MSDN remarks:
When you insert a page or column break, the selection is replaced by the break. If you don't want to replace the selection, use the Collapse method before using the InsertBreak method. When you insert a section break, the break is inserted immediately preceding the Selection object.
(My emphasis.) To get a break at the end of the page, I think you'll have to select nothing (as you are here) at the end of the document.
I can't recall whether the Document has its own range. Can you just get an all-encompassing range from myDoc.Characters?
If not, the first thing I would try is
start = int.MaxValue;
end = int.MaxValue;
If that doesn't work, you might resort to ComputeStatistics(). Something like this:
WdStatistic stats = WdStatistic.wdStatisticCharacters;
var chars = myDoc.ComputeStatistics(stats, false);
And then create your range from that value. Wish I could help more, but it's been a while for me. Good luck!
You need to set the rngDoc to insert at the end of ActiveDocument.Range. Here's some VBA that would be easy to port to C# that does this.
Sub InsertNewPageAtEndofDoc()
Dim rng As Range
Dim adRange As Range
Set adRange = ActiveDocument.Range
Set rng = ActiveDocument.Range(adRange.Start, adRange.End)
rng.Collapse (wdCollapseEnd)
rng.InsertBreak (wdPageBreak)
rng.Collapse (wdCollapseEnd)
End Sub
I used Todds approach but in vb.net:
Private Sub InsertNewPageAtEndofDoc(app As Word.Application)
Dim rng As Word.Range
Dim adRange As Word.Range
adRange = app.ActiveDocument.Range
rng = app.ActiveDocument.Range(adRange.Start, adRange.End)
rng.Collapse(Word.WdCollapseDirection.wdCollapseEnd)
rng.InsertBreak(Word.WdBreakType.wdPageBreak)
rng.Collapse(Word.WdCollapseDirection.wdCollapseEnd)
End Sub
I'm not an expert at this so this is just a guess, it looks a bit strange though that you're selecting a range from 0 to 0 and then collapsing that. A range from 0 to 0 sounds like it'll give you the very start of the document, I'd guess that you need to select the last bit of the document instead but not sure how to do that.
Related
I'm creating a Microsoft Word 365 Add-in where I need to be able to add and remove inline shapes. I currently have the following test code:
bookmark.Range.InlineShapes.AddPicture("c:\\temp\\test.png");
And although the InlineShape gets added to the page, and seemingly on the correct position, i.e. within the provided bookmark, the bookmark.Range.InlineShapes collection stays empty:
Assert.IsTrue(bookmark.Range.InlineShapes.Count > 0); // This fails
As far as I can see, the shape is actually not added to the range, but directly after it.
This behavior is odd and causes problems in my situation, where I need to be able to iterate the inline shapes of the bookmark later on, especially to be able to remove (toggle) the image again. But without the inline shape as part of the bookmark
What am I doing wrong and how can I fix this, in such way that the inline shape is becoming part of the bookmark again?
That's because your code doesn't insert the shape within the bookmark. This is a quite common misunderstanding. You need to first point a Range object to the bookmark range. Then, add the InlineShape to your Range object's range, then extend the range by one character. Finally, re-apply the bookmark to the Range object's range.
Following is a VBA example:
Sub Demo()
Dim wdRng As Word.Range, BkMkNm As String
BkMkNm = "MyBookmark"
With ActiveDocument
'Confirm that the bookmark exists
If .Bookmarks.Exists(BkMkNm) Then
Set wdRng = .Bookmarks(BkMkNm).Range
'Delete existing contents
wdRng.Text = vbNullString
'Insert Pic
.Range.InlineShapes.AddPicture FileName:="PictureFullName", Range:=wdRng
'Extend Range
wdRng.End = wdRng.End + 1
'Re-apply bookmark
.Bookmarks.Add BkMkNm, wdRng
End If
End With
End Sub
I have a MS Word add-in that needs to extract text from a range of text based solely on its formatting: in my case in particular, if the text is underlined or struck through, the range of characters/words that are underlined or struck through need to be found so that I can keep track of them.
My first idea was to use Range.Find, as is outlined here, but that won't work when I have no idea what the string is that I'm looking for:
var rng = doc.Range(someStartRange, someEndRange);
rng.Find.Forward = true;
rng.Find.Format = true;
// I removed this line in favor of putting it inside Execute()
//rng.Find.Text = "";
rng.Find.Font.Underline = WdUnderline.wdUnderlineSingle;
// this works
rng.Find.Execute("");
int foundNumber = 0;
while (rng.Find.Found)
{
foundNumber++;
// this needed to be added as well, as per the link above
rng.Find.Execute("");
}
MessageBox.Show("Underlined strings found: " + foundNumber.ToString());
I would happily parse the text myself, but am not sure how to do this while still knowing the formatting. Thanks in advance for any ideas.
EDIT:
I changed my code to fix the find underline issue, and with that change the while loop never terminates. More specifically, rng.Find.Found finds the underlined text, but it finds the same text over and over, and never terminates.
EDIT 2:
Once I added the additional Execute() call inside the while loop, the find functioned as needed.
You need
rng.Find.Font.Underline = wdUnderline.wdUnderlineSingle;
(At the moment you are setting the formatting for the specified rng, rather than the formatting for the Find)
I'm developing C# addin for MS Word. I can grab all words of current document - it's something like that:
app = (Word._Application )Application; // Application object comes on addin's connection
foreach(Word.Word word in app.Application.Words)
{
doSmth(word);
}
My question, is how to grab all words not from entire document but from current active(visible for user) page?
In other words, I need to define active page/paragraph of app.Application.ActiveDocument and do something with "active" words.
Interesting question. [See update at end]
Word's object model doesn't really have a "page" object, because the pagination of the document is constantly changing as you add and remove content (or change the font size, the paper size, etc.). So, there is no "ActiveDocument.Pages(1)" sort of thing.
What's more, there's no easy way to tell what page is currently displayed. In part, that's because the user doesn't necessarily see only one page at a time. He may be viewing the end of one page and the start of the next, or several pages may be displayed - depending on his view settings.
If I can make the question slightly easier, then perhaps I can answer it in a way that helps you. Let me re-define "current active (visible for user) page" as the page where the selection is. (Actually, since the selection can span several pages, let's define it as "the page where the active end of the selection is").
I'll also answer using VBA because it's easier to play around with it in the VBA immediate window, and it's trivial to convert to C# when you need to (it's the same object model, after all).
Word's Selection object has the properties of a Range, and if you simply wanted all the selected words, then this would be trivial (Selection.Words!). However, if we want all the words on that page, then we need to work a little harder.
First, let's find out what page the (start of the) selection is on. For this, we can use the Information method:
pageNumber = Selection.Information(wdActiveEndPageNumber)
So now we know what page we're interested in. We now need to get a Range object that includes all the text on that page. We need to do this in two steps - by finding first the start and then the end of that range.
To find the start of the range, we can use the Goto function, which returns a Range object representing the start of a specified item:
startOfRange = ActiveDocument.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, pageNumber).Start
The end of the range is either the start of the next page (minus one character, but let's not quibble), or the end of the document (if we're on the last page):
If pageNumber = ActiveDocument.Content.Information(wdNumberOfPagesInDocument) Then
endOfRange = ActiveDocument.Content.End
Else
endOfRange = ActiveDocument.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, pageNumber + 1).Start
End If
Now we can construct a Range object encompassing all the text on the page:
Set pageRange = ActiveDocument.Range(startOfRange, endOfRange)
... and from there we can get the words:
Set words = pageRange.Words
Here is a short VBA macro that uses the above technique to report the number of words on the active page:
Sub Test()
Dim pageNumber As Integer
Dim startOfRange As Integer
Dim endOfRange As Integer
Dim pageRange As Range
pageNumber = Selection.Information(wdActiveEndPageNumber)
startOfRange = ActiveDocument.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, pageNumber).Start
If pageNumber = ActiveDocument.Content.Information(wdNumberOfPagesInDocument) Then
endOfRange = ActiveDocument.Content.End
Else
endOfRange = ActiveDocument.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, pageNumber + 1).Start
End If
Set pageRange = ActiveDocument.Range(startOfRange, endOfRange)
MsgBox pageRange.Words.Count
End Sub
UPDATE
OK, it turns out that there's a much easier way to do this. Word has a "special bookmark" that points to the text on the current page, so this will do the same as all that code above:
words = ActiveDocument.Bookmarks("\page").Range.Words
Which would be potentially a best way to enumerate or iterate or simply look for empty cells or cells with specific data structure in Excel, and later once you find it do some processing on it.
I tired Range, Value, Value2, etc but it takes fairly long time when Excel Sheet is considerably larger. I believe there must be some other efficient way.
It would be nice, if you can show some example snippet.
The answer is relativley simple: get the array in one batch from excel (search SO for a how to) - test the values of the erray for empty cells and then acess only the empty cells in excel.
It is somewhat cumbersome, but the fastes way because iterating each cell is vastly slower than simply getting all data in a batch.
To find blank cells, use the .SpecialCells method of a range object.
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.range.specialcells(v=office.11).aspx
The .specialCells method returns a range object of the matching criteria (i.e., xlCellTypeVisible, xlCellTypeBlanks, etc.). You can then iterate of this range to perform your formatting, etc.
Update I'm not a C# programmer, but I can show you how I would do this in VBA. Assuming interop exposes most/all of the same methods and functionality, you should hopefully be able to translate this for your purposes.
Sub ColorVisibles()
Dim rng As Range
Dim rngBlanks As Range
Dim blanksExist As Boolean
'define your range
Set rng = Range("A1:AA300")
'check to make sure there are blank cells in the range:
blanksExist = Application.WorksheetFunction.CountBlank(rng) > 0
If blanksExist Then
Set rngBlanks = rng.SpecialCells(xlCellTypeBlanks)
rngBlanks.Interior.Color = vbYellow
Else:
MsgBox "No blank cells exist in the specified range.", vbInformation
End If
End Sub
I want to read the notes of a PowerPoint Slide in C#.
Following Snippet works for me.
slide.NotesPage.Shapes[2].TextFrame.TextRange.Text
However, this doesn't work for some presentations.
Then it throws an "Out of range" exception.
What ist the meaning of index 2? Are there any alternative to do this?
You can't assume that the notes text placeholder will be at any specific index or even that it'll have a specific name. Here's a simple function in VBA that returns the notes text placeholder for a slide:
Function NotesTextPlaceholder(oSl As Slide) As Shape
Dim osh As Shape
For Each osh In oSl.NotesPage.Shapes
If osh.Type = msoPlaceholder Then
If osh.PlaceholderFormat.Type = ppPlaceholderBody Then
' we found it
Set NotesTextPlaceholder = osh
Exit Function
End If
End If
Next
End Function
It means that you are trying to access the third element of the slide.NotesPage.Shapes collection. If the collection has 2 elements or less, the exception is thrown because the element at the specified index 2 could not be accessed since it doesn't exist — you simply cannot retrieve a collection's third element if it doesn't have one.
(The index is zero-based, meaning that the first element is given the index 0, the second one is given the index 1 and so on. Thus, the greatest possible index of a collection with N elements is N-1.)
It's dangerous to try and access an index object without checking if it exists first, since this might throw exceptions. You can check if the slide has notes with the HasNotesPage property of the slide object:
if(slide.HasNotesPage == Microsoft.Office.Core.MsoTriState.msoTrue)
{
}
If you want to get all the notes at once, you might want to use NotesPage property to retrieve a range with all notes.