Delphi 11.3中RichEdit控件失焦后仍自动滚动的问题求助
问题描述
在高度无法容纳全部内容的TRichEdit控件末尾添加行时,需要阻止控件自动滚动到新添加的行。测试代码在Delphi 2005中可以通过设置HideSelection := true并在OnChange事件中转移焦点来实现,但在Delphi 11.3中失效,控件仍会自动滚动显示新行。
测试场景:窗体包含一个高度仅能显示10行文本的RichEdit控件和一个按钮,点击按钮会添加100行文本,测试代码如下:
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls; type TForm1 = class(TForm) RichEdit1: TRichEdit; AddLine: TButton; procedure AddLineClick(Sender: TObject); procedure RichEdit1Change(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.AddLineClick(Sender: TObject); var i : integer; begin form1.RichEdit1.HideSelection := true; form1.RichEdit1.Clear; for i := 1 to 100 do Form1.RichEdit1.Lines.Add('Add New Line # ' + IntToStr(i)); end; procedure TForm1.RichEdit1Change(Sender: TObject); begin form1.AddLine.SetFocus; end; end.
解决方案
方法1:记录并恢复滚动位置
在添加文本前获取当前可见的首行索引,添加完成后将滚动位置恢复到该索引,完全阻止自动滚动:
procedure TForm1.AddLineClick(Sender: TObject); var i: integer; SaveTop: Integer; begin // 获取当前可见区域的第一行索引 SaveTop := RichEdit1.Perform(EM_GETFIRSTVISIBLELINE, 0, 0); RichEdit1.Clear; for i := 1 to 100 do RichEdit1.Lines.Add('Add New Line # ' + IntToStr(i)); // 恢复之前的滚动位置 RichEdit1.Perform(EM_LINESCROLL, 0, SaveTop); end;
方法2:临时禁用重绘提升性能并锁定滚动
批量添加大量文本时,临时关闭控件重绘可以减少闪烁,同时配合滚动位置恢复:
procedure TForm1.AddLineClick(Sender: TObject); var i: integer; SaveTop: Integer; begin // 关闭控件重绘 RichEdit1.Perform(WM_SETREDRAW, 0, 0); try SaveTop := RichEdit1.Perform(EM_GETFIRSTVISIBLELINE, 0, 0); RichEdit1.Clear; for i := 1 to 100 do RichEdit1.Lines.Add('Add New Line # ' + IntToStr(i)); // 恢复滚动位置 RichEdit1.Perform(EM_LINESCROLL, 0, SaveTop); finally // 重新开启重绘并刷新控件 RichEdit1.Perform(WM_SETREDRAW, 1, 0); RichEdit1.Invalidate; end; end;
方法3:优化OnChange事件触发逻辑
原代码中OnChange每次文本添加都触发焦点转移,在高版本Delphi中可能导致滚动逻辑混乱。改为仅在文本添加完成后转移一次焦点:
procedure TForm1.AddLineClick(Sender: TObject); var i: integer; SaveTop: Integer; begin SaveTop := RichEdit1.Perform(EM_GETFIRSTVISIBLELINE, 0, 0); // 临时禁用OnChange事件避免频繁触发 RichEdit1.OnChange := nil; try RichEdit1.Clear; for i := 1 to 100 do RichEdit1.Lines.Add('Add New Line # ' + IntToStr(i)); // 恢复滚动位置 RichEdit1.Perform(EM_LINESCROLL, 0, SaveTop); finally // 恢复OnChange事件 RichEdit1.OnChange := RichEdit1Change; // 完成后统一转移焦点 AddLine.SetFocus; end; end;
内容的提问来源于stack exchange,提问作者Leonid Danyushevsky
相关产品推荐
相关产品推荐

