You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 16:20:21