How does nesting of With ... End With affect scope of variables?

H

Howard Kaikow

I got bored today, so I decided to rewrite the code in KB article 316383 to
decrease the number of object references.
This resulted in a number of nested With ... End With.

The original code had a

Dim r As Integer, c As Integer

shortly before a For Next.

The original code does not use With ... End With.

I ended up with, no pun intended, the code below. If the Dim statement is
moved into more deeply nested With ... End Withs, there are compile errors.

I've included the statement twice below. I've commented out the case that
does work, so you should see a compile error.

Ordinarily such statements should be at the beginning of the procedure.
Are there any restrictions for placing PROCEDURE level Dim declaration
statement?
Where are these documented?

Imports Microsoft.Office.Interop
Public Class Form1
Inherits System.Windows.Forms.Form
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim oWord As Word.Application
Dim Pos As Double
Dim r As Integer, c As Integer
'Start Word and open the document template.
oWord = CreateObject("Word.Application")
With oWord
..Visible = True
Pos = .InchesToPoints(7)
..Documents.Add()
With .ActiveDocument
'Dim r As Integer, c As Integer ' This works
'Insert a paragraph at the beginning of the document.
With .Content
Dim r As Integer, c As Integer ' This fails
..InsertAfter(Text:="Heading 1")
..Font.Bold = True
..Paragraphs(1).SpaceAfter = 24 '24 pt spacing after paragraph.
..InsertParagraphAfter()
..Collapse(Word.WdCollapseDirection.wdCollapseEnd)
'Insert a paragraph at the end of the document.
..InsertAfter(Text:="Heading 2")
..Paragraphs(1).SpaceAfter = 6
..InsertParagraphAfter()
..Collapse(Word.WdCollapseDirection.wdCollapseEnd)
..InsertAfter(Text:="This is a sentence of normal text. Now here is a
table:")
'Insert another paragraph.
..InsertParagraphAfter()
..Paragraphs(.Paragraphs.Count).Range.Font.Bold = False
..InsertParagraphAfter()
..Collapse(Word.WdCollapseDirection.wdCollapseEnd)
'Insert a 3 x 5 table, fill it with data, and make the first row
'bold and italic.
With .Tables.Add(.Bookmarks.Item("\endofdoc").Range, 3, 5)
With .Range
..ParagraphFormat.SpaceAfter = 6
..Font.Bold = False
End With
For r = 1 To 3
For c = 1 To 5
..Cell(r, c).Range.Text = "r" & r & "c" & c
Next
Next
With .Rows.Item(1).Range.Font
..Bold = True
..Italic = True
End With
End With
End With
With .Content
'Add some text after the table.
..Paragraphs(.Paragraphs.Count).Format.SpaceAfter = 24
..Collapse(Word.WdCollapseDirection.wdCollapseEnd)
..InsertParagraphAfter()
End With
With .Paragraphs(.Paragraphs.Count)
..SpaceAfter = 24
With .Range
..Text = "And here's another table:"
..Font.Bold = False
..InsertParagraphAfter()
End With
End With
'Insert a 5 x 2 table, fill it with data, and change the column widths.
With .Tables.Add(.Bookmarks.Item("\endofdoc").Range, 5, 2)
..Range.ParagraphFormat.SpaceAfter = 6
For r = 1 To 5
For c = 1 To 2
..Cell(r, c).Range.Text = "r" & r & "c" & c
Next
Next
With .Columns
..Item(1).Width = oWord.InchesToPoints(2) 'Change width of columns 1 & 2
..Item(2).Width = oWord.InchesToPoints(3)
End With
End With
'Keep inserting text. When you get to 7 inches from top of the
'document, insert a hard page break.
With .Content
..InsertParagraphAfter()
..Collapse(Word.WdCollapseDirection.wdCollapseEnd)
Do
..ParagraphFormat.SpaceAfter = 6
..InsertAfter("A line of text")
..InsertParagraphAfter()
..Collapse(direction:=Word.WdCollapseDirection.wdCollapseEnd)
Loop While Pos >=
..Information(Word.WdInformation.wdVerticalPositionRelativeToPage)
..InsertBreak(Word.WdBreakType.wdPageBreak)
..Collapse(Word.WdCollapseDirection.wdCollapseEnd)
..InsertAfter("We're now on page 2. Here's my chart:")
..InsertParagraphAfter()
End With
'Insert a chart and change the chart.
With .Bookmarks.Item("\endofdoc").Range.InlineShapes.AddOLEObject( _
ClassType:="MSGraph.Chart.8", FileName _
:="", LinkToFile:=False, DisplayAsIcon:=False)
With .OLEFormat.Object
..charttype = 4 'xlLine = 4
With .Application
..Update()
..Quit()
End With
End With
'If desired, you can proceed from here using the Microsoft Graph
'Object model to make additional changes to the
'chart.
..Width = oWord.InchesToPoints(6.25)
..Height = oWord.InchesToPoints(3.57)
End With
'Add text after the chart.
With .Content
..InsertParagraphAfter()
..InsertAfter("THE END.")
End With
End With
End With
oWord = Nothing
'All done. Close this form.
Me.Close()
End Sub
End Class
 
H

Herfried K. Wagner [MVP]

* "Howard Kaikow said:
Dim r As Integer, c As Integer

This can be shortened to 'Dim r, c As Integer'.
The original code does not use With ... End With.

I ended up with, no pun intended, the code below. If the Dim statement is
moved into more deeply nested With ... End Withs, there are compile errors.

I've included the statement twice below. I've commented out the case that
does work, so you should see a compile error.

Ordinarily such statements should be at the beginning of the procedure.
Are there any restrictions for placing PROCEDURE level Dim declaration
statement?
Where are these documented?

Visual Basic Language Specification -- 10.2 Local Declaration Statements
<URL:http://msdn.microsoft.com/library/en-us/vbls7/html/vblrfvbspec8_3.asp>

"
Local variables, local constants, and static variables are scoped to the
statement block in which they are declared.
"

Visual Basic Language Reference -- 'Dim' Statement
<URL:http://msdn.microsoft.com/library/en-us/vblr7/html/vastmdim.asp>

"
Variables declared inside a procedure or a block are accessible only
from within that procedure or block.
"

Notice that the variable is not visible in front of the line it's dimmed
on:

\\\
i = 10
Dim i As Integer
///

.... will fail to compile.
Imports Microsoft.Office.Interop
Public Class Form1
[A log of code without indentation]

Do you have an indented version of the source code too :)?
 
D

David

I got bored today, so I decided to rewrite the code in KB article 316383 to
decrease the number of object references.

That's pretty bored :)

I'm answering the post below, but I strongly suspect you already know
this stuff and have a typo someplace, or just got confused with
commenting and uncommenting lines in your test code.
This resulted in a number of nested With ... End With.

The original code had a

Dim r As Integer, c As Integer

shortly before a For Next.

The original code does not use With ... End With.

I ended up with, no pun intended, the code below. If the Dim statement is
moved into more deeply nested With ... End Withs, there are compile errors.

Looking that the original code, it only Dims r and c once. Then just
re-assigns them in the loop. The With Statements don't have much to do
with it. With-End With defines a statment block, as does For..Next, and
it's illegal for a declaration in a statement block to shadow a
declaration in an enclosing block.

Dim r As Integer, c As Integer
'Start Word and open the document template.
oWord = CreateObject("Word.Application")
With oWord
.Visible = True
Pos = .InchesToPoints(7)
.Documents.Add()
With .ActiveDocument
'Dim r As Integer, c As Integer ' This works
'Insert a paragraph at the beginning of the document.
With .Content
Dim r As Integer, c As Integer ' This fails
.InsertAfter(Text:="Heading 1")


Both the commented and the uncommented version fail for me. Both are
attempting to shadow the "Dim r as Integer" at the top of the code
snippet there. The original code declares 'r' and 'c' only once.

Are you sure your commented line above works if uncommented? I can't
duplicate this, and I suspect there's either been a copying error or I'm
not understanding your question. What exactly is the compile error
you're getting?

I've included the statement twice below. I've commented out the case that
does work, so you should see a compile error.

Ordinarily such statements should be at the beginning of the procedure.

I don't agree, but that's neither here nor there, and totally irrelevant.
Are there any restrictions for placing PROCEDURE level Dim declaration
statement?
Where are these documented?

The language spec is a good start. The section on statements is at:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbls7/html/vblrfvbspec8.asp
 
H

Howard Kaikow

Yes, I was bored.
Trying not to think today, I'm scheduled to have oral surgery tomorrow.

One goal is to remove my foot from my mouth so "I'll have a leg to stand
on".

I'll post the actual VB .NET in a few minutes.

The error is reproducible here.
 
D

David


Maybe I'm just missing something, but there doesn't seem to be anything
particularly confusing there. The specs are crystal clear, and to my
mind at least, exactly what one would expect. The scope of a local
variable is from the point of declaration to the end of the current
statement block.

With..End With pairs constitute a statement block, as do For..Next
pairs.

Essentially what you have with your example is this...

**************************
With SomeVariable
Dim r as integer ' First declaration

With SomeOtherVariable
Dim r1 as integer ' Second declaration
r = 5
End With
' r1 is now out of scope

With AThirdVariable
r = 23 ' this is legal
r1 = 24 ' this is illegal
End With
End With
' r is now out of scope

r = 25 ' this is illegal

***************************

What do you feel is buggy about this, or what about it doesn't follow the
specification?

BTW, if you think this doesn't represent your long sample accurately, I
think you need to look at the sample again. r and c are definitely used
outside the scope of the second declaration. The second declaration
falls out of scope at line 111, but r and c are still being used at
lines 132-133. The first declaration (the one commented out in the
same) doesn't fall out of scope until line 179, that's why it works
fine.

One other thing, you actually don't need these declarations at all, you
can declare the variables within the loops, which give them the most
limited possible scope

For r AS Integer = 1 To 5
For c As Integer = 1 To 5
Next
Next
 
H

Howard Kaikow

It's clear.

As I said in an earlier post, I was trying not to think today, due to the
scheduled oral surgery tomorrow.
This posting is sufficient proof that I succeeded as it is clear that I was
not thinking.
 

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