MrTomRod
MrTomRod

Reputation: 344

Undo wdFieldHyperlink.Unlink

I want to temporarily remove hyperlinks. This is my function to remove them:

Sub HideHyperlinks()

Dim oField As Field

For Each oField In ActiveDocument.Fields
    If oField.Type = wdFieldHyperlink Then
        Debug.Print oField.Code.Text
        oField.Unlink
    End If
Next

Set oField = Nothing

End Sub

Then, I would like to change the document somehow and at a later stage add them again. Of course, that doesn't work because the field was turned into regular text and all information was lost.

How can I best do this? Should I turn wdFieldHyperlink into another wdField or some sort of Object, with the hyperlink URL as a hidden property? If so, how? This is my idea in pseudocode:

'Turn hyperlinks into newWdFieldType'
Sub HideHyperlinks()
For Each oField In ActiveDocument.Fields
    If oField.Type = wdFieldHyperlink Then
        'create newWdFieldType with hidden property oField.Code.Text'
        'delete oField from document'
    End If
Next
End Sub

'Turn newWdFieldType back into hyperlinks'
Sub ShowHyperlinks()
For Each oField In ActiveDocument.Fields
    If oField.Type = newWdFieldType Then
        're-create wdFieldHyperlink with hyperlink from newWdFieldType'
        'delete oField from document'
    End If
Next
End Sub

VBA is annoying to debug and the documentation is bad, or am I looking in the wrong place?

Upvotes: 0

Views: 146

Answers (2)

jonsson
jonsson

Reputation: 1301

Some not well-tested possibilities:

If the hyperlinks can be hidden rather than deleted then you could do something like this. At the moment it assumes that all hidden hyperlinks should be unhidden, so if you hide material for other reasons you would need to make some kind of distinction between the ones you want to unhide and the ones you don't

Option Explicit
Const bookmarkPrefix As String = "wasahl"

Sub hideHyperlinks()
Dim h As Word.Hyperlink
Dim i As Long
Dim r As Word.Range

' This should deal with hyperlinks in the document body.
' If you have them elsewhere you will need to process other ranges/objects

i = 0
For Each h In ActiveDocument.Hyperlinks
  ' ignore hyperlinks that are already hidden
  If Not h.Range.Font.Hidden Then
    Set r = h.Range
    ' copy and hide the original
    r.Copy
    r.Font.Hidden = True

    ' Insert a copy and unlink it
    r.Collapse WdCollapseDirection.wdCollapseEnd
    r.Paste
    r.Fields.Unlink
    
    ' This may not be needed but it makes finding and removing the
    ' unlinked material easier when we unhide the hyperlinks
    ' need a unique bookmark name, but this relies on the time, so don't
    ' run it twice in the same second!
    i = i + 1
    r.Bookmarks.Add bookmarkPrefix & format(Time(), "HHmmss") & CStr(i)
    Set r = Nothing
  End If
Next
 
End Sub

Sub unhideHyperlinks()
Dim b As Word.Bookmark
Dim h As Word.Hyperlink

' remove all our unlinked hyperlinks
' NB, as a user, you will need to be careful not to add material into
' the bookmarks as it will all be deleted!

For Each b In ActiveDocument.Bookmarks
  If Left(b.Name, Len(bookmarkPrefix)) = bookmarkPrefix Then
    b.Range.Text = ""
  End If
Next

' Find the SET fields, extract the formatted text (code+result)
' copy it into the document and delete the SET field
For Each h In ActiveDocument.Hyperlinks
  h.Range.Font.Hidden = False
Next
End Sub

I originally had a version of this that "hid" the original { HYPERLINK } field by nesting it inside a { SET } field, but if you can't retain hyperlinks in the document then that wouldn't be much use.

To save the link information in the general case is not trivial because in theory a { HYPERLINK } field could have nested field codes and non-text items in either its code or its result. In practice I think the only time when it makes sense to nest fields is probably when the document is a mail merge main document, and I don't think I've actually seen anyone insert any non-text item in either the field's code or result. So if all you ever have is text and no nesting, perhas something like this. NB, you would need to run the clearHLVariables() sub to get rid of the DOcument Variables. I didn't want to put that in the restore subroutine in case it fails.

Option Explicit
Const bookmarkPrefix As String = "wasahl"
Const codePrefix As String = "hlcode"
Const resultPrefix As String = "hlresult"

Sub saveHyperlinks()
Dim hc As Long
Dim i As Long
Dim r As Word.Range
Dim t As String
i = 0
' This should deal with hyperlinks in the document body.
' If you have them elsewhere you will need to process other ranges/objects

' We could either save the various possible components in the hyperlink, or we
' could just save the field code and perhaps the result. The latter seems a bit
' simpler so let's do that

With ActiveDocument
  For hc = .Hyperlinks.count To 1 Step -1
    i = i + 1
    t = format(Time(), "HHmmss")
  
    Set r = .Hyperlinks(hc).Range
    .Variables(codePrefix & t & CStr(i)).Value = r.Fields(1).Code
    ' Probably do not have to store the result as it should be in the document when we
    ' restore the links, but I'm going to anyway
    .Variables(resultPrefix & t & CStr(i)).Value = r.Fields(1).Result
  
    ' unlink the field and bookmark it with a name that will let us retrieve the
    ' associated variables later
  
    r.Fields.Unlink
    r.Bookmarks.Add bookmarkPrefix & t & CStr(i)
    Set r = Nothing
  Next
End With
 
End Sub

Sub restoreHyperlinks()
Dim b As Word.Bookmark
Dim r As Word.Range    
Dim s As String


With ActiveDocument
For Each b In .Bookmarks
  If Left(b.Name, Len(bookmarkPrefix)) = bookmarkPrefix Then
    s = Mid(b.Name, Len(bookmarkPrefix) + 1)
    b.Range.Fields.Add b.Range, WdFieldType.wdFieldEmpty, Trim(.Variables(codePrefix & s).Value)
  End If
Next
End With
End Sub

Sub clearHLVariables()

' Probably safest to run this separately when you know you don't need
' the variables any more.

Dim v As Word.Variable
For Each v In ActiveDocument.Variables
  If (Left(v.Name, Len(codePrefix)) = codePrefix) Or (Left(v.Name, Len(resultPrefix)) = resultPrefix) Then
    v.Delete
  End If
Next

End Sub

Upvotes: 1

macropod
macropod

Reputation: 13505

You can employ a custom undo record. For example:

Sub HideHyperlinks()
Application.ScreenUpdating = False
Dim objUndo As UndoRecord
Set objUndo = Application.UndoRecord
objUndo.StartCustomRecord ("Hyperlinks")
With ActiveDocument
  MsgBox "# Hyperlinks: " & .Hyperlinks.Count
  Do While .Hyperlinks.Count > 0
    .Hyperlinks(1).Range.Fields(1).Unlink
  Loop
  MsgBox "# Hyperlinks: " & .Hyperlinks.Count
  objUndo.EndCustomRecord
  .Undo
  MsgBox "# Hyperlinks: " & .Hyperlinks.Count
End With
End Sub

Upvotes: 0

Related Questions