Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations biv343 on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Word w/ Delphi: String Too Long 2

Status
Not open for further replies.

lespaul

Programmer
Feb 4, 2002
7,083
US
I'm using a technique that I've used in the past to merge information from Delphi to Word:

Code:
procedure MergeData(varWord: variant; strBookMark: string; strData: string);
  begin
  // if the Bookmark is defined in the document then add the string at that
  // location.
    if varWord.ActiveDocument.Bookmarks.Exists(strBookMark) = True then
      varWord.ActiveDocument.FormFields.Item(strBookMark).Result:= strData
  end;

works great, however, I have run into an issue. I have a very long string that I'm trying to pass into a bookmark that has 'Unlimited' as the length. I have tried changing it to a large number 2000 which is plenty long enough for the text, but that didn't work either. However I can type ALL the information into the bookmark. I just can't pass it programatically.

Any suggestions on how I can get my long string into the bookmark?

Thanks!



Leslie

Essential for database developers:
The Fundamentals of Relational Database Design
Understanding SQL Joins
 
Hi Leslie,

try changing
Code:
varWord.ActiveDocument.FormFields.Item(strBookMark).Result:= strData
to
Code:
varWord.ActiveDocument.Bookmarks(strBookMark).Range.Fields(1).Result.Text = strData

Also, any reason you're trying to stuff the data into a formfield? If you don't actually need to have the data input into a formfield for later editing, try deleting it and just having your bookmark at the insertion point. Then drive the process with:
Code:
Dim BmkRng as Range
With varWord.ActiveDocument
	If .Bookmarks.Exists(strBookMark) Then
		Set BmkRng = .Bookmarks(strBookMark).Range
		BmkRng.Text = strData
		.Bookmarks.Add strBookMark, BmkRng
	End if
End With
Set BmkRng = Nothing

[MS MVP - Word]
 
The reason I'm doing it that way is because I used examples from the Internet to do the OLE connection to Word and those are what I found!

Thanks for the pointers, but this piece of code
Code:
varWord.ActiveDocument.Bookmarks(strBookMark).Range.Fields(1).Result.Text = strData
returns an error of :
[tt]Project raised exception class EOleException with message ''Bookmarks' is not a method'. Process stopped.[/tt]

and none of the bookmarks are filled in...

and after modifying your example above to the correct syntax, I get the same error as above, that Bookmarks is not a method and again no data is transferred to the document.

Code:
procedure MergeData(varWord: variant; strBookMark: string; strData: string);
  var
  BmkRng : Variant;
  begin
  // if the Bookmark is defined in the document then add the string at that
  // location.
    If varWord.ActiveDocument.Bookmarks.Exists(strBookMark) Then
    begin
      BmkRng := varWord.ActiveDocument.Bookmarks(strBookMark).Range;
      BmkRng.Text := strData;
      varWord.ActiveDocument.Bookmarks.Add(strBookMark, BmkRng);
    End;
  end;

Thanks and any other insights are welcome!

Leslie
 
Hi Leslie,

The approach I took works fine when I drive it with the Test macro below:
Code:
Sub UpdateBookmark(BmkNm As String, NewTxt As String)
Dim BmkRng As Range
With ActiveDocument
    If .Bookmarks.Exists(BmkNm) Then
        Set BmkRng = .Bookmarks(BmkNm).Range
        BmkRng.Text = NewTxt
        .Bookmarks.Add BmkNm, BmkRng
    End If
End With
Set BmkRng = Nothing
End Sub

Sub Test()
Dim BmkNm As String
BmkNm = "BkMrk"
Dim NewTxt As String
NewTxt = "My String"
UpdateBookmark BmkNm, NewTxt
End Sub
I've used different variable names, but that shouldn't affect anything.

Cheers

[MS MVP - Word]
 
Except, I'm writing my code in Borland Delphi (which is a visual Pascal language). I can't use what you show because the syntax isn't correct...I know it's a Word problem because my Delphi exception specifies the OLEException message.

 
Hi Leslie,

If Delphi is correctly exposing the Word object model, which is what I'm assuming 'varWord' represents, I would have thought you should have full access to the bookmarks method. So I doubt it's a Word problem. Unfortunately, I know nothing about Delphi in that regard.

Cheers

[MS MVP - Word]
 
Did you do a search on this? There is a article on precisely this.
Error 4609 - String too long

There is, in fact, a programmatic limit of 255 characters into a formfield. The article supplies a workaround.

Essentially, you write a dummy string into the formfield Result; select the formfield, collapse the selection, wriggle a character, and type in the BIG string.

It works.

I made a big string - rand=(12,6) - and:

I could copy and paste manually into the formfield.

I could even type in manually a huge string into the formfield.

But I could not do it programmatically. Error 4609 - String too long.

The workaround did it. I got the long string into the formfield. As much as I hate it...you must use Selection. I don't think there is any other way. The basic change to your code is:
Code:
// if the Bookmark is defined in the document then 
// add the string at that location.
  If varWord.ActiveDocument.Bookmarks.Exists(strBookMark) _
          = True Then
      With varWord.Selection
         .GoTo what:=wdGoToBookmark, Name:= strBookmark
         .Collapse
         .MoveRight wdCharacter, 1
         .TypeText Text:=strData
      End With
  End If
The long string may still be extracted programmatically.

It is a bug. Note: the workground at the link first inserts a string. You may need to work with this, depending on whether the formfield already HAS content, or not.

faq219-2884

Gerry
My paintings and sculpture
 
Ok, I'll try to translate that into Delphi syntax and see if I can get it to work....thanks!

Leslie
 
Follow up.

1. Depending on what you have going on, you may need to switch
Code:
With varWord.Selection
to:
Code:
With varWord.ActiveDocument.Selection

2. Is a text formfield even needed? They are for user input. If the user is not actually going to be changing whatever is strData, then use a REAL bookmark.
Code:
// if the Bookmark is defined in the document then 
// add the string at that location.
  If varWord.ActiveDocument.Bookmarks.Exists(strBookMark) _
          = True Then
     varWord.ActiveDocument.Bookmarks(strBookmark) _
              .Range.Text = strData
And be done with it. There would no need for a workaround as that 255 character limit does not affect the range of a REAL bookmark.

faq219-2884

Gerry
My paintings and sculpture
 
What do you mean by 'REAL' bookmark? I'm using from the 'Forms' toolbar, the first item: Text Form Field.

The code you provided in #2 (Range.Text) returns the same error that 'Bookmarks method is not valid'. I did cross post this (thread102-1412432) since it really does involve two different programming languages.

I'm hoping that I can get some assistance in translating the VBA to Delphi....

Thanks for you help Gerry! I wasn't getting the error number so that link you provided helped. I also got another link in the delphi forum that helped explain the differences in string types between different languages.

Leslie

 
What do you mean by 'REAL' bookmark?
Menu Insert -> Bookmark ...

Hope This Helps, PH.
FAQ219-2884
FAQ181-2886
 
Ok, so I inserted a bookmark that way and still get the same error that 'Bookmarks is not a valid method.'
 
What is YOUR actual code ?
How is varWord instantiated ?

Hope This Helps, PH.
FAQ219-2884
FAQ181-2886
 
Word is instantiated using the CreateOLE. The template has several bookmarks inserted using the Forms toolbar. They all work. Only the one where the text is extraordinarily long does the process fail:

Code:
procedure MergeData(varWord: variant; strBookMark: string; strData: string);
  var
  Range : variant;
  begin
  // if the Bookmark is defined in the document then add the string at that
  // location.
    if varWord.ActiveDocument.Bookmarks.Exists(strBookMark) = True then
      varWord.ActiveDocument.FormFields.Item(strBookMark).Result:= strData;
  end;

Code:
procedure SchoolSchedule(APrefix : string; ACaseNum : string; SchoolCode : string; Reschedule : string);
var
WordApp, WordDoc : Variant;
i : integer;
TitleString, PrintType, SpecialNotes, Schedule : string;
ClassCode, ClassNumber : string;
Spanish : boolean;

  procedure AddNotes(NoteString : string);
  var
  NotesList : TStringList;
  position, j : integer;
  Result : widestring;
  begin
    NotesList := TStringList.Create;
    while length(NoteString) > 0 do
    begin
      position := pos(';', NoteString);
      NotesList.Add(LeftStr(NoteString, position - 1));
      NoteString := copy(NoteString, position + 1, length(NoteString));
    end;
    for j := 0 to NotesList.Count - 1 do
    begin
      Result := Result + NotesList.Strings[j] + Chr(11)
    end;

    MergeData(WordApp, 'bmNotes', Result);
  end;

  function DateIsBetween: Boolean;
  //if today is between October 15 and March 15, no matter the year
  var Target: TDateTime;
  begin
    Target:= Today;
    case MonthOf(Target) of
      9:     result:= DayOf(Target) >= 15;
      11, 12,
      01, 02: result:= true;
      03:     result:= DayOf(Target) <= 15;
      else    result:= false
    end; //case
  end;

begin
  dbCSReports.strFileLocation := 'R:\Case Management\ReportGenerator\Document Templates\English Forms\';

  Spanish := False;
  with dbCSReports.qryScheduleLetter do
  begin
    SQL.CLear;
    SQL.Add('SELECT DISTINCT D.STSCDE, JUDNAM, INTERP, DEFSSN, DEFNAM, DEFADD, DEFCTY, DEFSTT, DEFZCD, ' +
    'DPHON1, DEFDTOB, H.JUDCOD, H.HERNGDAT, D.SCHCOD, D.CLSCOD, D.CLSNUM, ' +
    'D.CLSSDT, SCHNAM, SCHLOC, SCHCTY, SCHSTA, SCHZIP, L.* ' +
    'FROM espdclmf D ' +
    'INNER JOIN CMPCASMF C ON D.CASPRE = C.CASPRE AND C.CASNUM = D.CASNUM ' +
    'INNER JOIN CMPDEFMF DD ON D.CASPRE = DD.CASPRE AND D.CASNUM = DD.CASNUM ' +
    'INNER JOIN ESPSCHLET L ON D.SCHCOD = L.SCHCOD ' +
    'INNER JOIN CMPHERMF H ON D.HERDAT = H.HERDAT AND D.HERTYP = H.HERTYP AND ' +
    'D.CASPRE = H.CASPRE AND D.CASNUM = H.CASNUM ' +
    'INNER JOIN CMPJUDNM N ON H.JUDCOD = N.JUDCOD ' +
    'INNER JOIN ESPSCHMF S ON D.SCHCOD = S.SCHCOD ' +
    'WHERE D.CASPRE = ' + QuotedStr(APrefix) + ' AND D.CASNUM = ' + ACaseNum + ' AND D.SCHCOD = ' + QuotedStr(Schoolcode));
    Active := True;
    ClassCode := FieldByName('CLSCOD').AsString;
    ClassNumber := FieldByName('CLSNUM').AsSTring;
    If FieldByName('INTERP').AsSTring = 'S' then
    begin
      Spanish := True;
      SpecialNotes := SpecialNotes + Chr(11) + 'INTERPRETER NEEDED: Spanish';
      dbCSReports.strFileLocation := 'R:\Case Management\ReportGenerator\Document Templates\Spanish Forms\';
    end;


    try
      WordApp := CreateOleObject('Word.Application');
      WordApp.Visible := True;
      WordDoc := WordApp.Documents.Add(dbCSReports.strFileLocation + 'SchoolRegistration.dot');
      //merge query information into word document

      //remove a lot of mergeData functions that work
      if not Spanish then
        AddNotes(FieldByName('ENGNOTES').AsString)
      else
        AddNotes(FieldByName('SPNNOTES').AsString);

      //remove other mergeData functions

      end;

      WordApp.Printout;
      //dbCSReports.booPrinting := True;

    finally
      WordDoc.close(0);
      WordApp.Quit;
    end;



  end;
end;

Leslie

Essential for database developers:
The Fundamentals of Relational Database Design
Understanding SQL Joins
 
I'm currently trying to work on using the early binding techniques within Delphi so that perhaps I can get the methods that ARE available for me to use. I've tried in the past to use the early binding and have not been successful...maybe I have learned enough to understand it now!

Leslie
 
A real bookmark is a defined (named) range. It is not a field.

A formfield is a field that also has a defined range. Therefore it is ALSO a member of the Bookmark collection. It is primarily a member of the Formfields collection.

In other words, formfields are always (also) bookmarks.

Bookmarks are not always (also) formfields.

For example, one of the things I have always found a little annoying is identifying and accessing a specific table in a document no matter where it is.

So I regularly bookmark my tables. I select the table and go Insert > Bookmark and give it a name, say "Clients". Now, this does not directly name the table, but it effectively does. I can create a table object and set it for THAT table...no matter where it is. The table can be moved, or other tables created before...it does not matter.

Let me back up.

Say you have a table. It is the first table in the document. It is identified as:

ActiveDocument.Tables(1)

It is identified in the Tables collection by its index number. The index number is set by the order in the document.

If you create a table before it, it automatically becomes:

ActiveDocument.Tables(2)

In other words, the index number of a table is NOT directly tied to the table itself.

BUT, if you bookmark it, no matter where it is, no matter how many tables you create before it, you can always point to it by name with a table object.
Code:
Dim oTable As Table
Set oTable = ActiveDocument.Bookmarks("Client") _
   .Range.Tables(1)
oTable is now the table IN the bookmark, regardless of where it is in the document. This is because Bookmarks are dynamic.

Bookmarks are locations. Their critical properties are .Start and .End. They give a name to a location that starts here and ends there. By being dynamic, if you put something new between Start and End, those parameters adjust accordingly.

So if the purpose is to put something AT a specific location, use bookmarks.

Formfields are fields. Now they do have specific locations, so they are also bookmarks. However, their main purpose is to take user input.

Further, for formfields to function as intended, the document must be protected for forms.

Bookmarks do not.

Further, bookmarks can be nested.

In the above example, say the whole table was bookmarked as "Client", and within it, Cell(2, 3) was bookmarked as "IBM".

Without ever identifying the table, you could get the contents of THAT cell.
Code:
Msgbox ActiveDocument.Bookmarks("IBM").Range.Text
or write to THAT cell:
Code:
 ActiveDocument.Bookmarks("IBM").Range.Text = _
    "Honking big company"
or make a Cell object, and have access to all the properties of a Cell object:
Code:
Dim oCell As Cell
Set oCell = ActiveDocument.Bookmarks("IBM").Range.Cells(1)
oCell.Shading.Texture = wdTexture15Percent

So say, THAT cell was originally Cell(2, 3) of the table. You add four rows above it. By index number it is now Cell(6, 3). However, if it is bookmarked...it does not matter!
Code:
Dim oCell As Cell
Set oCell = ActiveDocument.Bookmarks("IBM").Range.Cells(1)
will still make a cell object of THAT cell, regardless of its index numbers within the table.

Bookmarks, in a sense, are the highest level of location parameters. They even work across Story ranges, because VBA accesses them via the Bookmark collection. Say you bookmark the Header of Section 1, naming it "Section1Primary":
Code:
Dim r As Range
Set r = ActiveDocument.Bookmarks("Section1Primary").Range
r.Text = "Yadda yadda"
badda bing, badda boom. Section 1 header is "Yadda yadda".

Note to other Word people, yes, yes, there are other ways of doing the same thing, like with a HeaderFooter object.

Going back to the nesting ability. Say you have a table (bookmarked, say as "Contracts") with 16 cells (8 rows, 2 columns). But three cells are commonly changed, or for some reason you want to get at them easily. You bookmark those cells (say, "IBM", "Oracle", "Pizza").
Code:
Dim oTable As Table
Dim oBM As Bookmark
Dim msg As String
Set oTable = ActiveDocument.Bookmarks("Contracts") _
    .Range.Tables(1)
    For Each oBM In oTable.Range.Bookmarks
[COLOR=red]' the table itself is a bookmark and
' is included in the collection 
' so ignore it[/color red]
            If oBM.Name <> "Contracts" Then
                msg = msg & "The text of " & oBM.Name & _
                    " is " & oBM.Range.Text & vbCrLf
            End If
    Next
    Set oTable = Nothing
MsgBox msg
would display:

The text of IBM is text of THAT cell
The text of Oracle is text of THAT cell
The text of Pizza is text of THAT cell

If the purpose is to programmatically place/extract text at a location - use bookmarks.

If the purpose is to allow user input at a location - use formfields.

You can of course combine them. Say Section 1 has formfields, Section 2 is unprotected for user editing, Section 3 has formfields. But you want to get the formfield contents of just Section 3. Bookmark Section 3 (as "mySection3").
Code:
Dim r As Range
Dim oFF As Formfield
Dim msg As String
Set r = ActiveDocument.Bookmarks("mySection3").Range
   For Each oFF In r.FormFields
        msg = msg & oFF.Result & vbCrLf
   Next
MsgBox msg
Set r = Nothing
would pick of the contents of just the formfields in Section 3.

Bookmarks - ummmm, real bookmarks - are a very powerful feature of Word. Again, if the purpose is to action programmatically at a location, use bookmarks. If the purpose is for user input, use formfields. IMO, if there is no user input required, there is no point in using formfields.

faq219-2884

Gerry
My paintings and sculpture
 
Thanks for that explanation of the bookmark.

Ok, so I have created a "real" bookmark in the document. It is named bmNotes. I want to be able to insert a string from another program. In looking here I found information that is not in the Word VBA help, but I can't see where in any of those properties or methods I can programatically transfer information to just a bookmark.

Leslie
 
want to be able to insert a string from another program.....those properties or methods I can programatically transfer information to just a bookmark[\quote]If you actually read my last post
Code:
ActiveDocument.Bookmarks("bmNotes").Range.Text _
    = strData
Not sure what you mean by "just a bookmark".

NOTE: writing to the Range.Text puts the text immediately after the bookmark.

If your requirements - and nowhere have you actually stated what your requirements are - are to be able to write, and RE-write, to the location, replacing the inserted text, then you will something like:
Code:
Sub FillABookmark(strBM_Name As String, strBM_Text As String)
On Error Resume Next
  With Selection
    .GoTo what:=wdGoToBookmark, Name:=strBM_Name
    .Collapse Direction:=wdCollapseEnd
    ActiveDocument.Bookmarks(strBM_Name).Range _
        .Text = strBM_Text
    .MoveEnd unit:=wdCharacter, Count:=Len(strBM_Text)
    ActiveDocument.Bookmarks.Add Name:=strBM_Name, _
        Range:=Selection.Range
    .Collapse Direction:=wdCollapseEnd
  End With
End Sub
which places the text inside the bookmark itself.

So:
Code:
Call FillABookmark(strBookmark, strData)
This can be repeated as much as you like. The text will be replaced with the new text.

faq219-2884

Gerry
My paintings and sculpture
 
Hi Gerry,

The code I posted does the same, and more elegantly IMHO.

Cheers

[MS MVP - Word]
 
macropod - Indeed.

lespaul - I have tried to explain what the differences is between formfields and bookmarks, but obviously I am not doing that well.

"The template has several bookmarks inserted using the Forms toolbar."

NO, it does not. It has several formfields inserted using the Forms toolbar. The Formfield toolbar inserts formfields.

I have explained that formfields are also bookmarks, and some examples of where a bookmark - rather than a formfield - is more appropriate.

If you are getting object method errors, thsi has nothing to dow ith formfields or bookmarks. It has to do with the instance of Word you have created.

faq219-2884

Gerry
My paintings and sculpture
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top