PDA

View Full Version : Solved: How make last char of a table = paragraph mark???



DaVinney
03-30-2011, 08:01 PM
I have a VBA routine that successfully processes (modifies) each paragraph within each table in a document. Only problem is that the very last paragraph in the table may not have a paragraph mark after it, so the routine does not process the very last paragraph.

My question is how can I test whether there is a paragraph mark as the last character, and if not how can I perhaps temporarily place one there so the routine runs properly?

gmaxey
03-31-2011, 04:45 AM
Try:

Sub ScratchMacro()
'A quick macro scratch pad created by Greg Maxey
Dim oTbl As Word.Table
For Each oTbl In ActiveDocument.Tables
If oTbl.Cell(oTbl.Rows.Count, oTbl.Columns.Count).Range.Characters.Last.Previous = Chr(13) Then
'It's there
'Call your process
Else
'It's not there so add it
oTbl.Cell(oTbl.Rows.Count, oTbl.Columns.Count).Range.InsertAfter Chr(13)
'Call your process
'Remove it
oTbl.Cell(oTbl.Rows.Count, oTbl.Columns.Count).Range.Characters.Last.Previous.Delete
End If
Next
End Sub

DaVinney
03-31-2011, 07:27 AM
Fantastic! Many thanks, Greg :hi:

Just what I needed.

DaVinney
04-04-2011, 10:30 AM
I'm trying to re-work the subject macro a bit.

:help How can I tell whether the selected paragraph is the last paragraph in a table?

pseudo code:
If Selection.Tables(1).Range.Paragraph.Number = Tables(1).Paragraphs.Count Then 'do something

DaVinney
04-04-2011, 11:44 AM
Try:

Sub ScratchMacro()
'A quick macro scratch pad created by Greg Maxey
Dim oTbl As Word.Table
For Each oTbl In ActiveDocument.Tables
If oTbl.Cell(oTbl.Rows.Count, oTbl.Columns.Count).Range.Characters.Last.Previous = Chr(13) Then
'It's there
'Call your process
Else
'It's not there so add it
oTbl.Cell(oTbl.Rows.Count, oTbl.Columns.Count).Range.InsertAfter Chr(13)
'Call your process
'Remove it
oTbl.Cell(oTbl.Rows.Count, oTbl.Columns.Count).Range.Characters.Last.Previous.Delete
End If
Next
End Sub

On second thought...
How can I remove the last paragraph mark for only the tables that had a paragraph mark added by Greg's routine above?

In other words, the routine would process all tables in the document, adding a para mark to all tables not ending with a para mark, then just before the sub concludes, it would remove the added para mark only for those tables that had para marks previously added???

macropod
04-04-2011, 04:31 PM
Hi DaVinney,

Here's one way:
Sub Demo()
Dim i As Integer, StrTbl As String
With ActiveDocument
For i = 1 To .Tables.Count
With .Tables(i).Range
If .Cells(.Cells.Count).Range.Characters.Last.Previous = Chr(13) Then
'It's there
'Call your process
Else
'It's not there so add it
.Cells(.Cells.Count).Range.InsertAfter Chr(13)
StrTbl = "," & i & StrTbl
'Call your process
End If
End With
Next
For i = 1 To UBound(Split(StrTbl, ","))
With .Tables(Split(StrTbl, ",")(i)).Range
.Cells(.Cells.Count).Range.Characters.Last.Previous.Delete
End With
Next
End With
End Sub

DaVinney
04-05-2011, 09:00 AM
Thank you. I'm trying to work this concept into my code. Could you please explain this line (the part in parens):

With .Tables(Split(StrTbl, ",")(i)).Range


How does that table reference work?

DaVinney
04-05-2011, 01:54 PM
Similar but more tricky...

How could I have the routine process all tables in the document, adding a para mark to ALL CELLS within each table not ending with a para mark, then just before the sub concludes, it would remove the added para mark only for those CELLS that had para marks previously added???

I can't figure out an array method (or however) to get such a routine to work :banghead:

macropod
04-05-2011, 02:21 PM
Hi DaVinney,

The 'With .Tables(Split(StrTbl, ",")(i)).Range' expression takes the StrTbl string compiled earlier in the process to identify which tables to process, then uses the Split function to separate out each table's index, which tells the loop which table(s) had the extra paragraph added to the last cell.

As for your follow-up question, I have to wonder whether this is for a real-world project, or just for amusement ... One has to ask why it matters whether there's a conventional paragraph marker after every paragraph in a cell. I can't think of any good reason to put one in just to remove it later on.

DaVinney
04-05-2011, 02:39 PM
The 'With .Tables(Split(StrTbl, ",")(i)).Range' expression takes the StrTbl string compiled earlier in the process to identify which tables to process, then uses the Split function to separate out each table's index, which tells the loop which table(s) had the extra paragraph added to the last cell.Yes, I realize that much. More specifically, I was wondering what is the purpose of (i). I hadn't seen that before.


As for your follow-up question, I have to wonder whether this is for a real-world project, or just for amusement ... One has to ask why it matters whether there's a conventional paragraph marker after every paragraph in a cell. I can't think of any good reason to put one in just to remove it later on.Yes, it's a real project. Going back to my OP, I've come across a case in which it is not only the last para mark of the last cell in a table that is fouling my routine, it can be any cell within a table that causes the hiccup. So, I'm trying to add steps where the routine first detects all cells not having a trailing para mark (since this is what is causing the hiccup), if absent it then places one there, then I run my core routine that processes all paragraphs, then I need the cleanup step before the sub exits that removes the trailing paragraph in all cells that originally did not have one (i.e., leaves the cells in their original state).

Sorry if it seems these questions are only for amusement, but they are not.

Thanks for your help!

macropod
04-05-2011, 03:39 PM
Hi DaVinney,

The (i) tells the preceding Split(StrTbl, ",") function which entry to retrieve. For example, if comma-delimited StrTbl string holds, ',3,4,7,11', when i in the following loop gets to 3, Split(StrTbl, ",")(i) equals Split(StrTbl, ",")(3) and the 3rd entry in the string (7) will be returned.
For i = 1 To UBound(Split(StrTbl, ","))
msgbox Split(StrTbl, ",")(i)
Next

OK, so it's for a real project, but I think you need to explain what it is you're trying to do. As I said, I can't think of any good reason to add these paragraph marks just to remove them later on. It seems to me a different approach is probably called for.

DaVinney
04-05-2011, 04:11 PM
No doubt you're right, a different approach would probably be better overall. But at this point my project has grown to a point that I'm looking for a band-aid fix to avoid a major re-work.

Thanks for your explanations and examples. I think I may be close.

If you decide you can help me with the para marks in cells thing, I'd be grateful.

Mike

macropod
04-05-2011, 05:09 PM
Hi Mike,

Try:
Sub Demo()
Dim i As Integer, j As Integer, StrTbl As String
With ActiveDocument
For i = 1 To .Tables.Count
With .Tables(i).Range
For j = 1 To .Cells.Count
On Error Resume Next
If .Cells(j).Range.Characters.Last.Previous <> Chr(13) Then
'It's not there so add it
.Cells(j).Range.InsertAfter Chr(13)
'Store the table # & cell #, separated by a '-'
'Each table/cell pair is separated by a comma
StrTbl = "," & i & "-" & j & StrTbl
End If
Next
'Call your process
End With
Next
'Restore the tables to their previous state
For i = 1 To UBound(Split(StrTbl, ","))
'Get the table # via a nested split of the StrTbl string
With .Tables(Split(Split(StrTbl, ",")(i), "-")(0)).Range
'Get the cell # via a nested split of the StrTbl string
'and delete the final paragraph mark
.Cells(Split(Split(StrTbl, ",")(i), "-")(1)).Range.Characters.Last.Previous.Delete
End With
Next
End With
End Sub

DaVinney
04-05-2011, 05:46 PM
macropod!

You have made me very happy. It works!

I would never have come up with the nested split method. You have saved me from a restless night thinking about this.

Many many thanks :bow:


Mike

macropod
04-05-2011, 08:39 PM
Hi Mike,

I'll leave you with a little challenge:
Figure out why the 'For i = 1 To UBound(Split(StrTbl, ","))' loop starts at 1, and is able to do so, while the Split for the table # in
'With .Tables(Split(Split(StrTbl, ",")(i), "-")(0)).Range' uses 0.

DaVinney
04-06-2011, 10:13 AM
I'll leave you with a little challenge:
Figure out why the 'For i = 1 To UBound(Split(StrTbl, ","))' loop starts at 1, and is able to do so, while the Split for the table # in
'With .Tables(Split(Split(StrTbl, ",")(i), "-")(0)).Range' uses 0.
My answer:
The counting of array elements starts at 0 by default. The leading comma delimiter in the StrTbl string would cause a return of a blank value after the split if you had used 0 vice 1 to start the loop. The use of 0 in the Split for the table # returns the correct first element in the array since there is no leading delimiter resulting from the nested loop.


...or something like that :bug:

macropod
04-06-2011, 02:18 PM
Correct. Except for the 'or something like that' I'd say go to the top of the class!

gmaxey
04-06-2011, 04:01 PM
Mike,

Glad to see you found a solution and sorry for not getting back to you on your follow on question. I have been out of town to deal with a familiy issue and have had very limited computer access.

DaVinney
04-06-2011, 07:46 PM
Absolutely no problem, Greg. Thanks for your help.

Hope things are okay with your family.

gmaxey
04-07-2011, 06:25 AM
Paul,

Considering you are probably the world master at nesting field codes I am not surprised that you opted to nest a few Split functions to store the affected cell locations. While I have access to this forum I don't have access to Word right now so I wasn't able to help Mike futher.

Thinking about it now I would have probably proposed altering the affected cell with shading or something and then looped back through at the end and process only those altered affected cells.

Good job!!

Frosty
04-07-2011, 12:13 PM
Wow, my mind is having a hard time processing the nested split function. What a neat idea. As a brain teaser, here's an alternative way of doing this (just for fun). Mostly this is just a demo of how useful the collection object can be.

It seems to me, however, that both of these things are "breakable" in that you're storing some values which may or may not still point to the right location after you've done whatever processing you want to do.


Sub Demo2()
Dim oTable As Table
Dim oCell As Cell
Dim colChangedRanges As Collection
Dim rngWhere As Range

'initialize our collection
Set colChangedRanges = New Collection

'go through each table
For Each oTable In ActiveDocument.Tables
'and each cell in that able
For Each oCell In oTable.Range.Cells
'see if the last character is a carriage return
If oCell.Range.Characters.Last.Previous <> Chr(13) Then
'if not, add one
oCell.Range.InsertAfter Chr(13)
'and add this cell's range to our collection
colChangedRanges.Add oCell.Range
End If
Next
'do your processing for each table here?
Next

'or do your processing for all tables here?

'now go back through our collection stored ranges
For Each rngWhere In colChangedRanges
'and delete the second to last character (last one is the end of cell marker, which you can't touch)
rngWhere.Characters.Last.Previous.Delete
Next
End Sub

macropod
04-07-2011, 02:27 PM
Hi Greg,

Thanks. 'Twas the first time I've ever had recourse to a nested Split - I've nested Replace functions a few levels deep before, though. There are other ways of tackling that particular problem without resorting to shading and the like (eg separate arrays/strings for tables and cells), but the nested Split seemed the cleanest.

DaVinney
04-07-2011, 07:06 PM
Since I've already distributed a fix using macropod's nested split method, I'm going to keep Frosty's collections method in my back pocket for now.

I have another question related to my 'For Each Paragraph' looping routine (when it encounters a table). Calls for a separate thread, but I'll do some due diligence on my own to see if I can figure it out first.

Thanks all!

Mike