Search code examples
delphidelphi-6tmemo

Lock the begining of the text in a TMemo


I would like to have a TMemo which will always begin with the string 'SELECT c_name FROM ' and I want to lock it so users cannot remove or replace this string in the TMemo, they will have to write their text AFTER this string. Can someone help me with that ? I tried something with the onChange event but the problem is that users can click at the begining of the TMemo and edit it by the begining.

I'm using Delphi 6.


Solution

  • What you are asking for is not possible with TMemo. It is just a thin wrapper around a standard Win32 EDIT control, which does not support this kind of functionality.

    You need to use TRichEdit instead. It supports protecting text like you have described. After adding the desired text, select it using the TRichEdit.SelStart and TRichEdit.SelLength properties and then set the TRichEdit.SelAttributes.Protected property to true. If the user tries to modify the protected text in any way, TRichEdit will reject the modification by default (you can override that decision by using the TRichEdit.OnProtectChange event to set the AllowChange parameter to true). For example:

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

    Update: by default, protecting the text also prevents the user from copying it. If you want the user to be able to copy the protected text, you have to explicitly enable that. Although TRichEdit has a OnProtectChange event to allow access to protected text, it does not tell you WHY the text is requesting access. However, the underlying EN_PROTECTED notification does. So, you can subclass the TRichEdit to intercept EN_PROTECTED directly, and then return 0 (allow access) only when the user is copying the protected text.

    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;
    

    Alternatively:

    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;