MS Word function to append a Range to a document - vba

Suppose I want to add some text at the end of a document and immediately access
it as a Range object so I can set some properties of it without affecting the preceding text. Ideally, the Range.InsertAfter
method would return a Range object which would be perfect for this, but it doesn't.
It irks me that Word must know perfectly well what range defines the result of calling InsertAfter, but on the face of it, I need to calculate it "after the fact" from the length of the inserted text, or in some other way.
So I've devised a simple-minded work-around. In pseudo-code (actually it's Delphi
code but I hope that won't discourage VBA answers) what I do is this
ARange := Document.Range
ARange.Text := 'AAA'
AEnd := ARange.End - 1 // AEnd is an integer
ARange.SetRange(AEnd, AEnd)
ARange.Text := 'XXX'
ARange.Bold := True
and it seems that I can carry on indefinitely adding blocks of text to the end
of a document by repeating the second block of code.
The line
ARange.SetRange(AEnd, AEnd)
as I understand it, seems to construct a new Range at the end of the existing one (unlike calling Collapse on an existing range),
and works fine for the simple test cases I've tried. But it leaves me wondering
whether I'm missing a trick somewhere. Is there a more direct way to append a range
to a document and get a reference to it?
PS: I should have been a bit clearer that I'm trying to do this without using the Selection object (for a variety of reasons, including the fact that you can only have one of them at at time).

There are various ways to get the Range at the end of the document. You've discovered one, but as you say, it's somewhat circuitous. My preference:
Word.Range rngEndOfDoc = Document.Content;
//Content returns a Range object and is a property, not a method like Range()
rngEndOfDoc.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
rngEndOfDoc.Text = "Text at the end of the document";
Collapsing the Range is conceptually like pressing the Right (or Left) arrow key when you have a selection. So rngEndOfDoc becomes a "point" rather than containing the entire content of the main body of the document.
FWIW I can never think of a situation when I'd use EndKey for this (emulate user actions) and I'd only change the Selection when I want to leave the user at the end of the document so that he can start typing at that location.

With thanks for the three admirable answers from others, I thought I would add my
own. The following are two versions of the function that I started out wishing Word provided
natively, returning an appended range.
The first version uses the MS Word objects in the MS Word Type Library import
unit that traditionally comes with Delphi (e.g. the Word2000.Pas one), and uses "early binding" automation,
while the second version does the same thing using late binding.
function AppendRange(InputRange : Range) : Range;
var
Direction : OleVariant;
begin
Result := InputRange;
Direction := wdCollapseEnd;
Result.Collapse(Direction);
end;
function AppendRangeLB(InputRange : OleVariant) : OleVariant;
begin
Result := InputRange;
Result.Collapse(wdCollapseEnd);
end;
Usage is e.g.
AppendedRange := AppendRange(ExistingRange);
AppendedRange.Text := 'YYY';

Think of the Document.Range as the union of all the possible ranges in the main document (i.e. apart from headings, footers, floating stuff etc.). It always starts before the first character and ends after the last character (that, in my experience, is always a hidden paragraph mark). It is not possible to define another range that is after the current Document.Range or, conceptually "append" anything after Document.Range, because there is no "after" the Document.Range (it always reaches to the end).
Thus, to extend a document with new text or other inline objects, you have to insert them at the end of the Document.Range - actually just before the end, as that last, hidden, paragraph mark must still be the final character of the document. This is just what you are doing in your pseudocode.
In VBA, an empty range at the end of the document can be referenced with
Document.Range(Document.Range.End-1, Document.Range.End-1)
Attempting to define it at Document.Range.End actually raises an error, as it points to after that last (hidden) character - and that is out of the Document - that is why we need the "-1".
After getting this range we may fill it with stuff - expanding it and, logically, the Document.Range. For example, to add text at the end of the active document, one would write
ActiveDocument.Range(ActiveDocument.Range.End-1, ActiveDocument.Range.End-1).Text = "New Text"
Notice that the same "final" empty range can be reached with:
Document.Bookmarks("\EndOfDoc").Range

The following works for me in Word 15 (Office 365) using the Word 2010 type library (I'm using early binding, but it should work the same with late binding as well). Word is a TWordApplication, and aRange is a WordRange.
Word.Selection.EndKey(wdStory, wdMove);
aRange := Word.Selection.Range;
aRange.Text := 'This is new text added at the end.';

Related

cannot get value from a cell in libreoffice 6.4.3.2 basic

I am new to libreoffice basic, i have experience with VBA but this libreoffice is different.
I just want to get cell value but it always return zero value to me while the actuall cell can be text or number.
Here is a partial of my simple code.
Sub test_moved()
Dim Doc As Object
'worksheet
Dim sh_village As Object
Dim sh_cbc As Object
sh_village = ThisComponent.CurrentController.getActiveSheet()
'sh_village = Doc.Sheets.getByName("VillageFinal")
'sh_village = Doc.Sheets(1)
Msgbox(sh_village.getCellrangeByName("B2").getValue())
Msgbox(sh_village.getCellrangeByName("B2").Value)
Msgbox(sh_village.getCellByPosition(1,1).Value)
msgbox("The process is completed.")
End Sub
Do we need to do prior task before start coding?
The code works correctly for numeric values. However, for strings, including strings that look like a number, it will display 0 because there is no numeric value.
What you probably want instead is:
MsgBox(sh_village.getCellRangeByName("B2").getString())
Also check out Format -> Cells -> Number to see how the data is displayed in the cell. And be on the lookout for a single quote at the front of the value in the formula bar (for example '42), because that means it is a string. Delete the quote to make it a number.
i have experience with VBA but this libreoffice is different.
Yes, LibreOffice Basic is a different language from VBA and the LibreOffice API is very different from the MS Office API. Knowing that will help you use it more effectively. If possible, avoid Option Compatible, because it won't fix most problems and will only muddy the waters.

Convert automatic numbering and bullets to plain text

I have a word document with automatic numbering and bulleting.
I have selected the text where I need to convert automating numbering and/or bulleting to normal text.
In addition I need to keep both the formatting and numbers/bullets of the selected text.
What I have already tried:
cut the paragraphs and special pasted them (but it breaks formatting);
unpressed the "numbering"/"bulleting" button (but it erases all numbers and bullets);
used VBA-macro (but it returns an error):
Code (error, method or data member not found):
Sub convertNumbersAndBulletsToText()
Selection.ConvertNumbersToText
End Sub
What would you recommend me to do in order to keep both formatting and numbers/bullets?
You have practically done everything yourself!
This code will work:
Sub convertNumbersAndBulletsToText()
Selection.Range.ListFormat.ConvertNumbersToText
End Sub
Your example returns error because ConvertNumbersToText method doesn't work with Selection. It works with Range!
(look here: Change selected automatic numbered list to plain text in word)
Beware!
If you want to carry out many changes, you may find it easier to make them with ActiveDocument (look below).
But if want to do it manually (or through a loop),
then you'd better loop from the last element you want to convert till the first one
(not vice versa, because auto-numbers would then increment by one all the time)!
Small Tips
Personally I would recommend you to use this code instead:
Sub convertNumbersAndBulletsToText()
Dim myRange As Range
Set myRange = Selection.Range
myRange.ListFormat.ConvertNumbersToText
End Sub
Why this one? It is a little bit more flexible! Instead of Selection.Range you could use any other type of Range (ActiveDocument, ActiveDocument.Paragraphs, myRange.SetRange etc)
Here are some links from msdn to give you basic examples of Ranges: 1) Range Object (Word) (msdn), 2)
Range.SetRange Method (Word) (msdn).
Just for your information, you don't need to save VBA if you don't want to. You can use Immediate Window to launch VBA.
Press alt+f11 (VBA-editor), then ctrl+g (Immediate Window).
Paste the code bellow, press enter.
VoilĂ !
Code (for Immediate Window):
ActiveDocument.ConvertNumbersToText
(It converts auto-numbers and auto-bullets to normal numbers and bullets everywhere in ActiveDocument).
The result of any VBA here would be number+tab+text. If you want to have number+space+text you can:
either at the very end replace (press ctrl+h) this one .^t (dot and tab) for . (dot and whitespace),
or at the very beginning 1) select the list, 2) right click on it, 3) click "Adjuct list idents", 4) click "Follow number with: Space". (Look here: Adjust the spacing for a single list item (support.office))
You may need to have a leading zero in (auto-)numbering, then you can press ctrl+f9, write SEQ MyList \# "000" inside curly brackets, press alt+f9 to finish (look here: Insert fields in Word (support.office)). But this goes beyond the question, though you may find word fields really useful in some cases.
To sum up:
You can replace both bullets and numbers for plain text in Word:
for Selection (look above);
for ActiveDocument (look above);
with a Range (examples in msdn);
with a loop (examples are welcomed). But bear in mind that you are to loop from the end of the document to the beginning.

VBA getcrossreferenceitems(wdRefTypeNumberedItem) Paragraph Cut Off?

I'm using excel vba to extract information from a word document.
In the word document, there are levels of numbered lists. For example:
1. ABC
1.1 DEF
1.1.1 ABCDEF
2. AAA
2.1 BBB
2.1.1. CCC
and I need to get the full context of each heading in each level and put them into an excel range, i.e. {"1.ABC", "1.1 DEF", "1.1.1 ABCDEF", "2. AAA", "2.1 BBB", "2.1.1. CCC"}
The function I use is:
For Each sec In objDoc.getcrossreferenceitems(wdRefTypeNumberedItem)
However, my headings are truncated if the headings are too long. For example, I have (random text is added for confidentiality reasons):
"5.2.11. Current References: As part of the evaluation process, XXX will conduct 2340AERTQ3493YR. When selecting ADT34534FDGSR, please ensure that they are AERA34AEFDS."
But only
5.2.11. Current References: As part of the evaluation process, XXX will conduct 234
is displayed, and the rest of the sentence is gone.
If anybody has an alternate solution, please let me know.
i confirm this behavior. A workeable albeit and elaborate solution is to scan the document for all numbered items which gives you the full text and then cross reference that result against the list returned by the GetCrossReferenceItems. There's quite some work involved but works and gives you the ability to create one list with referable Headings and NumberedItems, which is what I did to build a more user friendly alternative to Word's own implementation.
You'll have to match the formatting Word applies to the list returned by GetCrossReferenceItems, ie. the identation and removal of special characters.
Be careful with track changes. There is a bug in GetCrossReferenceItems which means that items (in my case headers) that have a tracked change at the beginning of the text are not returned by GetCrossReferenceItems but internally are still on the list so the index is offset. If the item in question is item 11, then GetCrossReferenceItems gives the item belonging to item 12 the item 11. A workaround is to accept all revisions before GetCrossReferenceItems and undo it after.
It's not easy but works.
I met a similar problem in MSWord. I found some paragraph's text are shorten in the following code
Sub bug()
items = ActiveDocument.GetCrossReferenceItems(wdRefTypeNumberedItem)
For idx = 1 To UBound(items)
MsgBox items(idx)
Next
End Sub
I have to use a some long solution( in Python, sorry. But is is easy to rewrite in VBA):
varHeadings = []
for par in objDoc.Paragraph:
if par.Range.ListFormat.ListType == win32com.client.constants.wdListOutlineNumbering:
idx = par.Range.ListFormat.ListString
txt = par.Range.Text.strip('\n').strip('\r')
varHeadings.append('%s%s' % (idx, par.Range.Text))
which does work. However, as I have said, it is some tedious. So did I miss some VBA function in MSWord, or GetCrossReferenceItems has known bug and can not found any replacement in VBA?

Is it possible to use ASCII code in .MoveEndUntil?

I am new in VBA for Word
I was wondering if it is possible to use ASCII code in .MoveEndUntil for example: .MoveEndUntil cset:=Chr(13) & "-", Count:=wdForward
Yes, it is totally possible to use sample code from your question. However, it depends what you are trying to achieve. Your code will move the end of the selection until any of the specified characters are found in the document (see the documentation of the Range.MoveEndUntil method).
That means that moving the end of the range will stop as soon as a dash or a carriage return is reached.
However, this is probably not what you want. It looks like you are trying to extend the range until the next list item? If this should be the case then you can't use a simple Range.MoveEndUntil. You would have to expand the range to the end of the current paragraph and check whether the following paragraph has a list formatting.

How to properly read a single Excel cell

I've been looking up in Google in these past days but I still can't find a good answer to this one.
Currently, this is how I do it:
For Each cell In ws.Range(fromCol, toCol)
If IsNothing(cell.Value) Then Exit For
valueList.Push(cell.Value.ToString())
Next
But when it reads a cell whose assumed data type is Time, it returns a Double value. I try to parse that value but it's not the same as expected.
How can I properly read a single Excel cell with an assumed type of Time?
As per the comment suggesting the article,
.Text is a bad idea in the sense that it will give you just the displayed string in that cell. If you use TypeName on the cell's content, for example, it will always return string regardless of any datatypes the content might be. However, if you use .Value, it will return the actual datatype name of the content of that cell.
This can prove useful to you if you're using TypeName in a comparison for instance. It saves you using conversion functions.
Take this example:
Sub Test()
Range("A1") = "True"
Debug.print(TypeName(Range("A1").Value))
Debug.print(TypeName(Range("A1").Text))
End Sub
This Output is:
Boolean
String