Library tutorials & articles

RichTextBox Control

Multiple undo/redo

The RichTextBox actually supports multiple undo and redo. However, this functionality is hidden from VB programmers. In order to be able to use the undo and redo facilities, you need to add the following code.

Add this code to the Form_Load() event of the form that contains the RichTextBox control. We are calling the RichTextBox rtfText

Dim lStyle As Long
'// required to 'reveal' multiple undo
'// set rich text box style
lStyle = TM_RICHTEXT Or TM_MULTILEVELUNDO Or TM_MULTICODEPAGE
SendMessageLong rtfText.hwnd, EM_SETTEXTMODE, lStyle, 0

Then, add the code below. This code also adds cut/copy/paste/clear functionality, and expects the following menu items:

Menu Name Caption
mnuEdit &Edit
mnuEditUndo &Undo
mnuEditRedo &Redo
mnuEditCut Cu&t
mnuEditCopy &Copy
mnuEditPaste &Paste
mnuEditClear C&lear

Call the UpdateItems procedure in the mnuEdit_Click() event. This procedure updates the menu items. 

Public Property Get UndoType() As ERECUndoTypeConstants
    UndoType = SendMessageLong(rtfText.hWnd, EM_GETUNDONAME, 0, 0)
End Property
Public Property Get RedoType() As ERECUndoTypeConstants
    RedoType = SendMessageLong(rtfText.hWnd, EM_GETREDONAME, 0, 0)
End Property
Public Property Get CanPaste() As Boolean
   CanPaste = SendMessageLong(rtfText.hWnd, EM_CANPASTE, 0, 0)
End Property
Public Property Get CanCopy() As Boolean
   If rtfText.SelLength > 0 Then
      CanCopy = True
   End If
End Property
Public Property Get CanUndo() As Boolean
    CanUndo = SendMessageLong(rtfText.hWnd, EM_CANUNDO, 0, 0)
End Property
Public Property Get CanRedo() As Boolean
    CanRedo = SendMessageLong(rtfText.hWnd, EM_CANREDO, 0, 0)
End Property

'///////////////////////////////////////////////////////
'// Methods
Public Sub Undo()
    SendMessageLong rtfText.hWnd, EM_UNDO, 0, 0
End Sub
Public Sub Redo()
    SendMessageLong rtfText.hWnd, EM_REDO, 0, 0
End Sub
Public Sub Cut()
   SendMessageLong rtfText.hWnd, WM_CUT, 0, 0
End Sub
Public Sub Copy()
   SendMessageLong rtfText.hWnd, WM_COPY, 0, 0
End Sub
Public Sub Paste()
   SendMessageLong rtfText.hWnd, WM_PASTE, 0, 0
End Sub
Public Sub Clear()
   rtfText.SelText = Empty
End Sub
Public Sub UpdateItems()
    Dim bCanUndo As Boolean
    '// Undo/Redo options:
    bCanUndo = CanUndo
    mnuEditUndo.Enabled = bCanUndo
    '// Set Undo Text
    If (bCanUndo) Then
        mnuEditUndo.Caption = "&Undo " & TranslateUndoType(UndoType)
    Else
        mnuEditUndo.Caption = "&Undo"
    End If
    '// Set Redo Text
    bCanUndo = CanRedo
    If (bCanUndo) Then
        mnuEditRedo.Caption = "&Redo " & TranslateUndoType(RedoType)
    Else
        mnuEditRedo.Caption = "&Redo"
    End If
    mnuEditRedo.Enabled = bCanUndo
    tbToolBar.Buttons("Redo").Enabled = bCanUndo
    '// Cut/Copy/Paste/Clear options
    mnuEditCut.Enabled = CanCopy
    mnuEditCopy.Enabled = CanCopy
    mnuEditPaste.Enabled = CanPaste
    mnuEditClear.Enabled = CanCopy
End Sub
'// Returns the undo/redo type
Private Function TranslateUndoType(ByVal eType As ERECUndoTypeConstants) As String
   Select Case eType
   Case ercUID_UNKNOWN
      TranslateUndoType = "Last Action"
   Case ercUID_TYPING
      TranslateUndoType = "Typing"
   Case ercUID_PASTE
      TranslateUndoType = "Paste"
   Case ercUID_DRAGDROP
      TranslateUndoType = "Drag Drop"
   Case ercUID_DELETE
      TranslateUndoType = "Delete"
   Case ercUID_CUT
      TranslateUndoType = "Cut"
   End Select
End Function

Then, add this code to a module

'// View Types
Public Enum ERECViewModes
    ercDefault = 0
    ercWordWrap = 1
    ercWYSIWYG = 2
End Enum
'// Undo Types
Public Enum ERECUndoTypeConstants
    ercUID_UNKNOWN = 0
    ercUID_TYPING = 1
    ercUID_DELETE = 2
    ercUID_DRAGDROP = 3
    ercUID_CUT = 4
    ercUID_PASTE = 5
End Enum
'// Text Modes
Public Enum TextMode
    TM_PLAINTEXT = 1
    TM_RICHTEXT = 2 ' /* default behavior */
    TM_SINGLELEVELUNDO = 4
    TM_MULTILEVELUNDO = 8 ' /* default behavior */
    TM_SINGLECODEPAGE = 16
    TM_MULTICODEPAGE = 32 ' /* default behavior */
End Enum

Public Const WM_COPY = &H301
Public Const WM_CUT = &H300
Public Const WM_PASTE = &H302

Public Const WM_USER = &H400
Public Const EM_SETTEXTMODE = (WM_USER + 89)
Public Const EM_UNDO = &HC7
Public Const EM_REDO = (WM_USER + 84)
Public Const EM_CANPASTE = (WM_USER + 50)
Public Const EM_CANUNDO = &HC6&
Public Const EM_CANREDO = (WM_USER + 85)
Public Const EM_GETUNDONAME = (WM_USER + 86)
Public Const EM_GETREDONAME = (WM_USER + 87)

Public Declare Function SendMessageLong Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Any) As Long

And that's it! What you thought is impossible, is actually possible in a few lines of code. Of course, it would have been much easier if Microsoft had provided these functions for VB programmers anyway. This code has been adapted from VB Accelerator's RichEdit control.

Comments

  1. 24 Apr 2008 at 15:39

    This is long past the topic post date but...

     I used the selection method (where you select text and then update it), for formatting the output of an application's process in a RTF.  This worked great until I needed to buffer the contents of the RTF.  Once I started removing content from the RTF, the formatting I had applied through the selection method caused other text to be come improperly formatted.

    I am now proceeding with the inserting text as RTF method.
     

  2. 06 Jan 2007 at 19:18

    I am had the same proplems;

    RichTextBox1.SelectionFont.Bold
    RichTextBox1.SelectionFont.Underline etc are readonly

    Solved them the following way

    Me.RichTextBox1.SelectionColor = Color.Blue

    Me.RichTextBox1.SelectionFont = New System.Drawing.Font("Tahoma", 8.25!, System.Drawing.FontStyle.Bold)

    Me.RichTextBox1.SelectedText = BlueBoldText

    Me.RichTextBox1.SelectionColor = Color.Black

    Me.RichTextBox1.SelectionFont = New System.Drawing.Font("Tahoma", 8.25!, System.Drawing.FontStyle.Regular)

    Me.RichTextBox1.SelectedText = BlackRegularText

  3. 21 Sep 2006 at 17:48

    Kylua, your efforts were not wasted...

    You have probably saved me a week's worth of head-pounding. Why it is so difficult to programmatically modify RTF using this control is beyond me. All of the examples I have seen rely on the .Find method to select the text you want to modify.

    I am still going to learn the intricacies of RTF and what the difference is between a replace and simply assigning a string. Maybe there's some hidden header we can't see in the RTF property that an assignment overwrites but a replace does not.

    Anyway, thanks for sharing this! Big Smile [:D]

  4. 31 Jul 2006 at 20:13

    I spent AGES working thru this one and couldn't find it anywhere!

    The basic problem is that you have to update the richtextbox.rtf, not .text.

    And it is very fussy about goes in there!!

    A quick and dirty method is to set the richtextbox.text to a value such as "WHATEVER" and then replace that in the richtextbox.rtf with your rtf enabled code.  As for what .rtf recognises in formatting, I resorted to copying and pasting rtf into the richtextbox and then looking at the .rtf value.

    I only wanted a bit of bold!!  By the time I had sorted it out I had decided on a different display method anyway so I hope someone uses this so I haven't wasted my time!!

    The working code from my project, before I dump it:

    richtextbox.Text =

    "REPLACETHIS"

    Do While Not EOF(1)
    Input(1, aLine)
    info = Split(Replace(aLine,

    "\", "\\"), ",")
    TempInfo = TempInfo &
    "\b Result Displayed: \b0" & info(1) & " \par " & "\b URL: \b0 " & info(0) & " \par " & "\b Interval: \b0 " & info(2) & " \par " & "\b Log Path: \b0 " & info(3) & " \par " & " \par "

    Loop

    richtextbox.Rtf = Replace(richtextbox.Rtf, "REPLACETHIS", TempInfo)

    I've just done bolds and replaced the slashes as I was showing URLs and file paths.

  5. 06 Dec 2005 at 16:50

    James Crowley:


    I am using vb6sp6(EMSETTEXTMODE didn't work with vb6sp4 either) RTB control, I have already implemented URL detection similar to yours.  However now i am trying to get MULTI-LEVEL UNDO to work, which you stated can be revealed with EMSETTEXTMODE.  My attempts at setting a value(42 or any value for that matter) using EMSETTEXTMODE(WMUSER+89) have been unsuccessful, by using EMGETTEXTMODE(WMUSER+90) to read the value(EMGETTEXTMODE always returns a 38).  I have previously cleared the RTB with WMSETTEXT.  I tried SendMessageLong and SendMessage.  EM_SETTEXTMODE always returns a zero(non-error).  Can you shed some light on why this may not be working?  Can you pop an RTB control(VB6) on a form and test this again?


    Thank you

  6. 08 Nov 2005 at 09:51

    Hi


    These days I am trying to figure out why I get another new line in a RTB when I save the contents to a file. This line is placed at the position around 68000. There are about 72000 characters in the RTB.
    I tried with FileSave method and with Open command. In both cases result was the same.


    Oh, one thing to add. The change is not visible immediately. The new line is there when the file is reopened.
    If the file is opened with WordPad the new line is there.



    Marko

  7. 15 Jul 2005 at 08:30

    I think its to late to answer but, maybe someone uses it , huh?



    You can use UpTo method.



    Richtextbox1.UpTo strSTRING


    it search text and goes there if there is strSTRING in richtextbox1 text. if there is no text it goes to the end.


    You must enable Vertical Scroll Bar , and then samply use


    Richtextbox1.UpTo "§"



    Note: "§"=CHR(245), i use it because , i know there is no "§" in my richtextbox control, you can use something like that who wouldnt write it to the richtextbox.



    Sorry for my english


    Keep in Peace...



    EceL sTyLe

  8. 20 Dec 2004 at 14:52

    How can I format text that I'm adding to the box? IE, the program prints a string into the box, and I want to change the colour of some of the text in the string. Also how can I find the cursor's position and the character it's in front of?

  9. 25 Sep 2004 at 01:45

    i am very like this code. it prints all the text.  but how can extract some line from rtf.
    for e.g.  print 26 lines each from the rtf text.

  10. 23 Sep 2004 at 06:03

    hi


    how can i call another form from the current MDI child form


    ebi

  11. 23 Sep 2004 at 05:34

    hi
    we used pre printed paper for printing.  so i want spilt the contents of RTF text and print it on multiple page on particular start line and end line


    pls help me


    ebi

  12. 18 Jul 2004 at 22:03

    hi
    i also want the same thong for my application


    if u find any answer how to do this plz let me know


    thanks in anticapation


    mtikoo

  13. 01 Jul 2004 at 09:35

    I am writing code for a connection routine and am using a rich text box to display the responses from the server.  However as the text is added the user is constantly haveing to manually scroll the window to see the new text.  I would like the program to automaically scroll the window for the user.  Any help on this would be appreciated.  thanks

  14. 14 Jun 2004 at 14:52

    I'm using VB6 - sp5 - -
    This code absolutely does not work with my system..... I've been told that it's because the RichTextBox that comes with VB is an older version of the dll -----


    Can I get it to work by replacing the RichEd dll?
    If so, where can I get it?

  15. 24 Jan 2004 at 12:54


    how can  inert image into richtextbox ?

  16. 30 Jul 2003 at 15:30

    I believe the problem for VB'ers is that the RichText control is from richtx32.ocx, which is based on riched32.dll.  As per Microsoft ( http://msdn.microsoft.com/library/en-us/shellcc/platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp ) this library only supports one level of undo.  Later versions of riched (riched20.dll, etc.) allow for multiple levels, but I have found no Microsoft ocx that uses this library.


    Bottom line, not going to happen using ONLY Richtext control, even with the correct messages sent to it.


    As a side note, I found a few logic errors in above code...


    1)
      Public Property Get CanCopy() As Boolean
          If rtb1.SelLength < 0 Then
             CanCopy = True
          End If
      End Property


    Should read as


      Public Property Get CanCopy() As Boolean
         If rtb1.SelLength > 0 Then
            CanCopy = True
         End If
      End Property


    2)  
    With richtx32.ocx, the above code will display the undo all the time.  This is confusing during runtime.  Really we only want it to show if there is no redo.  This requires one of any number of possible revisions.  Generate your favorite.



  17. 30 Jul 2003 at 15:28

    I believe the problem for VB'ers is that the RichText control is from richtx32.ocx, which is based on riched32.dll.  As per Microsoft ( http://msdn.microsoft.com/library/en-us/shellcc/platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp ) this library only supports one level of undo.  Later versions of riched (riched20.dll, etc.) allow for multiple levels, but I have found no Microsoft ocx that uses this library.


    Bottom line, not going to happen using ONLY Richtext control, even with the correct messages sent to it.


    As a side note, I found a few logic errors in above code...


    1)
      Public Property Get CanCopy() As Boolean
          If rtb1.SelLength < 0 Then
             CanCopy = True
          End If
      End Property


    Should read as


      Public Property Get CanCopy() As Boolean
         If rtb1.SelLength > 0 Then
            CanCopy = True
         End If
      End Property


    2)  
    With richtx32.ocx, the above code will display the undo all the time.  This is confusing during runtime.  Really we only want it to show if there is no redo.  This requires one of any number of possible revisions.  Generate your favorite.



  18. 21 Mar 2003 at 07:51

    Is it possible to highlight a word in a "rich text box" and add a link to that word?


    For example in Microsoft Word you can highlight a word, click "insert" on the tool bar, select Hyperlink and add an url address to that word. This word can then be clicked on to go to that web page.


    Is this possible in RTB?


    Thanks
    Chris
    cgroves@lycos.co.uk

  19. 28 Nov 2002 at 13:26


    How do you autodetect URL's in VB? Not .net!

  20. 03 Nov 2002 at 15:12

    RichTextBOx1.SelectionFont.Bold
    RichTextBOx1.SelectionFont.Underline etc are readonly


    How to set the formating for the selected text???

  21. 08 Oct 2002 at 10:40

    Hi,


    Can u please tell me, how to do this in VB.NET or C#. I need it urgently.


    Thanx
    Ashok

  22. 12 May 2002 at 15:22

    VB.net has introduced a number of changes to the way you access the RichTextBox's properties. SelUnderline, SelBold, SelStrikethrough etc are now available in the


    richTextBox1.SelectionFont


    property. For example, in VB.net, use


    richTextBox1.SelectionFont.Underline


    instead of


    richTextBox1.SelUnderline


    I will try to update the tutorial to be .net compliant in the next month or so.

  23. 07 Mar 2002 at 08:53

    but still  can only undo level 1,why?can u tell me?thank you very much.

  24. 12 Feb 2002 at 09:35

    What o/s are you running? on Windows 2000/XP, the richtext box has been "upgraded" to include multiple undo/redo.. otherwise, you'll probably be left with the old single undo/redo..


  25. 03 Feb 2002 at 15:28

    i expirienced the same thing... does anyone have a working example on this?

  26. 11 Jan 2002 at 04:57

    After using this code the undo level is still 1.


    Can someone help me out ?


    Thank you !

  27. 01 Jan 1999 at 00:00

    This thread is for discussions of RichTextBox Control.

Leave a comment

Sign in or Join us (it's free).