Fabrizio
Fabrizio

Reputation: 8043

Why dataset's state changes to dsEdit after suppressing WM_PASTE?

I'm intercepting and suppressing the WM_PASTE message for a TDBEdit by assigning its WindowProc property, as described in this answer.

After pressing Ctrl+V, despite the WM_PASTE is intercepted, the dataset's state changes from dsBrowse to dsEdit.

Why is this happening and how could I avoid that?

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, DBCtrls, StdCtrls, Mask, DB, DBClient;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    FPrevWindowProc : TWndMethod;
    procedure   MyWindowProc(var AMessage: TMessage);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  Dst : TClientDataSet;
  Dsc : TDataSource;
  Fld : TField;
  Nav : TDBNavigator;
  Edt : TDBEdit;
begin
  //dataset
  Dst := TClientDataSet.Create(Self);
  Dst.FieldDefs.Add('TEST', ftString, 20);
  Dst.CreateDataSet();
  Dst.Active := True;
  Fld := Dst.Fields[0];
  Dst.Append();
  Fld.AsString := 'test';
  Dst.Post();

  //datasource
  Dsc := TDataSource.Create(Self);
  Dsc.DataSet := Dst;

  //navigator
  Nav := TDBNavigator.Create(Self);
  Nav.DataSource := Dsc;
  Nav.Top := 3;  
  Nav.Left := 3;
  Nav.Parent := Self;

  //editor
  Edt := TDBEdit.Create(Self);
  Edt.DataSource := Dsc;
  Edt.DataField := Fld.FieldName;
  Edt.Top := 31;
  Edt.Left := 3;
  Edt.Parent := Self;
  FPrevWindowProc := Edt.WindowProc;
  Edt.WindowProc := MyWindowProc;
end;

procedure   TForm1.MyWindowProc(var AMessage: TMessage);
begin
  if(AMessage.Msg = WM_PASTE) then
  begin
    ShowMessage('WM_PASTE, exit!');
    Exit;
  end;

  FPrevWindowProc(AMessage);
end;

end.

Upvotes: 2

Views: 277

Answers (1)

MartynA
MartynA

Reputation: 30715

Using the interposer class solution in Remy's answer to your linked question, if you create a BeforeEdit handler for your DataSet and put a breakpoint in it, you will find that the breakpoint trips before the interposer's WMPaste() method is entered.

If you then trace out of the BeforeEdit handler, you will eventually arrive in TDBEdit.KeyPress() which (in D7) contains the code below:

procedure TDBEdit.KeyPress(var Key: Char);
begin
  inherited KeyPress(Key);
  if (Key in [#32..#255]) and (FDataLink.Field <> nil) and
    not FDataLink.Field.IsValidChar(Key) then
  begin
    MessageBeep(0);
    Key := #0;
  end;
  case Key of
    ^H, ^V, ^X, #32..#255:
      FDataLink.Edit;
    #27:
      begin
        FDataLink.Reset;
        SelectAll;
        Key := #0;
      end;
  end;
end;

So, the DataSet is put into dsEdit state by the call to FDataLink.Edit() as a result of KeyPress() seeing the ^V character.

You could achieve the behavior you want by also overriding KeyPress() in the interposer class. The following will prevent pressing ^V from having any effect:

type  // This can be in your Form's unit but must go before your Form's type declaration
  TDBEdit = class(DBCtrls.TDBEdit)
    procedure WMPaste(var Message: TMessage); message WM_PASTE;
    procedure KeyPress(var Key: Char); override;
  end;
[...]

procedure TDBEdit.WMPaste(var Message: TMessage);
begin
  if not (Message.Msg = WM_PASTE) then
    inherited;
end;

procedure TDBEdit.KeyPress(var Key: Char);
begin
  case Key of
    ^V : Key := #0;
  end;  { case }
  inherited;
end;

Upvotes: 1

Related Questions