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;
You really should submit this to QC.
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.
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.
This RichEdit error strip in the threads functions.
Thanks for this – saved my Monday!
Looks like over-complication to me. Just do this:
Perform(WM_VSCROLL, SB_BOTTOM, 0);
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.
Thanks for explaining the purpose behind your code.
Pingback: Rechedit scrollen, aber ohne Fokus - Delphi-PRAXiS