RichEdit On Scrolling Strike

We have an application that displays information in a scrolling RichEdit control, always displaying the latest, i.e. the bottom lines. For this purpose we add new lines at the bottom, place the SelStart at the end, to move the caret there, and ask the RichEdit to show the current position of the caret.
This works fine with the old version of the application, but fails to scroll to the end with the new version compiled with Delphi XE. Same code (mostly), same machine, different Delphi. Digging a little bit I found that it still works though when the RichEdit has the focus, which is unlikely to happen (not the default).

Between D2007 and D2009 the code snippet used to scroll the caret into view

      Perform(Messages.EM_SCROLLCARET, 0, 0);  // Scroll to caret

ceased to work for a RichEdit control when the RichEdit does not have the focus.

Because the same code works with D2007 and, on the same machine, does not work anymore with D2009 and above, one would easily put the blame on Embarcadero, not on Microsoft.
In fact, that would be blaming the messenger; Embarcadero basically did not change their code, but merely went from linking the RichEdit 1.0 compatibility module to the 2.0 one
Up to D2007: RichEditModuleName = 'RICHED32.DLL';
D2009 and up: RichEditModuleName = 'RICHED20.DLL';

The bug (I’m sure they could call it a feature) lies with Microsoft. And if you try with the newest Richedit 4.1, you get the same problem.

There is a workaround though, and that’s where Embarcadero could have made good on the promise of the VCL to isolate the developers from the quirks of the Windows API. Making sure that the RichEdit does not hide the selection allows EM_SCROLLCARET to perform as expected.
But according to Murphy’s Law, the default is HideSelection := True….

procedure ScrollToEnd(ARichEdit: TRichEdit);
var
  isSelectionHidden: Boolean;
begin
  with ARichEdit do
  begin
    SelStart := Perform(Messages.EM_LINEINDEX, Lines.Count, 0);//Set caret at end
    isSelectionHidden := HideSelection;
    try
      HideSelection := False;
      Perform(Messages.EM_SCROLLCARET, 0, 0);  // Scroll to caret
    finally
      HideSelection := isSelectionHidden;
    end;
  end;
end;
This entry was posted in Delphi, VCL, Windows and tagged , , , , , . Bookmark the permalink.

9 Responses to RichEdit On Scrolling Strike

  1. gabr says:

    You really should submit this to QC.

  2. A.Bouchez says:

    As far as I remember, RICHED32.DLL is just a wrapper around RICHED20.DLL, since Windows XP. The problem is indeed in the Microsoft side…

    Thanks for pointing out the HideSelection := False trick.

  3. Delfi Phan says:

    Long chain of precedence cases.

    Windows has many library functions which I used to use (Win3.0 days), thinking Microsoft knows what they are doing. Over the versions, they became unreliable and I increasingly rolled my own.

    DOS was the same. To use DOS interrupts, you really had to know the undocumented tricks.

    Even the PC Bios had RS232 serial port functionality which was avoided by everyone due to abysmal performance.

    To rely on something as complex as Window’s RichEdit is not advisable.

  4. Claud says:

    This RichEdit error strip in the threads functions.

  5. Joel says:

    Thanks for this – saved my Monday!

  6. David Heffernan says:

    Looks like over-complication to me. Just do this:

    Perform(WM_VSCROLL, SB_BOTTOM, 0);

    • François Gaillard says:

      Yes, it scrolls the Richedit, and is indeed a very good solution (thank you) when you want to scroll all the way down (which was the example here).
      HideSelection := False on the other hand allows EM_SCROLLCARET to work as expected and scroll wherever the caret might be.

  7. David Heffernan says:

    Thanks for explaining the purpose behind your code.

  8. Pingback: Rechedit scrollen, aber ohne Fokus - Delphi-PRAXiS

Leave a Reply to David Heffernan Cancel reply

Your email address will not be published. Required fields are marked *