Consulting

Results 1 to 12 of 12

Thread: Get range for text columns

  1. #1

    Get range for text columns

    Is it possible to set a range for a text column (newspaper-style).

    I'm trying something along these lines:
    [vba]
    Sub test()

    Dim iViewType As Integer
    Dim iLine As Integer
    Dim oRng As Word.range
    Dim sec As Word.Section

    iViewType = ActiveWindow.View.Type: ActiveWindow.View.Type = wdPrintView
    Options.Pagination = True

    Dim i As Long
    With ActiveDocument
    For i = 1 To .Sections.count
    With .Sections(i)
    If .PageSetup.TextColumns.count > 1 Then
    oRng.SetRange Start:???, End:=???
    ' or
    ' iLine = oRng.ComputeStatistics(wdStatisticLines)
    iLine = oRng.Information(wdFirstCharacterLineNumber)
    MsgBox iLine
    End If
    End With
    Next i
    End With

    End Sub[/vba]
    On msdn were this question originated (http://social.msdn.microsoft.com/For...c-3deaf9e9d8e2) JosephFox suggested using something like the following, the problem is that my knowledge is too limited to make it work, (also as he says it will be very slow (if I can get it to work)):
    [vba]
    Dim rangeStart As Integer
    Dim outOfColFlag As Integer
    Dim curColumn As Integer
    Dim range As range
    Dim content As range
    Dim doc As ActiveDocument
    Dim count As Integer
    Dim totalChars As Integer
    Dim totalTextColumns As TextColumns

    totalChars = doc.content.range.Text.count
    curColumn = 1
    rangeStart = 0
    outOfColFlag = -1
    Set totalTextColumns As doc.PageSetup.TextColumns


    For count = 1 To totalChars - 1 Step 1

    'Move range position forward one character
    range.SetRange(Start:=count, End:=count)

    'Get text column, if we're in a text column
    If range.PageSetup.TextColumns.count > 0 Then

    'If we're transferring into a text column, update flag and range
    If outOfColFlag < 0 Then
    outOfColFlag = 1
    rangeStart = range.Start
    End If

    If range.PageSetup.TextColumns.Item(1) <> totalTextColumns(curColumn) Then
    'We've transfered into a new text column. Increment column index and get lines
    curColumn = curColumn + 1

    Dim lines As Integer
    range.Start = rangeStart
    rangeStart = range.Start 'This position may be the start of a new column
    range.End = range.End - 1
    lines = range.ComputeStatistics(wdStatisticLines)

    End If
    Else
    If outOfColFlag > -1 Then
    'We're transferring out of a text column. Increment column index and get lines for the column we've left
    curColumn = curColumn + 1

    Dim lines As Integer
    range.Start = rangeStart
    range.End = range.End - 1
    lines = range.ComputeStatistics(wdStatisticLines)

    'Set flag
    outOfColFlag = -1
    End If
    End If

    Next count[/vba]
    He isn't available to follow-up so I turn to the VBA Express experts.

  2. #2
    VBAX Master
    Joined
    Feb 2011
    Posts
    1,480
    Location
    Do you mean you'd like to have a function which, basically, you could use to select all the text of column 2 of a 3 column section?

  3. #3
    I need to get the total number of lines per column. I would use this function if I could set a text column as a range.

    [VBA]Function lngLineCount(ByVal rng As Range) As Long
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''
    ''' Function: lngLineCount
    ''' Comments: Return LONG the count of lines in the given range.
    ''' Arguments: RANGE The range of text being examined.
    ''' Returns: LONG
    ''' Comments:
    '''
    ''' Date Developer Action
    ''' --------------------------------------------------------------------------
    ''' 2011/02/11 Chris Greaves Created
    '''
    Dim lngS As Long
    lngS = lngLineStart(rng)
    Dim lngE As Long
    lngE = lngLineEnd(rng)
    lngLineCount = lngE - lngS + 1
    If lngLineCount = 0 Then ' only a part of line was given
    lngLineCount = 1
    Else
    End If
    'Sub TESTlngLineCount()
    ' MsgBox lngLineCount(Selection.Range.Paragraphs(1).Range)
    'End Sub
    End Function
    [/VBA]

  4. #4
    VBAX Master
    Joined
    Feb 2011
    Posts
    1,480
    Location
    I'm telling you... you'd spend less time learning how to make tables work for these purposes, MacroShadow. You see how much effort and questions you have to raise *solely* for the purpose of text wrapping in a newspaper column? You should really really think about how much effort you're putting into making something work which is fundamentally broken, rather than the effort you'd put into using tables instead.

    Apart from the education of getting better at vba, the reason you're finding little pieces of solutions (rather than the whole thing), is because people with a lot more experience already know that the textcolumns area of Word is just a stinky piece of poo... so no comprehensive solutions have really been hashed out.

    I honestly think your time would be better served investigating:
    1. How to use tables in Word to do newspaper-style desktop publishing
    2. How to "convert" existing documents into this new style.

    If I were to proceed on this, I would work on a function which started with my current page range (ActiveDocument.Bookmarks("/page").Range).

    And from there I would attempt to see
    1) how many columns I'm looking for (2,3, etc)
    2) define the range of each of the columns on this page
    3) articulate that back as a collection of those column ranges

    Utilize this
    Dialogs(wdDialogFormatColumns).ColumnNo (which returns the column number the selection is in), or perhaps the .Information(wdFirstCharacterColumnNumber).

    I think the methodology described by the Joseph Fox is slower than it needs to be, since it starts with the entire activedocument.content, rather than just the current page (which is what you care about).

    However, I can't emphasize this point enough: the hardest part about what you're doing is trying to use code to describe things which are really really fluid: pages and text columns within those pages. One of the reasons this is so hard is because it fluid. One of the reasons tables work so well for this is because a table per page gives you your "page" concept, and the table's columns takes care of that. And the only thing you "lose" as the end-user is the automatic text wrapping.

    Now, I haven't tried this... but you could also use "linked" text boxes (at least, it appears available in Word 2003). Having never used that before, I don't really know the pros and cons, but those allow placement *and* the text wraps between them.

  5. #5
    I do have every intention of perusing the table route as I've mentioned before, when I do I'm sure you will notice. Since I have a working solution (it probably isn't water-tight but so far it hasn't sprung any leaks) I figured I'd try to enhance it before moving on.
    This question is related to totally automating the process with out any user input. If I can't get it to work, not much harm done, since my solution works for manual adjustment.
    Would it be possible to throw together an example of the function you mentioned? I'm not sure how to go about the steps you outlined, besides isn't it easier (or at least not as hard) to learn how a clock ticks by taking one apart rather then trying to build one without the necessary knowledge)?
    Your idea of using linked text boxes does sound interesting, I may get around to it after following-up on the table route.

  6. #6
    VBAX Master
    Joined
    Feb 2011
    Posts
    1,480
    Location
    If the sample was easy, I would have posted it rather than giving you a diatribe about how what you're doing is hard *grin*

    I'm curious about *how* hard it is, so I was going to give that a whirl (although it will end up being similar to the caveats in the other thread about printing the last "page" of a section)... correctly identifying these kinds of ranges can be highly dependent on a number of settings. So a proof-of-concept would be just that. Not production-ready, since you'd likely need to account for a number of scenarios which would only complicate the concept part of the "proof."

    So, to sum up: it's hard, I've got work to do, but I'd like to give you a clock to take apart at some point, but maybe in a few hours. Just don't rely on the clock's alarm if you've got to be somewhere really important

  7. #7
    Thank you I'm looking forward to it.

  8. #8
    VBAX Master
    Joined
    Feb 2011
    Posts
    1,480
    Location
    Here's a proof of concept... if you're not a fan of the immediate window, you can try adjusting the parameters you pass by throwing different section numbers at it, a la...

    fGetColumnRange(2, ActiveDocument.Sections(3).Range)
    or
    fGetColumnRange(3).Select
    [vba]
    'pass in a column number, and it will be returned as the range
    'if no specific column passed, then the current column is returned
    'this routine relies on the selection object, unfortunately
    Public Function fGetColumnRange(iColNum As Integer, Optional rngSearch As Range) As Range
    Dim rngWorking As Range
    Dim rngOrig As Range
    Dim rngPage As Range
    Dim rngCurSec As Range
    Dim iNumColumns As Integer
    Dim iColCur As Integer
    Dim iColPrev As Integer
    Dim rngWord As Range
    Dim rngCol As Range
    Dim colColumnRanges As Collection

    Application.ScreenUpdating = False
    'if we didn't pass in a search range, use the selection
    If rngSearch Is Nothing Then
    Set rngSearch = Selection.Range.Duplicate
    End If

    'initialize our collection of ranges
    Set colColumnRanges = New Collection

    'first, we need to define a couple of boundaries...
    'the current page
    Set rngPage = ActiveDocument.Bookmarks("\page").Range
    'and the current section
    Set rngCurSec = rngSearch.Sections(1).Range

    'and start with our current selection (so we can restore later?)
    Set rngOrig = rngSearch.Duplicate
    Set rngWorking = rngOrig.Duplicate
    rngWorking.Collapse wdCollapseStart

    'now get the smaller of either our page, our our section, first get our .Start
    If rngPage.Start < rngCurSec.Start Then
    rngWorking.Start = rngCurSec.Start
    Else
    rngWorking.Start = rngPage.Start
    End If
    'and then our .End
    If rngPage.End < rngCurSec.End Then
    rngWorking.End = rngPage.End
    Else
    rngWorking.End = rngCurSec.End
    'and remove the section break for our purposes
    rngWorking.MoveEnd wdCharacter, -1
    End If

    'now, our rngWorking is the area we're going to try to determine the various columns
    'how many columns do we think we're in?
    iNumColumns = rngWorking.PageSetup.TextColumns.Count

    'initialize our first column range
    Set rngCol = rngWorking.Words(1)
    'and our first column
    iColPrev = 1
    'now we're going to go through each word, and see if we switch columns
    For Each rngWord In rngWorking.Words
    'using the dialog requires the selection
    rngWord.Select
    iColCur = Dialogs(wdDialogFormatColumns).ColumnNo
    'if we're still in the same column, move our end range
    If iColCur = iColPrev Then
    rngCol.End = rngWord.End
    'otherwise, add our range to the collection, and start over
    Else
    colColumnRanges.Add rngCol.Duplicate
    Set rngCol = rngWord.Duplicate
    End If
    iColPrev = iColCur
    Next
    'and don't forget about our last column
    colColumnRanges.Add rngCol.Duplicate

    If iColNum <= colColumnRanges.Count Then
    Set fGetColumnRange = colColumnRanges(iColNum)
    Else
    MsgBox "Unable to select the column. Column " & iColNum & " does not exist", vbInformation, "fGetColumnRange"
    Set fGetColumnRange = rngOrig.Duplicate
    End If

    Application.ScreenRefresh

    End Function
    [/vba] Again... not really thrilled about this methodology (requires use of the selection, since the primary "special sauce" of this approach is getting the .ColumnNo property of a word dialog called wdDialogFormatColumns -- something I found in a couple of quick google searches on the topic).

    By the way... regarding your lngLineCount function. I think it's bad practice to use the same naming convention for you function name as you use for your variable typing. It's very confusing to have a function called "lngLineCount" and use "lng" as a prefix for your variables which are Longs.

    The function you posted is very incomplete, since it relies on a couple of other functions. I'm guessing by your commented out code that you aren't using the immediate window yet. You should. It makes development so much faster.

    More to come in a moment, re: line counting.

  9. #9
    VBAX Master
    Joined
    Feb 2011
    Posts
    1,480
    Location
    Actually, just even doing a little bit of testing... I realize there's a bit of a flaw already... since I establish a boundary of the current page, but then allow passing in a search range. So a multiple page multiple column kind of thing will only give you the column of whatever page you're on. So you may need to always limit by selection, or create an additional parameter to pass in two boundaries: a page and a section (since you want to search whichever range is smaller-- the page or the section).

  10. #10
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    <sits chuckling in the next room>

  11. #11
    VBAX Master
    Joined
    Feb 2011
    Posts
    1,480
    Location
    <glares at the next room>
    I'm going to stand by the code above... it *sort of* works, except that at the moment, it only gives you the column of the first page your selection is currently in (it relies on both ActiveDocument.Bookmarks("/Page").Range and whatever range you've passed in).

    But I'm not going to modify, for now, since I suspect you're going to need to rely on the selection any way.

    If, as you say, you've got something that mostly works right now... I'm happy to help you keep learning. But you should really start learning the other area of this experience: a good end-product which is going to use Word tables, or maybe linked text boxes (although my instinct is to shy away from them... there are "glitches" with textboxes in Word 2000-2003, and there are some serious errors with floating objects in 2007/2010).

  12. #12
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Linked text boxes, if used for only that purpose, AND you are not trying to get text OUT work of them *sort of* OK, but there are most definitely glitches in them. IMO, unless you have a real and well defined need to use them - avoid them. Another kludge that MS shoved into the application.

    I find them ugly to work with.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •