2016-05-05 14 views
2

Ich muss TSpeedButton um zwei neue Eigenschaften erweitern. Obwohl die Eigenschaften im Objektinspektor korrekt angezeigt werden und die Werte in der DFM-Datei gespeichert werden, erhält die Methode "create" zur Laufzeit die Eigenschaften als "nil".So fügen Sie TSpeedButton eine Eigenschaft hinzu (Delphi)

Was ist los? Hier

ist die kundenspezifische Komponentencode:

unit ulbSpeedButton; 

    interface 

    uses 
     Winapi.Windows, Winapi.Messages, System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Graphics, 
     Vcl.StdCtrls, Vcl.ExtCtrls, Winapi.CommCtrl, Vcl.ImgList, 
     Vcl.Themes, System.Generics.Collections, Vcl.Buttons; 

    type 
     tlbSpeedButton = class(TSpeedButton) 
     private 
     fImageList : TImageList; 
     fImageIndex : Integer; 
     function GetImageIndex:Integer; 
     function GetImageList:TImageList; 
     procedure SetImageIndex(aIndex:Integer); 
     procedure SetImageList(aImageList:TImageList); 
     protected 

     public 
     constructor Create(AOwner: TComponent); override; 
     published 
     property ImgIndex : Integer read fImageIndex write SetImageIndex; 
     property ImgList : TImageList read GetImageList write SetImageList; 
     end; 

    procedure Register; 

    implementation 

    procedure Register; 
    begin 
     RegisterComponents('Leo Bruno', [tlbSpeedButton]); 
    end; 

    { tlbSpeedButton } 

    constructor tlbSpeedButton.Create(AOwner: TComponent); 
    begin 
     inherited Create(AOwner); 

     if ((Assigned(fImageList)) and (fImageList.Count > 0)) then 
     fImageList.GetBitmap(fImageIndex,Self.Glyph); 
    end; 

    function tlbSpeedButton.GetImageIndex: Integer; 
    begin 
     Result := fImageIndex; 
    end; 

    function tlbSpeedButton.GetImageList: TImageList; 
    begin 
     Result := fImageList; 
    end; 

    procedure tlbSpeedButton.SetImageIndex(aIndex: Integer); 
    begin 
     if fImageIndex <> aIndex then 
     begin 
     fImageIndex := aIndex; 
     Invalidate; 
     end; 
    end; 

    procedure tlbSpeedButton.SetImageList(aImageList: TImageList); 
    begin 
     if fImageList <> aImageList then 
     begin 
     fImageList := aImageList; 
     Invalidate; 
     end; 
    end; 

    end. 

Antwort

6

Zusätzlich zu dem, was KenWhite sagte, die beiden Property Setter sollten die Glyph aktualisieren (für den Fall, dass die Eigenschaften in Code nach DFM-Streaming oder sogar nur zur Entwurfszeit aktualisiert werden müssen). Stellen Sie nur sicher, dass sie die ComponentState-Eigenschaft für das Flag csLoading überprüfen, damit sie das Glyph während DFM-Streaming nicht aktualisieren, da Loaded() das behandeln wird.

Und vergessen Sie nicht, FreeNotification() auf dem zugewiesenen TImageList aufzurufen, da es außerhalb der Schaltfläche ist und potenziell freigegeben werden kann, bevor die Schaltfläche freigegeben wird.

Versuchen Sie folgendes:

unit ulbSpeedButton; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Graphics, 
    Vcl.StdCtrls, Vcl.ExtCtrls, Winapi.CommCtrl, Vcl.ImgList, 
    Vcl.Themes, System.Generics.Collections, Vcl.Buttons; 

type 
    tlbSpeedButton = class(TSpeedButton) 
    private 
    fImageList : TCustomImageList; 
    fImageIndex : Integer; 
    procedure SetImageIndex(aIndex: Integer); 
    procedure SetImageList(aImageList: TCustomImageList); 
    procedure UpdateGlyph; 
    protected 
    procedure Loaded; override; 
    procedure Notification(AComponent: TComponent; Operation: TOperation); override; 
    public 
    constructor Create(AOwner: TComponent); override; 
    published 
    property ImgIndex : Integer read fImageIndex write SetImageIndex default -1; 
    property ImgList : TCustomImageList read fImageList write SetImageList; 
    end; 

procedure Register; 

implementation 

procedure Register; 
begin 
    RegisterComponents('Leo Bruno', [tlbSpeedButton]); 
end; 

{ tlbSpeedButton } 

constructor tlbSpeedButton.Create(AOwner: TComponent); 
begin 
    inherited; 
    fImageIndex := -1; 
end; 

procedure tlbSpeedButton.Loaded; 
begin 
    inherited; 
    UpdateGlyph; 
end; 

procedure tlbSpeedButton.Notification(AComponent: TComponent; Operation: TOperation); 
begin 
    inherited; 
    if (Operation = opRemove) and (AComponent = fImageList) then 
    begin 
    fImageList := nil; 
    UpdateGlyph; 
    end; 
end; 

procedure tlbSpeedButton.UpdateGlyph; 
begin 
    if csLoading in ComponentState then Exit; 
    if Assigned(fImageList) and (fImageIndex >= 0) and (fImageIndex < fImageList.Count) then 
    fImageList.GetBitmap(fImageIndex, Self.Glyph) 
    else 
    Self.Glyph := nil; 
    Invalidate; 
end; 

procedure tlbSpeedButton.SetImageIndex(aIndex: Integer); 
begin 
    if fImageIndex <> aIndex then 
    begin 
    fImageIndex := aIndex; 
    UpdateGlyph; 
    end; 
end; 

procedure tlbSpeedButton.SetImageList(aImageList: TImageList); 
begin 
    if fImageList <> aImageList then 
    begin 
    if Assigned(fImageList) then fImageList.RemoveFreeNotification(Self); 
    fImageList := aImageList; 
    if Assigned(fImageList) then fImageList.FreeNotification(Self); 
    UpdateGlyph; 
    end; 
end; 

end. 
+0

Sehr netter Mann. Gearbeitet wie ein Charme. Nur eine Sache konnte ich nicht beheben. In der Designzeit, wenn ich einen Bildindex und eine Bildliste zuweise, wird die Glyphe aktualisiert. Wenn jedoch ein bereits gesetzter Bildindex in der Entwurfszeit geändert wird, wird das Zeichen nicht aktualisiert. Vielen Dank für Ihre Zeit und Geduld. –

+0

@LeoBruno: Sobald die Dimensionen der 'Glyph' festgelegt sind, zeichnet 'GetBitmap()' einfach neue Bilder über die vorhandene Bitmap. Ich erwartete, dass 'GetBitmap()' das 'OnChange'-Ereignis der Glyphe auslöst, so dass' TSpeedButton' automatisch 'Invalidate() 'selbst würde. Aber anscheinend ist das nicht der Fall, es sei denn, 'GetBitmap()' muss die Größe der Bitmap ändern (was beim ersten Mal der Fall ist). Also, mache '' UpdateGlyph() '' 'Invalidate()' direkt aufzurufen, um es zu erzwingen. –

4

Sie nicht die Bildliste von der Create Veranstaltung Komponente zugreifen können; Es tritt auf, bevor der andere Inhalt aus der .DFM-Datei gestreamt wurde. Die Schaltfläche muss erstellt werden, bevor die Eigenschaften festgelegt werden können, und das Ereignis Create findet zu diesem Zeitpunkt statt.

Sie müssen Ihren Code verschieben, die die Bildliste zu einer überschriebenen Loaded Methode stattdessen greift, die nach hat der gesamte Inhalt geschieht in gestreamt wurde.

type 
    tlbSpeedButton = class(TSpeedButton) 
    private 
    fImageList : TImageList; 
    fImageIndex : Integer; 
    function GetImageIndex:Integer; 
    function GetImageList:TImageList; 
    procedure SetImageIndex(aIndex:Integer); 
    procedure SetImageList(aImageList:TImageList); 
    protected 
    procedure Loaded; virtual; override; 
    public 
    constructor Create(AOwner: TComponent); override; 
    published 
    property ImgIndex : Integer read fImageIndex write SetImageIndex; 
    property ImgList : TImageList read GetImageList write SetImageList; 
    end; 

implementation 

    constructor Create(AOwner: TComponent); 
    begin 
    inherited; 
    end; 

    procedure TlbSpeedButton.Loaded; 
    begin 
    inherited; 
    if Asssigned(fImageList) and (fImageList.Count > 0) and 
     (fImageIndex > -1) then 
     fImageList.GetBitmap(fImageIndex, Self.Glyph); 
    end; 

    // The rest of your code 
end; 
+1

Die beiden Eigenschaft Setter sollte die 'Glyph' als auch für den Fall, aktualisieren Sie die Eigenschaften in Code aktualisiert werden, nachdem DFM-Streaming, oder auch zur Entwurfszeit. Stellen Sie nur sicher, dass sie die 'ComponentState'-Eigenschaft für das' csLoading'-Flag überprüfen, damit sie das 'Glyph' beim DFM-Streaming nicht aktualisieren, da' Loaded() 'das übernimmt. –

+0

@Remy: Guter Punkt. Ich werde meine Antwort aktualisieren, wenn ich Zugriff auf einen Compiler habe, um ihn vor dem Posten zu testen (um sicherzustellen, dass ich die Dinge richtig abbilde). –

+1

Und nicht zu vergessen 'FreeNotification()' auch, da die 'TImageList' außerhalb der Schaltfläche ist. –