This is becoming unbearable!

L

Legal Learning

I need a macro that clears all footers and puts in a data and time in all
footers. Could be first page footer, primary footer etc. This can't be so
hard but I am struggling with this almost to tears.

I am not a great programmer (and I don't profess to be) but in the past I
have been pretty successful in accomplishing many tasks.

Ok, I'll stop whining. Using word 2003, however, I will be loading this on
Word 2000.

Signed,

Frustrated
 
K

Klaus Linke

Legal Learning said:
I need a macro that clears all footers and puts in a data and time in all
footers. Could be first page footer, primary footer etc. This can't be
so
hard but I am struggling with this almost to tears.

I am not a great programmer (and I don't profess to be) but in the past I
have been pretty successful in accomplishing many tasks.

Ok, I'll stop whining. Using word 2003, however, I will be loading this
on
Word 2000.

Signed,

Frustrated


Hi,

For macros involving story ranges, I often use a standard helper macro,
LoopStoryRanges(myMacro As String), that can run any other macro on all
story ranges.

The macro that does all the work -- in this case
TestHeadersFootersRange(myRange As Range) -- takes some range as an
argument.

Since I want the work macro to do its work on all story ranges, a third
simple macro -- in this case TestHeadersFooters -- calls LoopStoryRanges
with the work macro as an argument.

Sounds a bit complicated, but since I can reuse LoopStoryRanges pretty
often, and the other two macros are simple to write, I've found it handy.

Do you want to put in a field for the time? If you have trouble with that,
post back!

Regards,
Klaus



Private Sub LoopStoryRanges(myMacro As String)
Dim myStoryRange As Range
For Each myStoryRange In ActiveDocument.StoryRanges
Application.Run myMacro, myStoryRange
While Not (myStoryRange.NextStoryRange Is Nothing)
Set myStoryRange = myStoryRange.NextStoryRange
Application.Run myMacro, myStoryRange
Wend
Next myStoryRange
End Sub

Sub TestHeadersFooters()
LoopStoryRanges ("TestHeadersFootersRange")
End Sub

Sub TestHeadersFootersRange(myRange As Range)
' depending on the type of story...
Select Case myRange.StoryType
Case wdEvenPagesFooterStory, wdEvenPagesHeaderStory, _
wdFirstPageFooterStory, wdFirstPageHeaderStory, _
wdPrimaryFooterStory, wdPrimaryHeaderStory
' delete existing text
myRange.Text = ""
' insert something else
myRange.InsertBefore "Hello World"
End Select
End Sub
 
B

Bear

Klaus:

I'm trying to understand storyranges better. Why do you use storyranges
rather than sections? Would this code do the same thing?

For Each objSection In ActiveDocument.Sections
For Each objHeaderFooter In objSection.Headers
objHeaderFooter.Range.Fields.Update
Next objHeaderFooter
For Each objHeaderFooter In objSection.Footers
objHeaderFooter.Range.Fields.Update
Next objHeaderFooter
Next objSection

Of course, the range.update would have to be changed to delete the old
content and insert the new.

Bear
 
L

Legal Learning

Yes I need a date and time field inserted IF there is not one. If there is
one just update it.

I copied your code into a module and when it asks for the macro to be run, I
choose the testheaderfooter. But the Hello world is not put into the footer.
Instead it is in the body of the document. I want this to go into the
footer.

Thanks for your help
 
L

Legal Learning

Klaus:

I apologize. It does go into the header and footer. I removed the
reference to the header (because I only want it in the footer) and added the
date/time field. Now, my problem is how do I test to see if the field is
already in the footer? If it is in the footer then all I want to do is
update it. If not, then I want to add it.
 
B

Bear

Legal Learning:

I'm sure I've posted this here recently, but I'll repeat it. In many cases
it's easier to create a condition than to test for it. My overall strategy
would be to delete the footer entirely and rebuild it entirely.

Bear
 
L

Legal Learning

Hi Bear,

The problem with that apprach is that they may have other things in the
footer (page numbering etc) that I don't want to delete. They may or may not
have other text in the footer so that could be a real problem. I can tell
you that there is at least one constant in the footer and that is the
document id number which looks like this: {xxxx}. It always has the curly
brackets. Thanks for any help you may be able to give me. Please know that
I appreciate it! Perhaps I won't go insane on this one - or perhaps I will!
 
J

Jean-Guy Marcil

Legal Learning was telling us:
Legal Learning nous racontait que :
Klaus:

I apologize. It does go into the header and footer. I removed the
reference to the header (because I only want it in the footer) and
added the date/time field. Now, my problem is how do I test to see
if the field is already in the footer? If it is in the footer then
all I want to do is update it. If not, then I want to add it.

A liitle modification to Klaus' code should get you going.
Note that I have added some stuff about bank headers/footers. If you have a
blank header/footer at the beginning of the section, nothing gets checks in
that section.

'_______________________________________
Option Explicit

'_______________________________________
Private Sub LoopStoryRanges(myMacro As String)

Dim myStoryRange As Range
Dim lngJunk As Long
'Fix the skipped blank Header/Footer problem as _
provided by Peter Hewett
lngJunk = ActiveDocument.Sections(1).Headers(1).Range.StoryType

For Each myStoryRange In ActiveDocument.StoryRanges
Application.Run myMacro, myStoryRange
While Not (myStoryRange.NextStoryRange Is Nothing)
Set myStoryRange = myStoryRange.NextStoryRange
Application.Run myMacro, myStoryRange
Wend
Next myStoryRange

End Sub
'_______________________________________

'_______________________________________
Sub TestHeadersFooters()

LoopStoryRanges ("TestHeadersFootersRange")

End Sub
'_______________________________________

'_______________________________________
Sub TestHeadersFootersRange(myRange As Range)
Dim lngFields As Long
Dim i As Long

lngFields = -1

Select Case myRange.StoryType
Case wdEvenPagesFooterStory, wdFirstPageFooterStory, _
wdPrimaryFooterStory
'We found a footer, initialize the long
lngFields = 0
If myRange.Fields.Count > 0 Then
For i = 1 To myRange.Fields.Count
If myRange.Fields(i).Type = wdFieldTime Or _
myRange.Fields(i).Type = wdFieldDate Then
lngFields = lngFields + 1
End If
Next
End If
End Select

If lngFields = 0 Or lngFields = 1 Then
'if it is equal to 0 or 1 it means that _
one or both of the field types are missing
'Add your date fields where you want in the footer

'Code Here

End If

End Sub
'_______________________________________

--

Salut!
_______________________________________
Jean-Guy Marcil - Word MVP
(e-mail address removed)
Word MVP site: http://www.word.mvps.org
 
B

Bear

Legal Learning:

I stand by my suggestion. If you accept that there could be ANY hodge-podge
of material in a footer, you're going to have to write a lot of code to
accommodate all the possibilities. How can you know where to put your
material if you don't know what's liable to be there already?

You should take this opportunity to not only update but standardize the
document footers. Determine, for each document type, what the official
standard footer should be, then add that as autotext to the template for that
document type, and in your code, add that autotext (including your fields)
into an empty footer.

Maybe you haven't got that option. Maybe there are no clear cut document
types, no templates, and you have no authority to create or enforce standards.

I understand you may need to preserve the old footers, and don't mean to
imply that there's anything wrong with that. I just wanted to state my case
fully.

For each footer, test to see if there are any fields in the footer material.
If there are, go through them and see if any are your date and time field. If
one is, update it and move on to the next footer. Otherwise, add the field.
But where? How do you know adding it won't ruin the wrapping or tabbing or
something in an existing line?

Come back with specific problems and I'll try to help as best I can. Here's
some pseudo code. I'm sure there's a better way (some sort of loop) to do
this. Hope an MVP chimes in. We'll both learn something...

flgFound = False

if objFooterRange.Fields.Count > 0 then
for each objField in objFooterRange.Fields
if objField.type = wdFieldDate then
flgFound = True
objField.Update
end if
next objField
endif
if flgFound = False then
objFooterRange.Fields.Add (here's where the tough part is)
end if

Bear
 
J

Jean-Guy Marcil

Bear was telling us:
Bear nous racontait que :
Legal Learning:

I stand by my suggestion. If you accept that there could be ANY
hodge-podge of material in a footer, you're going to have to write a
lot of code to accommodate all the possibilities. How can you know
where to put your material if you don't know what's liable to be
there already?

True, but if one states that the date/time field needs to be on a line of
its own as the first or last thing in the footer. In such a case the code is
fairly simple and will not disturb the footer content. For example, using
the If block from my other post:

If lngFields = 0 Or lngFields = 1 Then
'if it is equal to 0 or 1 it means that _
one or both of the field types are missing
'Add your date fields where you want in the footer

'Field before:
With myRange
.Collapse wdCollapseStart
.InsertParagraphAfter
.Collapse wdCollapseStart
.InsertDateTime DateTimeFormat:="MMMM dd, yyyy", _
InsertAsField:=True
End With

'Field after:
With myRange
.Collapse wdCollapseEnd
.InsertParagraphAfter
.Collapse wdCollapseStart
.InsertDateTime DateTimeFormat:="MMMM dd, yyyy", _
InsertAsField:=True
End With


End If

You should take this opportunity to not only update but standardize
the document footers. Determine, for each document type, what the
official standard footer should be, then add that as autotext to the
template for that document type, and in your code, add that autotext
(including your fields) into an empty footer.

This may not be possible (as you provided for in your next paragraph) if
documents come from various authors who have all created their documents
based on Normal.dot
Maybe you haven't got that option. Maybe there are no clear cut
document types, no templates, and you have no authority to create or
enforce standards.

I understand you may need to preserve the old footers, and don't mean
to imply that there's anything wrong with that. I just wanted to
state my case fully.

--

Salut!
_______________________________________
Jean-Guy Marcil - Word MVP
(e-mail address removed)
Word MVP site: http://www.word.mvps.org
 
K

Klaus Linke

Why do you use storyranges rather than sections?

Lazyness <g>

As I said, the LoopStoryRanges macro is part of my toolbox, and used pretty
frequently.
I don't need to write/test it any more.

In the rest of the code, I don't need to loop at all. So it's pretty easy to
write and understand.
Would this code do the same thing?

For Each objSection In ActiveDocument.Sections
For Each objHeaderFooter In objSection.Headers
objHeaderFooter.Range.Fields.Update
[...]

Your code (looping all headers/footers in all sections) is cleaner, because
it doesn't touch the other story ranges at all.
And of course, you could also put the code that does the actual work
(.Range.Fields.Update or whatever) into a separate macro that takes the
range as an argument.
OTOH, the code could become complicated if you want to treat different kinds
of headers/footers differently.

I guess the basic question
-- is it easier to loop all objects, and once you have the object do
something with it depending on its type/properties/location,
-- or to loop all objects of some type/properties/location, do something
with them, then the next kind, do something else with those, and so on.

Depends, I guess :)

Regards,
Klaus
 
L

Legal Learning

Hi Jean-Guy,

Thanks so much for your help. Klaus' code is WAY beyond my skills. I used
some of your post before but still getting an error message saying "Oject
Variable or With Block Variable not set"

Here is my code which may be so off. Should I just give up? You guys are
so talented and skilled and I am really remedial. Thanks for any help you
can give me. The lines about the update footer are for the WORLDOX DMS
system.

Option Explicit
Sub DeleteSelectedFields()
Dim lngFields As Long
Dim i As Long
Dim pRange As Word.Range

Application.Run macroname:="WORLDOX.NewMacros.WdClearFooter"
Set pRange = pRange.NextStoryRange

Select Case pRange.NextStoryRange
Case wdFirstPageFooterStory, _
wdPrimaryFooterStory, wdEvenPagesFooterStory

On Error Resume Next
lngFields = 0
If pRange.fields.Count > 0 Then
For i = 1 To pRange.fields.Count
If pRange.fields(i).Type = wdFieldTime Or _
pRange.fields(i).Type = wdFieldDate Then
lngFields = lngFields + 1
End If
Next
End If
End Select

If lngFields = 0 Or lngFields = 1 Then
With pRange
.Collapse wdCollapseEnd
.InsertParagraphAfter
.Collapse wdCollapseStart
.InsertDateTime DateTimeFormat:="mm dd, yyyy", _
InsertAsField:=True
End With


End If
Application.Run macroname:="WORLDOX.NewMacros.WdUpdateFooter"


End Sub
 
J

Jean-Guy Marcil

Legal Learning was telling us:
Legal Learning nous racontait que :
Hi Jean-Guy,

Thanks so much for your help. Klaus' code is WAY beyond my skills.
I used some of your post before but still getting an error message
saying "Oject Variable or With Block Variable not set"

Here is my code which may be so off. Should I just give up? You
guys are so talented and skilled and I am really remedial. Thanks
for any help you can give me. The lines about the update footer are
for the WORLDOX DMS system.

Option Explicit
Sub DeleteSelectedFields()
Dim lngFields As Long
Dim i As Long
Dim pRange As Word.Range

Application.Run macroname:="WORLDOX.NewMacros.WdClearFooter"
Set pRange = pRange.NextStoryRange

Select Case pRange.NextStoryRange
Case wdFirstPageFooterStory, _
wdPrimaryFooterStory, wdEvenPagesFooterStory

On Error Resume Next
lngFields = 0
If pRange.fields.Count > 0 Then
For i = 1 To pRange.fields.Count
If pRange.fields(i).Type = wdFieldTime Or _
pRange.fields(i).Type = wdFieldDate Then
lngFields = lngFields + 1
End If
Next
End If
End Select

If lngFields = 0 Or lngFields = 1 Then
With pRange
.Collapse wdCollapseEnd
.InsertParagraphAfter
.Collapse wdCollapseStart
.InsertDateTime DateTimeFormat:="mm dd, yyyy", _
InsertAsField:=True
End With


End If
Application.Run macroname:="WORLDOX.NewMacros.WdUpdateFooter"


End Sub

Your code will not scans all footers, in fact it does not even get to the
first footer.

I do not understand why you are not using Klaus' code with my modifications.
All you have to do is add the code you need to work with the footer where it
says " 'Code Here". Our combined code scans all footers and looks for the
presence of date/time fields. If one of the two is missing, the area marked"
'Code Here" gets executed.
If you need to update the date/time fields that are already there (which is
not necessary as a print preview or printing will update them) just add the
line

myRange.Fields(i).Update

just before

lngFields = lngFields + 1



As to the error you are getting, it is probably because:

Set pRange = pRange.NextStoryRange

Here you are setting pRange using pRange itself, which has not been set
yet...therefore the error telling you that an object is not set.



Finally, if you need to run those other macros before and after the fields
business (the WORLDOX stuff), just use this:

'_______________________________________
Sub TestHeadersFooters()

Application.Run macroname:="WORLDOX.NewMacros.WdClearFooter"

LoopStoryRanges ("TestHeadersFootersRange")

Application.Run macroname:="WORLDOX.NewMacros.WdUpdateFooter"

End Sub
'_______________________________________

instead of

'_______________________________________
Sub TestHeadersFooters()

LoopStoryRanges ("TestHeadersFootersRange")

End Sub
'_______________________________________


To see what code does, I would suggest the following instructive exercise:

Set the VBA editing window and the Document window to be both full screen
width, but each only half the available height so that they can be placed
on top of each other.
Use a simple document with only two or three footers that contain text and
various fields, including date/time fields.
Place your cursor anywhere inside the "TestHeadersFooters" Sub and hit F8.
This will take you into the code, one step at the time. After each line of
code gets executed, you need to hit F8 again. Before hitting F8, you can
scroll the document to see whatever changes the macro has made, if any. Then
you click back inside the VBA editing window and hit F8 to resume debugging.
If you hit F5, the whole code will execute, unless you have put breaks in
the code (Place the cursor on a line and hit F9 to include a break point,
only on actual line that contain executable code - not like comments...)
This way you will see the impact of the macro and you will see how the
various loops and Next/For blocks operate. I do this all the time when I
debug, I find that it is a very useful way to debug.

--

Salut!
_______________________________________
Jean-Guy Marcil - Word MVP
(e-mail address removed)
Word MVP site: http://www.word.mvps.org
 
L

Legal Learning

Hey Jean-Guy,

The reason why I did not use Klaus' code is because it keeps asking me which
macro to run and the macro that does the work does not launch - I have to
choose it from the macros dialog window. I don't know enough on how to
change that.
Does that make any sense?

Signed,

Miss programmer illiterate
 
J

Jean-Guy Marcil

Legal Learning was telling us:
Legal Learning nous racontait que :
Hey Jean-Guy,

The reason why I did not use Klaus' code is because it keeps asking
me which macro to run and the macro that does the work does not
launch - I have to choose it from the macros dialog window. I don't
know enough on how to change that.
Does that make any sense?

I understand that it can be a bit overwhelming at first. The reason Klaus
presented that code was that each task was located in a separate sub, so
the whole things is organized and easily re-usable.
I guess Klaus' comments might have been a bit obscured to a beginner.

So, let's try again:

Klaus presented you with three Subs:

1) Private Sub LoopStoryRanges(myMacro As String)
2) Sub TestHeadersFooters()
3) Sub TestHeadersFootersRange(myRange As Range)

Any sub that has a parameter (The stuff between brackets - like (myMacro As
String) and so on) cannot be a starting point for a procedure. The parameter
means that the Sub needs that information before it can run. Sub 1) needs
the name of macro that it will in turn call from within its own code. This
name is provided by Sub 2) which calls Sub 1) (See the line "LoopStoryRanges
("TestHeadersFootersRange")" - this means that Sub 2) calls Sub 1)
[LoopStoryRanges] which will call Sub 3) [TestHeadersFootersRange]...)
The third Sub needs a range object, provided by Sub 1) ("Application.Run
myMacro, myStoryRange" - here myMacro, the name of the parameter, has the
value "TestHeadersFootersRange" because this is what the parameter received
from Sub 2) was when it called it).

So, all three together work like this (I am using my code which is based on
Klaus'):

You start with Sub 2)
All Sub 2) does is call Sub 1) giving Sub 1) the name of a macro to call
whenever it gets to that point in its code - in this case, the name of Sub
3).
Sub 1) does the job of looping through all the StoryRanges in the document
(Areas of text), and when it sets the next StoryRange it calls Sub 3)
through its parameter value.
Sub 3) tests if the StoryRange (which is a type of Range) is a footer, if
so, it tests for the presence of both Date and Time fields, if one of those
is missing, the code inside the "If lngFields = 0 Or lngFields = 1 Then"
block gets executed.
This why I said you need to add your stuff where it says 'Code Here.

I guess it would have been more linear if it had been presented thus:

1) Sub TestHeadersFooters()
2) Private Sub LoopStoryRanges(myMacro As String)
3) Sub TestHeadersFootersRange(myRange As Range)

Sorry about that.

I think you need to run other macros before and after the fields business
(the WORLDOX stuff), this is why in my previous messageI stated that you
need to use this:

'_______________________________________
Sub TestHeadersFooters()

Application.Run macroname:="WORLDOX.NewMacros.WdClearFooter"

LoopStoryRanges ("TestHeadersFootersRange")

Application.Run macroname:="WORLDOX.NewMacros.WdUpdateFooter"

End Sub
'_______________________________________

instead of

'_______________________________________
Sub TestHeadersFooters()

LoopStoryRanges ("TestHeadersFootersRange")

End Sub
'_______________________________________


As you can see, once you understand hw it all works, you can easily recycle
those Subs. If you want to call a different macro when each StoryRange is
found, change
LoopStoryRanges ("TestHeadersFootersRange")
to something like
LoopStoryRanges ("NameOfOtherMacro")

Or, you could change
Select Case myRange.StoryType
Case wdEvenPagesFooterStory, wdFirstPageFooterStory, _
wdPrimaryFooterStory
so that you have a totally different action whether the actual StoryRange is
a primary footer or not, whether it is a textbox or a footnote, etc.

Good luck.

--

Salut!
_______________________________________
Jean-Guy Marcil - Word MVP
(e-mail address removed)
Word MVP site: http://www.word.mvps.org
 

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