Delphi组件开发 - 在组件内传播事件

时间:2017-02-24 17:09:39

标签: delphi event-handling custom-component

我正在尝试开发一个新的TEdit-Component。

TDBFilterEdit = class(TEdit)

该组件用于根据在其编辑字段中输入的字符串过滤关联的DataSet。

这是我的组件的样子:

type
TDBFilterEdit = class(TEdit)
  private
    { Private-Deklarationen }
    fFilter:String;
    fDataSource:TDataSource;
    fDataSet:TDataSet;
    fText:string;
  protected
    { Protected-Deklarationen }
    procedure SetFilter(value:String);
    procedure SetDS(value:TDataSource);
    procedure FilterRecords(DataSet:TDataSet; var Accept:Boolean);
    procedure Change(Sender:TObject);
    procedure SetText(value:String);
  public
    { Public-Deklarationen }
    constructor Create(AOwner:TComponent);
  published
    { Published-Deklarationen }
    property Text:String read fText write SetText;
    property Filter:String read fFilter write SetFilter;
    property DataSource:TDataSource read fDataSource write SetDS;
  end;

现在,在组件开发方面,我是新手。我的第一个想法是,一旦DataSource被分配给我的组件,就覆盖数据集的OnFilterRecord方法,并在我的编辑组件的文本发生变化时触发它。

procedure TDBFilterEdit.SetDS(value:TDataSource);
var
  myaccept:Boolean;
begin
  fDataSource:=value;
  fDataSet:=fDataSource.DataSet;
  if fDataSet=nil then Exit;

  fDataSet.OnFilterRecord:=FilterRecords;
  if Filter<>'' then fDataSet.OnFilterRecord(fDataSet,myaccept);
end;

我的问题是,我不知道如何让组件意识到其Text-property已更新。我尝试使用以下代码覆盖OnChange-Method

procedure TDBFilterEdit.Change(Sender:TObject);
begin
  Filter:=Text;
  inherited Change();
end;
然而,到目前为止无济于事。

1 个答案:

答案 0 :(得分:4)

  

我的问题是,我不知道如何使组件意识到其Text-property已更新。

Text属性继承自TControl。当属性值更改时,TControl会向自己发出CM_TEXTCHANGED通知消息。后代类可以通过以下任一方式处理该消息:

  1. 使用message处理程序:

    type
      TDBFilterEdit = class(TEdit)
        ...
        procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
        ...
      published
        ...
        // DO NOT redeclare the Text property here!
        // It is already published by TEdit...
      end;
    
    procedure TDBFilterEdit.CMTextChanged(var Message: TMessage);
    begin
      inherited;
      // use new Text value as needed...
      Filter := Text;
    end;
    
  2. 覆盖虚拟WndProc()方法。

    type
      TDBFilterEdit = class(TEdit)
        ...
      protected
        ...
        procedure WndProc(var Message: TMessage); override;
        ...
      end;
    
    procedure TDBFilterEdit.WndProc(var Message: TMessage);
    begin
      inherited;
      if Message.Msg = CM_TEXTCHANGED then
      begin
        // use new Text value as needed...
        Filter := Text;
      end;
    end;
    
  3. 至于你的组件的其余部分,它看起来应该更像这样:

    type
      TDBFilterEdit = class(TEdit)
      private
        { Private-Deklarationen }
        fDataSource: TDataSource;
        fDataSet: TDataSet;
        fFilter: String;
        procedure FilterRecords(DataSet: TDataSet; var Accept: Boolean);
        procedure SetDataSource(Value: TDataSource);
        procedure SetDataSet(Value: TDataSet);
        procedure SetFilter(const Value: String);
        procedure StateChanged(Sender: TObject);
        procedure UpdateDataSetFilter;
      protected
        { Protected-Deklarationen }
        procedure Notification(AComponent: TComponent; Operation: TOperation); override;
        procedure WndProc(var Message: TMessage); override;
      public
        { Public-Deklarationen }
        destructor Destroy; override;
      published
        { Published-Deklarationen }
        property DataSource: TDataSource read fDataSource write SetDataSource;
        property Filter: String read fFilter write SetFilter;
      end;
    
    ...
    
    destructor TDBFilterEdit.Destroy;
    begin
      SetDataSource(nil);
      inherited;
    end;
    
    procedure TDBFilterEdit.FilterRecords(DataSet: TDataSet; var Accept: Boolean);
    begin
      // ...
    end;
    
    procedure TDBFilterEdit.Notification(AComponent: TComponent; Operation: TOperation);
    begin
      inherited Notification(AComponent, Operation);
      if Operation = opRemove then
      begin
        if AComponent = fDataSource then
        begin
          SetDataSet(nil);
          fDataSource := nil;
        end
        else if AComponent = fDataSet then
        begin
          fDataSet := nil;
        end;
      end;
    end;
    
    procedure TDBFilterEdit.SetFilter(const Value: String);
    begin
      if fFilter <> Value then
      begin
        fFilter := Value;
        UpdateDataSetFilter;
      end;
    end;
    
    procedure TDBFilterEdit.SetDataSource(Value: TDataSource);
    begin
      if fDataSource <> Value then
      begin
        SetDataSet(nil);
    
        if fDataSource <> nil then
        begin
          fDataSource.RemoveFreeNotification(Self);
          fDataSource.OnStateChange := nil;
        end;
    
        fDataSource := Value;    
    
        if fDataSource <> nil then
        begin
          fDataSource.FreeNotification(Self);
          fDataSource.OnStateChange := StateChanged;
          SetDataSet(fDataSource.DataSet);
        end;
      end;
    end;
    
    procedure TDBFilterEdit.SetDataSet(Value: TDataSet);
    begin
      if fDataSet <> Value then
      begin
        if fDataSet <> nil then
        begin
          fDataSet.RemoveFreeNotification(Self);
          fDataSet.OnFilterRecord := nil;
        end;
    
        fDataSet := Value;
    
        if fDataSet <> nil then
        begin
          fDataSet.FreeNotification(Self);
          fDataSet.OnFilterRecord := FilterRecords;
          UpdateDataSetFilter;
        end;
      end;
    end;
    
    procedure TDBFilterEdit.StateChanged(Sender: TObject);
    begin
      if fDataSource.DataSet <> fDataSet then
        SetDataSet(fDataSource.DataSet);
    end;
    
    procedure TDBFilterEdit.UpdateDataSetFilter;
    begin
      if fDataSet <> nil then
      begin
        fDataSet.Filter := fFilter;
        fDataSet.Filtered := fFilter <> '';
      end;
    end;
    
    procedure TDBFilterEdit.WndProc(var Message: TMessage);
    begin
      inherited;
      if Message.Msg = CM_TEXTCHANGED then
        Filter := Text;
    end;
    

    更新:抱歉,我的不好。只有在代码中以可编程方式更新CM_TEXTCHANGED属性时,才会发送Text消息。要检测用户何时更改了文本,您需要处理Win32 EN_CHANGE通知:

    1. 使用message处理程序:

      type
        TDBFilterEdit = class(TEdit)
          ...
          procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
          procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
          ...
        published
          ...
          // DO NOT redeclare the Text property here!
          // It is already published by TEdit...
        end;
      
      procedure TDBFilterEdit.CMTextChanged(var Message: TMessage);
      begin
        inherited;
        // use new Text value as needed...
        Filter := Text;
      end;
      
      procedure TDBFilterEdit.CNCommand(var Message: TWMCommand);
      begin
        inherited;
        if Message.NotifyCode = EN_CHANGE then
        begin
          // use new Text value as needed...
          Filter := Text;
        end;
      end;
      
    2. 覆盖虚拟WndProc()方法。

      type
        TDBFilterEdit = class(TEdit)
          ...
        protected
          ...
          procedure WndProc(var Message: TMessage); override;
          ...
        end;
      
      procedure TDBFilterEdit.WndProc(var Message: TMessage);
      begin
        inherited;
        case Message.Msg of
          CM_TEXTCHANGED: begin
            // use new Text value as needed...
            Filter := Text;
          end;
          CN_COMMAND: begin
            if TWMCommand(Message).NotifyCode = EN_CHANGE then
            begin
              // use new Text value as needed...
              Filter := Text;
            end;
          end;
        end;
      end;
      
    3. 事实上,TCustomEdit已为您处理EN_CHANGE,并将调用其虚拟Change()方法(以触发其OnChange事件),您可以覆盖:< / p>

          type
            TDBFilterEdit = class(TEdit)
              ...
            protected
              ...
              procedure Change; override;
              ...
            end;
      
          procedure TDBFilterEdit.Change;
          begin
            inherited;
            // use new Text value as needed...
            Filter := Text;
          end;