Yet Another Code Site

Changes To TRichEdit In BCB5

What follows is an analysis of what changed between BCB v4 and v5 based strictly upon a comparison of the comctrls.hpp and comctrls.pas source code.  It does *not* include possible changes in ancestor classes which may have introduced further changes.

Very little changed for the TRichEdit component between Borland C++ Builder versions 4 and 5.  It still implements Rich Edit 1.0, it adds one new event (OnContextPopup), and it adds one bug fix.  Here is a summary of changes followed by the specific changes to the source code for the TRichEdit class.

Summary Of Changes

The detailed changes are below.  To summarize the changes for those uninterested in minutiae:

  1. In BCB4, each instance of TRichEdit loaded and freed riched32.dll.  In BCB5, the DLL is loaded by the first instance of TRichEdit only.  The WMNCDestroy() method no longer frees the DLL.  Net result?  Once loaded, the Rich Edit DLL will stay in memory until the program terminates.

  2. A new event, OnContextPopup, was added to simplify responding to right-click messages.  A new message handler, WMRButtonUp, was added to support OnContextPopup events.

  3. When loading text into the TRichEdit component using the TRichEdit::Lines::LoadFromFile() method, the method now ends by trying to set the maximum length of text in the control to 0x7FFFFFF0.  This value is hard-coded and used regardless of the value of MaxLength.  (This may not solve the problem for all versions of the Rich Edit control.  See the related FAQ.)

  4. The TRichEdit CreateWnd() method now uses a local variable to establish whether the component has been modified.  The new local variable (WasModified) is used to save and re-establish the Modified property state.  Modified should now return the correct value after changing properties that cause the control to be destroyed and recreated, e.g., changing the ScrollBars property.  See the paper on Window handles vs. TRichEdit and other VCL controls for an explanation of this oblique comment.

Details Of Changes

Here are the declarations, methods, etc., that changed between BCB4 and BCB5.  "<!" means removed in BCB5.  "!>" means added to BCB5.  "..." (ellipsis) indicates that code was omitted for brevity.

BCB header (comctrls.hpp)

    class PASCALIMPLEMENTATION TCustomRichEdit : public Stdctrls::TCustomMemo 
    {
        typedef Stdctrls::TCustomMemo inherited;
        
    private:
 <!     unsigned FLibHandle;
        bool FHideScrollBars;
        TTextAttributes* FSelAttributes;
        ...
        HIDESBASE MESSAGE void __fastcall WMPaint(Messages::TWMPaint &Message);
        HIDESBASE MESSAGE void __fastcall WMSetFont(Messages::TWMSetFont &Message);
 !>     HIDESBASE MESSAGE void __fastcall WMRButtonUp(Messages::TWMMouse &Message);
        
    protected:
        virtual void __fastcall CreateParams(Controls::TCreateParams &Params);
        ...
    };

TRichEdit implementation (comctrls.pas)

    class PASCALIMPLEMENTATION TRichEdit : public TCustomRichEdit 
    {
        typedef TCustomRichEdit inherited;
        
    __published:
        __property Align ;
        __property Alignment ;
        ...
        __property WordWrap ;
        __property OnChange ;
 !>     __property OnContextPopup ;
        __property OnDragDrop ;
        ...     
    };

The following is a global variable added in BCB5.

!>   FRichEditModule: THandle;

    procedure TRichEditStrings.LoadFromFile(const FileName: string);
    var
      Ext: string;
      Convert: PConversionFormat;
    begin
      Ext := AnsiLowerCaseFileName(ExtractFileExt(Filename));
      System.Delete(Ext, 1, 1);
      Convert := ConversionFormatList;
      while Convert <> nil do
        with Convert^ do
          if Extension <> Ext then Convert := Next
          else Break;
      if Convert = nil then
        Convert := @TextConversionFormat;
      if FConverter = nil then FConverter := Convert^.ConversionClass.Create;
      try
        inherited LoadFromFile(FileName);
      except
        FConverter.Free;
        FConverter := nil;
        raise;
      end;
 !>   RichEdit.DoSetMaxLength($7FFFFFF0);
    end;

    procedure TCustomRichEdit.CreateParams(var Params: TCreateParams);
    const
      RichEditModuleName = 'RICHED32.DLL';
      HideScrollBars: array[Boolean] of DWORD = (ES_DISABLENOSCROLL, 0);
      HideSelections: array[Boolean] of DWORD = (ES_NOHIDESEL, 0);
 <! var
 <!   OldError: Longint;
 <! begin
 <!   OldError := SetErrorMode(SEM_NOOPENFILEERRORBOX);
 <!   FLibHandle := LoadLibrary(RichEditModuleName);
 <!   if (FLibHandle > 0) and (FLibHandle < HINSTANCE_ERROR) then FLibHandle := 0;
 <!   SetErrorMode(OldError);
 !> begin
 !>   if FRichEditModule = 0 then
 !>   begin
 !>     FRichEditModule := LoadLibrary(RichEditModuleName);
 !>     if FRichEditModule <= HINSTANCE_ERROR then FRichEditModule := 0;
 !>   end;
      inherited CreateParams(Params);
      CreateSubClass(Params, 'RICHEDIT');
      with Params do
      begin
        Style := Style or HideScrollBars[FHideScrollBars] or
          HideSelections[HideSelection];
        WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW);
      end;
    end;

    procedure TCustomRichEdit.CreateWnd;
    var
 <!   Plain, DesignMode: Boolean;
 !>   Plain, DesignMode, WasModified: Boolean;
    begin
 !>   WasModified := inherited Modified;
      inherited CreateWnd;
      if (SysLocale.FarEast) and not (SysLocale.PriLangID = LANG_JAPANESE) then
        Font.Charset := GetDefFontCharSet;
      SendMessage(Handle, EM_SETEVENTMASK, 0,
        ENM_CHANGE or ENM_SELCHANGE or ENM_REQUESTRESIZE or
        ENM_PROTECTED);
      SendMessage(Handle, EM_SETBKGNDCOLOR, 0, ColorToRGB(Color));
      if FMemStream <> nil then
      begin
        Plain := PlainText;
        FMemStream.ReadBuffer(DesignMode, sizeof(DesignMode));
        PlainText := DesignMode;
        try
          Lines.LoadFromStream(FMemStream);
          FMemStream.Free;
          FMemStream := nil;
        finally
          PlainText := Plain;
        end;
      end;
 <!   Modified := FModified;
 !>   Modified := WasModified;
    end;

    procedure TCustomRichEdit.WMNCDestroy(var Message: TWMNCDestroy);
    begin
      inherited;
 <!   if FLibHandle <> 0 then FreeLibrary(FLibHandle);
    end;

 !> procedure TCustomRichEdit.WMRButtonUp(var Message: TWMRButtonUp);
 !> begin
 !>   // RichEd20 does not pass the WM_RBUTTONUP message to defwndproc,
 !>   // so we get no WM_CONTEXTMENU message.  Simulate message here.
 !>   if Win32MajorVersion < 5 then
 !>     Perform(WM_CONTEXTMENU, Handle, LParam(PointToSmallPoint(
 !>       ClientToScreen(SmallPointToPoint(Message.Pos)))));
 !>   inherited;
 !> end;



Home | Top Of Page | Code | Papers | FAQs | Links | Search | Feedback

Page updated February 23, 2000

Copyright © 2000 Thin Air Enterprises and Robert Dunn.  All rights reserved.