2017-02-24 1 views
0

Ich versuche eine neue TEdit-Komponente zu entwickeln.Delphi-Komponentenentwicklung - Ereignisse in der Komponente weitergeben

TDBFilterEdit = class(TEdit) 

Die Komponente soll ein zugeordnetes DataSet basierend auf der Zeichenfolge filtern, die in seinem Bearbeitungsfeld eingegeben wird.

das ist, was meine Komponente wie folgt aussieht: I Jetzt

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; 

, ziemlich Anfänger bin, wenn es um Komponenten-Entwicklung kommt. Meine erste Idee war, die OnFilterRecord-Methode des Datasets zu überschreiben, sobald die DataSource meiner Komponente zugewiesen wird und diese ausgelöst wird, wenn sich der Text meiner Edit-Komponente ändert.

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; 

Mein Problem ist, ich weiß nicht, wie man die Komponente darauf aufmerksam macht, dass ihre Text-Eigenschaft aktualisiert wurde. Ich habe versucht, die OnChange-Methode mit folgendem Code

procedure TDBFilterEdit.Change(Sender:TObject); 
begin 
    Filter:=Text; 
    inherited Change(); 
end; 

jedoch bisher ohne Erfolg zu übersteuern.

+0

Der übliche Weg von Wenn Sie dies tun, schreiben Sie eine Methode (die als DoOnChange bezeichnet werden kann), die innerhalb Ihrer Änderung aufgerufen wird, und führt alles aus, was Sie möchten, einschließlich des Aufrufs spezieller Ereignisbehandlungsroutinen, die Sie möglicherweise bereitstellen möchten. Schauen Sie sich die verschiedenen DoOnxxxx-Methoden in der VCL an und Sie sollten bald auf die Idee kommen. – MartynA

Antwort

4

Mein Problem ist, ich weiß nicht, wie Sie die Komponente darauf aufmerksam machen, dass ihre Text-Eigenschaft aktualisiert wurde.

Die -Eigenschaft wird von TControl übernommen. Wenn sich der Eigenschaftswert ändert, gibt TControl eine Benachrichtigungsnachricht an CM_TEXTCHANGED aus. Abgeleitete Klassen können diese Nachricht entweder handhaben:

  1. mit einem message Handler:

    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. die virtuelle WndProc() Methode überschrieben.

    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; 
    

Wie für den Rest Ihrer Komponente, sollte es so aussehen:

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; 

UPDATE: sorry, mein schlecht. Die CM_TEXTCHANGED Nachricht wird nur gesendet, wenn die Eigenschaft programmgesteuert in Code aktualisiert wird. Zu erkennen, wenn der Benutzer den Text geändert wird, müssen Sie die Win32 EN_CHANGE Benachrichtigung behandeln statt:

  1. eine message Handler:

    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. die virtuelle WndProc() Methode überschrieben.

    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; 
    

In der Tat behandelt TCustomEdit bereits EN_CHANGE für Sie, und wird seine virtuelle Change() Methode aufrufen (seine OnChange Ereignis ausgelöst), die Sie überschreiben können:

type 
     TDBFilterEdit = class(TEdit) 
     ... 
     protected 
     ... 
     procedure Change; override; 
     ... 
     end; 

    procedure TDBFilterEdit.Change; 
    begin 
     inherited; 
     // use new Text value as needed... 
     Filter := Text; 
    end; 
+0

Vielen Dank für Ihre Antwort, es half mir weiter zu meinem Ziel. Ich wählte Methode 1 und machte einen CMTextChanged-message-Handler, aber es wird nicht ausgelöst. Ich versuchte eine CM_MOUSEENTER-Nachricht Handler, die zwar ausgelöst, aber die Text-Eigenschaft leer blieb. Muss ich die anderen Prozeduren wie Notification, StateChange und WndProc implementieren/überschreiben? Auch das Zuweisen der Datenquelle funktioniert, aber aus irgendeinem seltsamen Grund bleibt das DataSet meiner DataSource Null –

+0

Ich fand heraus, dass der richtige Nachrichten-Handler, den ich suche, CN_COMMAND ist, jetzt kann ich die Prozedur für die feuern erster Buchstabe gebe ich in das Editierfeld ein. eine Idee, wie Sie dies bei jedem Tastendruck aufrufen? –

+0

Ich habe herausgefunden, dass das Dataset nur den OnFilterRecord ausführt, wenn von filtered: = false zu filtered: = true gewechselt wird. Da ich die gefilterte Eigenschaft nirgends zurückgesetzt habe, war der Wert immer wahr, nachdem ich den ersten Buchstaben in mein Bearbeitungsfeld eingegeben hatte. nun verhält sich die Komponente fast wie beabsichtigt, es bleibt nur noch das mysteriöse Verhalten des "nil-Dataset" beim Zuordnen der Datenquelle (vielleicht benutze ich einfach ein Dataset direkt anstelle einer Datenquelle) und ausgiebiges Testen meiner FilterRecords-Methode. –

Verwandte Themen