锁定 TMemo 中文本的开头

Lock the begining of the text in a TMemo

我想要一个始终以字符串 'SELECT c_name FROM ' 开头的 TMemo,我想锁定它,这样用户就无法删除或替换 TMemo 中的这个字符串,他们必须在之后写下他们的文本这个字符串。有人可以帮我吗?我尝试了一些 onChange 事件,但问题是用户可以在 TMemo 的开头单击并在开头进行编辑。

我正在使用 Delphi 6.

TMemo 无法满足您的要求。它只是标准 Win32 EDIT 控件的薄包装,不支持此类功能。

您需要改用 TRichEdit。它支持像您描述的那样保护文本。添加所需文本后,select 使用 TRichEdit.SelStartTRichEdit.SelLength 属性,然后将 TRichEdit.SelAttributes.Protected 属性 设置为 true。如果用户试图以任何方式修改受保护的文本,TRichEdit 将默认拒绝修改(您可以通过使用 TRichEdit.OnProtectChange 事件将 AllowChange 参数设置为 true 来覆盖该决定).例如:

RichEdit1.Text := 'SELECT c_name FROM ';
RichEdit1.SelStart := 0;
RichEdit1.SelLength := 19;
RichEdit1.SelAttributes.Protected := True;

更新: 默认情况下,保护文本还可以防止用户复制它。如果您希望用户能够复制受保护的文本,则必须明确启用它。尽管 TRichEdit 有一个允许访问受保护文本的 OnProtectChange 事件,但它不会告诉您 为什么 该文本请求访问。但是,基础 EN_PROTECTED 通知确实如此。因此,您可以子类化 TRichEdit 以直接拦截 EN_PROTECTED,然后 return 0(允许访问)仅当用户正在复制受保护的文本时。

interface

uses
  ...;

type
  TMyForm = class(TForm)
    RichEdit1: TRichEdit;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    DefRichEditWndProc: TWndMethod;
    procedure RichEditWndProc(var Message: TMessage);
  public
    { Public declarations }
  end;

...

implementation

uses
  Richedit;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  DefRichEditWndProc := RichEdit1.WindowProc;
  RichEdit1.WindowProc := RichEditWndProc;

  RichEdit1.Text := 'SELECT c_name FROM ';
  RichEdit1.SelStart := 0;
  RichEdit1.SelLength := 19;
  RichEdit1.SelAttributes.Protected := True;
end;

procedure TMyForm.RichEditWndProc(var Message: TMessage);
begin
  DefRichEditWndProc(Message);
  if Message.Msg = CN_NOTIFY then
  begin
    if TWMNotify(Message).NMHdr.code = EN_PROTECTED then
    begin
      if PENProtected(Message.lParam).Msg = WM_COPY then
        Message.Result := 0;
    end;
  end;
end;

或者:

interface

uses
  ...;

type
  TRichEdit = class(ComCtrls.TRichEdit)
  private
    procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
  end;

  TMyForm = class(TForm)
    RichEdit1: TRichEdit;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

...

implementation

uses
  Richedit;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  RichEdit1.Text := 'SELECT c_name FROM ';
  RichEdit1.SelStart := 0;
  RichEdit1.SelLength := 19;
  RichEdit1.SelAttributes.Protected := True;
end;

procedure TRichEdit.CNNotify(var Message: TWMNotify);
begin
  inherited;
  if Message.NMHdr.code = EN_PROTECTED then
  begin
    if PENProtected(Message.NMHdr).Msg = WM_COPY then
      Message.Result := 0;
  end;
end;

这是可能的,但我不知道您是否喜欢这个解决方案。

如前所述,TMemo 尚未实施那种行为。所以你必须对这种行为进行编程。

使用应用OnIdle事件和内容的纪念品。在每个空闲消息上验证备忘录内容。如果备忘录内容不是以'SELECT c_name FROM '开头则赋memento值,否则存储备忘录内容到memento.

这是验证和游标保护的示例

type
  TMainForm = class( TForm )
    Memo1: TMemo;
    ApplicationEvents1: TApplicationEvents; // OnIdle = ApplicationEvents1Idle
    procedure ApplicationEvents1Idle( Sender: TObject; var Done: Boolean );
  private
    FMemo1StartWith: string;
    FMemo1Memento  : string;
    procedure ValidateMemo( AMemo: TMemo; const AStartWith: string; var AMemento: string );
  public
    procedure AfterConstruction; override;

  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.AfterConstruction;
begin
  inherited;
  FMemo1StartWith := 'SELECT c_name FROM ';
end;

procedure TMainForm.ApplicationEvents1Idle( Sender: TObject; var Done: Boolean );
begin
  ValidateMemo( Memo1, FMemo1StartWith, FMemo1Memento );
end;

procedure TMainForm.ValidateMemo( AMemo: TMemo; const AStartWith: string; var AMemento: string );
var
  lCurrentContent: string;
begin
  // protect content

  lCurrentContent := AMemo.Text;
  if Pos( AStartWith, lCurrentContent ) = 1
  then
    AMemento := lCurrentContent
  else if Pos( AStartWith, AMemento ) = 1
  then
    AMemo.Text := AMemento
  else
    AMemo.Text := AStartWith;

  // protect cursor position

  if ( AMemo.SelLength = 0 ) and ( AMemo.SelStart < Length( AStartWith ) )
  then
    AMemo.SelStart := Length( AStartWith );
end;