Swapping Rich Text Fields
Recently a developer of ours had a need to swap two rich text fields on a form. The rich text fields were going to be text only (no attachments or images) but the customer wanted to be able to use things like bold and colors in the two fields that were to be swapped, so they had to be rich text. If the two fields were regular text fields, then swapping their values is pretty straightforward - you swap the two fields in the back end, perform a reload and a refresh in the front end, and you're done. But working with rich text fields is a lot more difficult. Even with the new parameters added to the 5 designer, swapping two rich text fields is not straightforward. So here's some code and the logic behind how we got there.
First, we looked into what was new in 5 that we could use. On the NotesUIDocument object, the Refresh method has a new optional parameter which, if True, includes all rich text items in the back end document. So, great, we thought. Swap the values in the back end then refresh with that parameter. Well, it didn't do anything. Looking at the help again, we looked at the Reload method in the NotesUIDocument class. In the Reload method, the help says "Modifications made to rich text items on the back end document do not appear on the current document until it is closed and reopened." So, can someone explain the purpose of the new parameter on the Refresh method then?
So, we went back to the drawing board. We knew we had to swap the fields in the back end. It might have been possible to programmatically highlight everything in field 1 copy it to the clipboard, move to a temporary field in the UI, paste it, etc. to do our swap. But that would have been ugly to watch from an end user point of view, so we didn't want to try that route.
So, we are back to swapping the fields in the back end and then saving the changes and closing and reopening the document so the user can see the swapped values. But if you swap the fields in the back end, when you close the document the user is prompted "do you want to save your changes?" which is not good from a user interface standpoint. If we force a save in the front-end, then that wipes out our changes in the back end because the back end changes aren't brought forward (even with a reload and a refresh). So we ended up using a pretty neat trick. We make the swap on the back end and then save the back end document. Then we add a field called SaveOptions to the back end document (after it's been saved). The changes are loaded up into the front end (so now we have a field called SaveOptions in the front end with a value of "0". So the front end document can be closed without the "do you want to save your changes?" prompt. So the technique of saving the document UNID, then finding the document again and reopening it can work.
Here's the code we ended up with:
Sub Click(Source As Button)
Dim session As New NotesSession
Dim ws As New NotesUIWorkspace
Dim uiDoc As NotesUIDocument
Dim doc As NotesDocument
Dim db As NotesDatabase
Dim unid As String
Dim field1 As NotesRichTextItem
Dim field2 As NotesRichTextItem
Dim tempFieldName As String
Dim tempField As NotesRichTextItem
Set uiDoc = ws.CurrentDocument
On Error Resume Next
Call uiDoc.Save ' Make sure we have a UNID and make sure validation passes
On Error Goto 0
If Err <> 0 Then ' Validation error
Err = 0
Exit Sub
End If
Set doc = uiDoc.Document
Set db = session.CurrentDatabase
unid = doc.UniversalID ' Save the UNID for when we reopen the doc
Set field1 = doc.GetFirstItem("Body1")
Set field2 = doc.GetFirstItem("Body2")
tempFieldName = "Q" ' Build a unique field name that doesn't exist on the document
While doc.HasItem(tempFieldName)
tempFieldName = tempFieldName & "Q"
Wend
Set tempField = doc.CreateRichTextItem(tempFieldName)
Call tempField.AppendRTItem(field1) ' Copy field 1 to temp field
While doc.HasItem("Body1")
Call doc.RemoveItem("Body1") ' Wipe out the first field so we can rebuild it
Wend
Set field1 = doc.CreateRichTextItem("Body1") ' Rebuild the first field
Call field1.AppendRTItem(field2) ' Copy field 2 to field 1
While doc.HasItem("Body2")
Call doc.RemoveItem("Body2") ' Wipe out the second field so we can rebuild it
Wend
Set field2 = doc.CreateRichTextItem("Body2") ' Rebuild the second field
Call field2.AppendRTItem(tempField) ' Copy temp field to field 2
While doc.HasItem(tempFieldName)
Call doc.RemoveItem(tempFieldName) ' Temp field no longer needed
Wend
Call doc.Save(True, False, True) ' Save the changes to the rich text fields
Call doc.ReplaceItemValue("SaveOptions", "0") ' Prevent the UI "do you wan to save" prompt
Call uiDoc.Reload ' Bring that save options field into the front end
Call uiDoc.Close(True) ' Close the document (in 6, happens immediately)
Set doc = Nothing ' Wipe out the variable
Set doc = db.GetDocumentByUNID(unid) ' Re-find the document
Call ws.EditDocument(True, doc) ' Open up this same doc in edit mode
End Sub
Note that our field names are "Body1" and "Body2". You would have to change those to your field names. But the temporary field (used for swapping) is a unique field name that doesn't exist on the document. Technically, it is a field called "Q" if that doesn't already exist, or "QQ", or "QQQ", etc.
This code works in both Notes 5 and Notes 6. The additional prompt to the NotesUIDocument Save method is ignored in Notes 5 (doesn't cause any errors) and the code still behaves as expected.
We have tested this code with multiple internal rich text fields (created when there's a lot of content). The code works just fine - all the contents from all the internal fields are copied over. However, we have only tried this technique with text (colors, bold, etc.) and haven't tried it when file attachments or images or other embedded objects are being used. So I don't know at this time how those special cases will work.