Solved

Automating Word - Tables: Can Set Borders But Can't Remove Them

Posted on 2011-09-27
24
1,113 Views
Last Modified: 2012-05-12
I'm using VB.Net 2010 Professional to create a MS Word document.  Because of the miriad of versions of Word, I am using late binding.

I create a table with this code:
nRows = UBound(DataArray) + 4  'UBound(DataArray) = 18 (Verified)
nCols = 10
oRange = m_oDoc.Bookmarks.Item("\endofdoc").Range
oTable = m_oDoc.Tables.Add(oRange, nRows, nCols)

oTable.Rows.Alignment = wdAlignRowCenter
oTable.PreferredWidthType = wdPreferredWidthPoints
oTable.PreferredWidth = m_oWord.InchesToPoints(7.0)

oTable.Rows(1).HeadingFormat = True

Open in new window


I set all the borders in that tale with this code:
For Each bord In m_aBorder
    oTable.Borders(bord).LineStyle = wdLineStyleSingle
Next
'm_aBorder is an array defined at module level as this:
'Dim m_aBorder() As Integer = {wdBorderLeft, _
'                              wdBorderRight, _
'                              wdBorderTop, _
'                              wdBorderBottom, _
'                              wdBorderHorizontal, _
'                              wdBorderVertical}

Open in new window

Then I remove a section of the borders with this code:
oRange = m_oDoc.Range(Start:=oTable.Cell(1, 1).Range.Start, _
                      End:=oTable.Cell(3, 6).Range.End)
oRange.Borders(wdBorderLeft).LineStyle = wdLineStyleNone
oRange.Borders(wdBorderTop).LineStyle = wdLineStyleNone
oRange.Borders(wdBorderHorizontal).LineStyle = wdLineStyleNone
oRange.Borders(wdBorderVertical).LineStyle = wdLineStyleNone

Open in new window


The last line throws the following error:
"The requested member of the collection does not exist."
I presume it is refering to Borders(wdBorderVertical) (wdBorderVertical = -6)

Why did it not complain when the borders were set, but complains when I'm tring to remove some of them?

TIA
0
Comment
Question by:Clif
  • 10
  • 8
  • 3
  • +1
24 Comments
 
LVL 27

Expert Comment

by:MikeToole
ID: 36715865
I can confirm the problem, but I don't have a solution.
It has nothing to do with VB.Net, I reproduced it in VBA in a Word document.
I suggest that you target the Word gurus for further progress.

What I found:
The attached code is a VBA conversion of the .net code you posted, just open the code behind on an empty word document (alt+F11) and paste it into the ThisDocument module. I tweaked it to assign a different LineStyle to each of the Border Types so that I could see what was happening.
With the code halted on a Breakpooint just after the oRange.Select, I ran some tests in the Immediate window with the results shown after the end of the Sub.

Using BorderType to index the Borders collection of the Table to print the LineStyle produces exactly what would be expected.
However, doing the same over the Range produces nonsense.
The final test iterates the whole of the Range Borders collection to show 7 members (compared to the 8 in the Table Borders collection)

That's about as far as I can take it.
Good Luck!
The doumentation for Word does state that different objects can have differing numbers of BorderTypes in the Borders collection, but  
Public Sub tryIt()
    Const BorderTypes = 6
    Dim m_aBorder(BorderTypes) As Integer
    m_aBorder(1) = wdBorderLeft ' -2
    m_aBorder(2) = wdBorderRight ' -4
    m_aBorder(3) = wdBorderTop ' -1
    m_aBorder(4) = wdBorderBottom ' -3
    m_aBorder(5) = wdBorderHorizontal ' -5
    m_aBorder(6) = wdBorderVertical ' -6
    
    On Error Resume Next
        Me.Tables(1).Delete
    On Error GoTo 0
    
    nRows = 18 'UBound(DataArray) + 4  'UBound(DataArray) = 18 (Verified)
    nCols = 10
    Dim oRange As Object
    Set oRange = Me.Bookmarks.Item("\endofdoc").Range
    
    Set OTable = Me.Tables.Add(oRange, nRows, nCols)
    
    OTable.Rows.Alignment = wdAlignRowCenter
    OTable.PreferredWidthType = wdPreferredWidthPoints
    OTable.PreferredWidth = Application.InchesToPoints(7#)
    
    OTable.Rows(1).HeadingFormat = True
    For i = 1 To BorderTypes
        OTable.Borders(m_aBorder(i)).LineStyle = i  'wdLineStyleSingle ' +1
    Next
    
    
    Set oRange = Me.Range(Start:=OTable.Cell(1, 1).Range.Start, _
                          End:=OTable.Cell(3, 6).Range.End)
    oRange.Select
    ' ----------- 
    oRange.Borders(wdBorderLeft).LineStyle = wdLineStyleNone
    oRange.Borders(wdBorderTop).LineStyle = wdLineStyleNone
    oRange.Borders(wdBorderHorizontal).LineStyle = wdLineStyleNone
    oRange.Borders(wdBorderVertical).LineStyle = wdLineStyleNone

End Sub

for i = 1 to 6: ?OTable.Borders(m_aBorder(i)).LineStyle: next
 1 
 2 
 3 
 4 
 5 
 6 
for i = 1 to 6: ?oRange.Borders(m_aBorder(i)).LineStyle: next
 1 
 6 
 3 
 5 
 5 
for each i in oRange.Borders : ? i, i.LineStyle: next
True           3 
True           1 
True           5 
True           6 
True           5 
False          0 
False          0

Open in new window

0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36715877
Woops, bad editing ...
The doumentation for Word does state that different objects can have differing numbers of BorderTypes in the Borders collection, but  I would expect that the indexing of the Borders collection by BorderType would be consistent, and it seems not to be.
That's about as far as I can take it.
Good Luck!
0
 
LVL 76

Expert Comment

by:GrahamSkan
ID: 36720147
Yes. For a table, a Vertical border is the same as a Left or a Right border for a cell. Word seems to make an attempt at interpreting the borders within your range specification, but fails for the Vertical.

It would be better to specify which object you want to work with. If you want to set the borders for part of a table, I would suggest treating each cell. This is a modification of MikeToole's macro:
Public Sub tryIt()
    Dim nRows As Integer
    Dim nCols As Integer
    Dim i As Integer
    Dim oTable As Table
    Dim r As Integer
    Dim c As Integer
    
    Const BorderTypes = 6
    Dim m_aBorder(BorderTypes) As Integer
    m_aBorder(1) = wdBorderLeft ' -2
    m_aBorder(2) = wdBorderRight ' -4
    m_aBorder(3) = wdBorderTop ' -1
    m_aBorder(4) = wdBorderBottom ' -3
    m_aBorder(5) = wdBorderHorizontal ' -5
    m_aBorder(6) = wdBorderVertical ' -6
    
    If ActiveDocument.Tables.Count > 0 Then
        ActiveDocument.Tables(1).Delete
    End If
    
    nRows = 18 'UBound(DataArray) + 4  'UBound(DataArray) = 18 (Verified)
    nCols = 10
    Dim oRange As Object
    Set oRange = ActiveDocument.Bookmarks.Item("\endofdoc").Range
    
    Set oTable = ActiveDocument.Tables.Add(oRange, nRows, nCols)
    
    oTable.Rows.Alignment = wdAlignRowCenter
    oTable.PreferredWidthType = wdPreferredWidthPoints
    oTable.PreferredWidth = Application.InchesToPoints(7#)
    
    oTable.Rows(1).HeadingFormat = True
    For i = 1 To BorderTypes
        oTable.Borders(m_aBorder(i)).LineStyle = i  'wdLineStyleSingle ' +1
    Next
    
    
    For c = 1 To 6
       For r = 1 To 3
           With oTable.Cell(r, c)
                .Borders(wdBorderLeft).LineStyle = wdLineStyleNone
                .Borders(wdBorderTop).LineStyle = wdLineStyleNone
            End With
        Next r
    Next c
 
End Sub

Open in new window

0
 
LVL 10

Author Comment

by:Clif
ID: 36814832
I had an issue at one time where I was setting the borders for each cell.  It took two to three minutes for some reports to "paint" all the borders.

But I will try it (in a bit, right now I have a huge fire to put out) since the way it looks right now is fugly.  :)
0
 
LVL 40

Accepted Solution

by:
Vadim Rapp earned 500 total points
ID: 36815208
Try this:

oRange = m_oDoc.Range(Start:=oTable.Cell(1, 1).Range.Start, _
                      End:=oTable.Cell(3, 6).Range.End)
oRange.Select
Selection.Borders(wdBorderLeft).LineStyle = wdLineStyleNone
Selection.Borders(wdBorderTop).LineStyle = wdLineStyleNone
Selection.Borders(wdBorderHorizontal).LineStyle = wdLineStyleNone
Selection.Borders(wdBorderVertical).LineStyle = wdLineStyleNone
0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36815231
I still think that it's a bug in Word.
You can address all the range borders at once using the InsideLIneStyle and OutSideLinestyle properties:

oRange.Borders.InsideLineStyle = wdLineStyleNone
    Effect: removes all the horizontal borders, but leaves the vertical ones
oRange.Borders.OutsideLineStyle = wdLineStyleNone
    Effect: removes all outside borders, both horizontal and vertical.


0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36815287
Ah, using Selection does the trick, this works to remove all borders within the range:

    oRange.Select
    Selection.Borders.InsideLineStyle = wdLineStyleNone

That makes it look even more like a bug in Range.Borders
0
 
LVL 40

Expert Comment

by:Vadim Rapp
ID: 36815456
It's not a bug. If you open Locals window in VBA and compare the contents of oRange vs. Selection, you will see that they are different. oRange has more cells because it includes all columns in the table, i.e. all cells from (1,1) to (3,6), which includes columns 7 - 10. The range is not rectangular, which is why Word drops the border it can't apply. But it becomes rectangular when you select it, and that's when the border becomes possible.
0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36816183
Ah, I see, the Range selection traverses the table horizontally, wrapping at the end of the row rather than staying in the column bounds to produce a raw cells collection that includes extra cells that are not in the range .

However, it has sufficient self-knowledge to know that the following statement shouldn't apply to the cells not in the column range of the selection.
    oRange.Borders(wdBorderHorizontal).LineStyle = wdLineStyleNone
I would have thought that the same would apply to the VerticalBorder.

for each c in oRange.Cells: ?c.RowIndex, c.ColumnIndex: next
 1             1 
 1             2 
 1             3 
 1             4 
 1             5 
 1             6 
 1             7 
 1             8 
 1             9 
 1             10 
 2             1 
 2             2 
 2             3 
 2             4 
 2             5 
 2             6 
 2             7 
 2             8 
 2             9 
 2             10 
 3             1 
 3             2 
 3             3 
 3             4 
 3             5 
 3             6

Open in new window

0
 
LVL 40

Expert Comment

by:Vadim Rapp
ID: 36816593
> However, it has sufficient self-knowledge to know that the following statement shouldn't apply to the cells not in the column range of the selection.

So there's still room for improvement in Word, for the future versions :-) You probably can sign up somewhere at connect.microsoft.com and send them this suggestion. Or, if you want it actually addressed, open support incident, report, show business justification ($$ your business is losing because of this inefficiency), and they will consider fixing it. Looking at the recent developments such as Ribbon, it doesn't look like this might be one of their priorities, but who knows.
0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36816778
Well, I guess it's not that critical - especially when the Select() resolves it.
Overall I'm impressed at how well the Word object model has survived the passage of time to remain relevant and useful.

0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 40

Expert Comment

by:Vadim Rapp
ID: 36817291
> Word object model has survived the passage of time to remain relevant and useful.

Yep - escaped being ruined by new new generations. I guess, some things are still possible only as WordBasic operations, right? MSKB 216026, FilePrintSetup, and more.
0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36817508
WordBasic! Now you're rolling back the years :-)
0
 
LVL 40

Expert Comment

by:Vadim Rapp
ID: 36817522
It's not me... it's still the only way to do some things.
0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36817654
I'd forgotten - your comment took me back to days gone by, when that was still a fresh memory.
0
 
LVL 40

Expert Comment

by:Vadim Rapp
ID: 36817696
I recall this fact about worbasic whenever I see shiny ad in IT magazine etc. implying that upgrade, upgrade, upgrade is the way to be more productive. Proud faces of people who upgraded (all very young, for some reason).

We still run sql server 2000. When I tried 2008, which I thoroughly beta tested for MS, in 15 minutes I had the first query that takes 5 seconds in sql 2000 but took 2 minutes in 2008. Sent to MS. They admitted and promised that will be working on this problem in future versions - to which I replied that they already worked on it, and very successfully, in the past ones.
0
 
LVL 10

Author Comment

by:Clif
ID: 36817942
vadimrapp1,
I'd like to try your suggestion before I go though each cell individually to set the borders.

However, it's not recognizing "Selection" a a keyword.

Remember, I'm doing this within VB.Net 2010, and using late binding.  How do I set the Selection object?

I tried
Dim Selection As Object = oRange.select()

I have also tried:
m_oDoc.Selection.Borders(wdBorderLeft).LineStyle = wdLineStyleNone

and:
oRange.Selection.Borders(wdBorderLeft).LineStyle = wdLineStyleNone

But none worked.
0
 
LVL 40

Expert Comment

by:Vadim Rapp
ID: 36818204
Selection is a property of the Application.
0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36890184
So this should work in your original code to remove all borders within selection

    oRange = m_oDoc.Range(Start:=oTable.Cell(1, 1).Range.Start, _
                      End:=oTable.Cell(3, 6).Range.End)
    oRange.Select
    oRange.Application.Selection.Borders.InsideLineStyle = wdLineStyleNone
0
 
LVL 40

Assisted Solution

by:Vadim Rapp
Vadim Rapp earned 500 total points
ID: 36891085
I'm sure the code already has Application variable somewhere, otherwise where would it obtain oDoc from. Something like

oApp = createobject("word.application")
odoc = oApp.add MyTemplate or ...open MyFile
0
 
LVL 27

Expert Comment

by:MikeToole
ID: 36891126
Yes, but grabbing it from the Range object meant that the posted code should work without edit - and it makes no difference to the outcome.
 
0
 
LVL 10

Author Closing Comment

by:Clif
ID: 36891243
That worked perfectly, once I was able to use the Selection object.

Thanks.
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

This article describes some techniques which will make your VBA or Visual Basic Classic code easier to understand and maintain, whether by you, your replacement, or another Experts-Exchange expert.
Recently Microsoft released a brand new function called CONCAT. It's supposed to replace its predecessor CONCATENATE. But how does it work? And what's new? In this article, we take a closer look at all of this - we even included an exercise file for…
This video shows where to find templates, what they are used for, and how to create and save a custom template using Microsoft Word.
The viewer will learn how to use the =DISCRINV command to create a discrete random variable, use this command to model a set of probabilities and outcomes in a Monte Carlo simulation, and learn how to find the standard deviation of a set of probabil…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now