Paragraph Mark In The Header

J

Jeff

Hi,

I'm maintaining some software for my company that calls word via the
COM. I'm using this forum as word exposes most of its VBA functions
through the COM so I still have access to the methods. If any one here
doesn't know how to help then I'm stuffed :).

First I'll explain what I'm trying to do and then the problem I'm
having. My company's software allows people to create word templates
that allow merge fields to be filled from a database. When we upgraded
this software the vba code behind these templates was changed to
compensate. For our clients to upgrade we need to convert their old
templates (often in the hundreds) to new ones with the new vba code.

The way it was done was to create a blank document with the new vba
code, open the old template and copy and paste the contents. Rather
basic but it worked until now.

A client I'm trying to upgrade have used some of the more advanced
features of word like continuous sections. When these are copied they
turn into next page sections. I found Microsoft documentation say it
does this by design.

I had to bight the bullet and learn the word object model. I now have
it looping though coping section by section, restoring the section
brakes as it goes.

The problem I have is the headers and footers. The headers are blank in
some of the original documents. When copied a single paragraph mark
appears pushing out the formatting of the main body. If I open the
header view and close it without typing a thing they disappear. Same
happens if I open the print preview. I just can't get the code to do
this automatically.

I found a bug http://support.microsoft.com/default.aspx?kbid=813816 and
applied the suggested patch. It didn't help. I've been banging my head
against this for days now. Any ideas would be most welcome.
 
P

Peter Hewett

Hi Jeff

If your using just cut and paste you could have a LOT more problems than
are currently apparent.

A template contains many things you can't cut and paste:
- Styles
- Macros
- AutoText
- Toolars
- Document Properties
- Custom Document Properties
- Document Variables

Cutting and pasting may transfer the text but if you end up with the
correct format you may be just lucky! If you cut text from the original
template and say the default style for Normal is Arial and you paste it
into a new template that uses the the Time New Roman font for it's Normal
style, then you end up with Times New Roman!!

If you're already aware of the above that's really good.

I'd tackle the Header/Footer problem as follows:
- Loop through the input template transfering the section setting to the
new template.
- loop through the input template transfering each Sections Header/Footer
settings and then transfer the text.
- You may need to change the page layout for each section as well, because
the margins and portrait/landscape etc. can change from section to section.

Given the problems with the above isn't there any easier way to swap out
your code or is it integrated with the users own code?

Cheers - Peter
 
J

Jeff

Thanks for the quick reply.

The vba code in the template runs when opened. It copies fields from a
local data source into some merge fields and saves the resulting doc in a
predefined location. The data connection part of these templates has been
changed plus some other things (these are often from 95/98 machines and old
versions of word).

My program connects, disables these macros and opens the template. It then
copies the formatting as follows (Delphi code but I think you'll get the
idea):

{ Copy First Section }
with BlankDoc.Sections.First do
begin

// the following line dosn't work for some reason
PageSetup := OldDoc.Sections.First.PageSetup;

// I have to do the following instead
with PageSetup do
begin
TextColumns := OldDoc.Sections.First.PageSetup.TextColumns;
Orientation := OldDoc.Sections.First.PageSetup.Orientation;
Gutter := OldDoc.Sections.First.PageSetup.Gutter;

PageWidth := OldDoc.Sections.First.PageSetup.PageWidth;
PageHeight := OldDoc.Sections.First.PageSetup.PageHeight;

FooterDistance := OldDoc.Sections.First.PageSetup.FooterDistance;
HeaderDistance := OldDoc.Sections.First.PageSetup.HeaderDistance;

LeftMargin := OldDoc.Sections.First.PageSetup.LeftMargin;
RightMargin := OldDoc.Sections.First.PageSetup.RightMargin;
TopMargin := OldDoc.Sections.First.PageSetup.TopMargin;
BottomMargin := OldDoc.Sections.First.PageSetup.BottomMargin;

LayoutMode := OldDoc.Sections.First.PageSetup.LayoutMode;

end;


// the strange thing is if I leave out the next two for loops there is
no difference in the final document expect that the loops put extra
paragraph marks into the headers and footers.

for x := 1 to OldDoc.Sections.First.Headers.Count do
begin

Headers.Item(x).Range.FormattedText :=
OldDoc.Sections.First.Headers.Item(x).Range.FormattedText;

Headers.Item(x).Exists := OldDoc.Sections.First.Headers.Item
(x).Exists;

Headers.Item(x).LinkToPrevious := OldDoc.Sections.First.Headers.Item
(x).LinkToPrevious;

end;

for x := 1 to OldDoc.Sections.First.Footers.Count do
begin

Footers.Item(x).Range.FormattedText :=
OldDoc.Sections.First.Headers.Item(x).Range.FormattedText;

Footers.Item(x).Exists := OldDoc.Sections.First.Headers.Item
(x).Exists;

Footers.Item(x).LinkToPrevious := OldDoc.Sections.First.Headers.Item
(x).LinkToPrevious;

end;

OldDoc.Sections.First.Range.Copy;
Range.Paste;

// delete the extra paragraph mark the the paste creates
Range.Paragraphs.Last.Range.Delete(EmptyParam, EmptyParam);


I then loop through the remaining sections doing the same as above with a
few extra lines to restore the section brakes.

It seems when I open the header view or print preview word does a check and
sees the headers and footers are empty and hides them. I want to be able
to do this within my code.

If there is an easer way I'd love to know :).

Thanks
 
P

Peter Hewett

Hi Jeff

As another idea you can use VBA to manipulate the VBA project/module
(s)/code. How about just removing the existing VBA modules and adding the
new ones?? You can use code like this to add a VBA module from a text file.
The name the module actually gets added to the project as is contained in
the text file. Here's how:

ActiveDocument.VBProject.VBComponents.Add(vbext_ct_StdModule) _
.CodeModule.AddFromFile _
"F:\My Templates\Test Documents\More Crap.bas"

Let me know, if it's no go I'll look at your code in more detail.

HTH + Cheers - Peter
 
J

Jeff

Hi Peter

You've given me an idea. Would it be possible to delete all the modules
from the old template and copy across the new templates modules? I've been
looking at the VBComponents methods and think it is possible. Basically as
you were suggesting I should copy the VBA code instead of the text.

One question, I'm running this with Office XP. The templates can sometimes
come from very early versions of word. If it's xp that's opening and
saving them will they be saved in an xp format or doesn't it matter?

Thanks

Jeff
 
P

Peter Hewett

Hi Jeff

What you do is loop through and delete all the VBA Projects existing
standard modules. You can also delete any Class Modules and Forms as
necessary. If all your "old" modules have standard names you could delete
just those and leave any user modules intact.

Just open the template that contains the new modules and from the VBA IDE
Export the modules, this will create a ".bas" file for each module, a
".cls" file for each class and a ".frm" and a ".frx" for each form.

You can then use a version of the code I supplied to add the new modules to
the template VBA Project using the files you manually exported.

Since this is a one-off excercise, unless you have very good reasons I'd
write this stuff using Word/VBA rather than Pascal/COM.

If you need further help, just keep on posting!

HTH + Cheers - Peter
 
J

Jeff

Thanks,

I will have to stick with Delphi as this code has been reused in other
projects. Once I have it working on the conversion app I will have to
update these as well. I'll give it a go and see what happens. Cheers
for the advice.

Jeff
 
P

Peter Hewett

Hi Jeff

Even simpler than:
ActiveDocument.VBProject.VBComponents.Add(vbext_ct_StdModule) _
.CodeModule.AddFromFile _
"F:\My Templates\Test Documents\More Crap.bas"

use:
ActiveDocument.VBProject.VBComponents.Import _
"F:\My Templates\Test Documents\More Crap.bas"


Cheers - Peter
 
P

Peter Hewett

Hi Jeff

Here's some VBA code you can port to Delphi:

' Useful for debugging and general info
Private Sub ListComponentsInProject()
Dim vbcItem As vbide.VBComponent

With ActiveDocument.VBProject
Debug.Print "Components in project: " & .VBComponents.Count
For Each vbcItem In ActiveDocument.VBProject.VBComponents
Debug.Print vbcItem.Name & " [";
Select Case vbcItem.Type
Case vbext_ct_StdModule
Debug.Print "Standard Module";
Case vbext_ct_ClassModule
Debug.Print "Class Module";
Case vbext_ct_MSForm
Debug.Print "User Form";
Case vbext_ct_ActiveXDesigner
Debug.Print "Active X Designer";
Case vbext_ct_Document
Debug.Print "Document";
End Select
Debug.Print "]"
Next
End With
End Sub

' Delete existing module in your project
Public Sub DeleteComponents()
Dim vbcItem As vbide.VBComponent
Dim boolDelete As Boolean

With ActiveDocument.VBProject
For Each vbcItem In ActiveDocument.VBProject.VBComponents

' List of component names and types to delete
boolDelete = (vbcItem.Name = "MoreCrap" And _
vbcItem.Type = vbext_ct_StdModule)
boolDelete = boolDelete Or (vbcItem.Name = "clsRandom" And _
vbcItem.Type = vbext_ct_ClassModule)
boolDelete = boolDelete Or (vbcItem.Name = "clsRandom1" And _
vbcItem.Type = vbext_ct_ClassModule)

' Delete the current component
If boolDelete Then
Debug.Print "Deleting component: " & vbcItem.Name
.VBComponents.Remove vbcItem
End If
Next
End With
End Sub

' Add new components
Private Sub ImportModules()

' Add your new module here
With ActiveDocument.VBProject.VBComponents
.Import "F:\My Templates\Test Documents\More Crap.bas"
.Import "F:\My Templates\Test Documents\clsRandom.cls"
End With
End Sub


HTH + Cheers - Peter
 
J

Jeff

Thanks again

I did have a little trouble deleting the Components but I got there in
the end.

My code ended up as:

{ Removes Old VBA Code }
NoOfVBComps := OldDoc.VBProject.VBComponents.Count;
finished := false;
VBCompPos := 1;
count := 0;

while (not finished) do
begin
oleVar := VBCompPos;
try
oleVar2 := OldDoc.VBProject.VBComponents.Item(oleVar).Name;
OldDoc.VBProject.VBComponents.Remove
(OldDoc.VBProject.VBComponents.Item(oleVar2));
except
inc(VBCompPos);
end;

inc(count);
if count = NoOfVBComps then
finished := True;
end;

{ Add new code }
OldDoc.VBProject.VBComponents.Import(ExtractFilePath(ParamStr(0)) +
'Crap1.bas');
OldDoc.VBProject.VBComponents.Import(ExtractFilePath(ParamStr(0)) +
'Crap2.bas');

Not as pretty but it seems to work.

Any obvious bugs or problems with the above?

Jeff
 
P

Peter Hewett

Hi Jeff

Can't see anything obviously wrong (though it's 16 years since I last wrote
any serious Pascal)!

Good luck - Peter


Thanks again

I did have a little trouble deleting the Components but I got there in
the end.

My code ended up as:

{ Removes Old VBA Code }
NoOfVBComps := OldDoc.VBProject.VBComponents.Count;
finished := false;
VBCompPos := 1;
count := 0;

while (not finished) do
begin
oleVar := VBCompPos;
try
oleVar2 := OldDoc.VBProject.VBComponents.Item(oleVar).Name;
OldDoc.VBProject.VBComponents.Remove
(OldDoc.VBProject.VBComponents.Item(oleVar2));
except
inc(VBCompPos);
end;

inc(count);
if count = NoOfVBComps then
finished := True;
end;

{ Add new code }
OldDoc.VBProject.VBComponents.Import(ExtractFilePath(ParamStr(0)) +
'Crap1.bas');
OldDoc.VBProject.VBComponents.Import(ExtractFilePath(ParamStr(0)) +
'Crap2.bas');

Not as pretty but it seems to work.

Any obvious bugs or problems with the above?

Jeff

Hi Jeff

Here's some VBA code you can port to Delphi:

' Useful for debugging and general info
Private Sub ListComponentsInProject()
Dim vbcItem As vbide.VBComponent

With ActiveDocument.VBProject
Debug.Print "Components in project: " & .VBComponents.Count
For Each vbcItem In ActiveDocument.VBProject.VBComponents
Debug.Print vbcItem.Name & " [";
Select Case vbcItem.Type
Case vbext_ct_StdModule
Debug.Print "Standard Module";
Case vbext_ct_ClassModule
Debug.Print "Class Module";
Case vbext_ct_MSForm
Debug.Print "User Form";
Case vbext_ct_ActiveXDesigner
Debug.Print "Active X Designer";
Case vbext_ct_Document
Debug.Print "Document";
End Select
Debug.Print "]"
Next
End With
End Sub

' Delete existing module in your project
Public Sub DeleteComponents()
Dim vbcItem As vbide.VBComponent
Dim boolDelete As Boolean

With ActiveDocument.VBProject
For Each vbcItem In ActiveDocument.VBProject.VBComponents

' List of component names and types to delete
boolDelete = (vbcItem.Name = "MoreCrap" And _
vbcItem.Type = vbext_ct_StdModule)
boolDelete = boolDelete Or (vbcItem.Name = "clsRandom" And _
vbcItem.Type = vbext_ct_ClassModule)
boolDelete = boolDelete Or (vbcItem.Name = "clsRandom1" And _
vbcItem.Type = vbext_ct_ClassModule)

' Delete the current component
If boolDelete Then
Debug.Print "Deleting component: " & vbcItem.Name
.VBComponents.Remove vbcItem
End If
Next
End With
End Sub

' Add new components
Private Sub ImportModules()

' Add your new module here
With ActiveDocument.VBProject.VBComponents
.Import "F:\My Templates\Test Documents\More Crap.bas"
.Import "F:\My Templates\Test Documents\clsRandom.cls"
End With
End Sub
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top