2016-05-05 2 views
2

Мне нужно добавить 2 новых свойства в TSpeedButton. Хотя свойства правильно отображаются в инспекторе объектов и его значения, хранящиеся в файле DFM, метод «create» во время выполнения сохраняет свойства «nil».Как добавить собственность в TSpeedButton (Delphi)

Что не так?

Вот заказной код компонента:

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. 

ответ

6

В дополнение к тому, что сказал KenWhite, два разработчики свойств должны обновлять Glyph (в случае, если свойства необходимо обновить в коде после потоковой передачи DFM или даже просто во время разработки). Просто убедитесь, что они проверяют свойство ComponentState для флага csLoading, поэтому они не обновляют Glyph во время потоковой передачи DFM, так как Loaded() справится с этим.

И не забудьте позвонить FreeNotification() по назначенному TImageList, так как он является внешним по отношению к кнопке и может быть освобожден до освобождения кнопки.

Попробуйте это:

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

Очень симпатичный man.Worked как шарм. Только одно, что я не мог исправить. Во время разработки, если я назначаю imageindex и imagelist, глиф обновляется. Но изменение уже заданного imageindex в режиме разработки не обновляет глиф. Большое вам спасибо за ваше время и терпение. –

+0

@LeoBruno: После того, как были установлены размеры 'Glyph',' GetBitmap() 'будет просто рисовать новые изображения поверх существующего растрового изображения. Я ожидал, что 'GetBitmap()' запускает событие 'OnChange' глифа, поэтому' TSpeedButton' будет автоматически '' Определять() 'сам. Но, по-видимому, это не так, если только 'GetBitmap()' не должен изменять размер растрового изображения (который он делает впервые). Поэтому просто запустите 'UpdateGlyph()' call 'Invalidate()', чтобы заставить его. –

4

Вы не можете получить доступ к ImageList из Create события компонента; это происходит до того, как другой контент был передан из файла .DFM. Кнопка должна быть создана до того, как ее свойства будут установлены, и в это время произойдет событие Create.

Вы должны переместить свой код, который обращается к ImageList к перегруженной Loaded методу вместо, который происходит после весь контент был струился.

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

Два сеттеры собственности должны обновить 'Glyph', а также, в случае свойства обновляются в коде после DFM потоковой передачи, или даже во время разработки. Просто убедитесь, что они проверяют свойство 'ComponentState' для флага' csLoading', чтобы они не обновляли 'Glyph' во время потоковой передачи DFM, так как' Loaded() 'будет обрабатывать это. –

+0

@Remy: Хорошая точка. Я буду обновлять свой ответ, когда у меня будет доступ к компилятору, чтобы протестировать его перед публикацией (чтобы убедиться, что я правильно прав). –

+1

И не забывайте также 'FreeNotification()', так как 'TImageList' является внешним по отношению к кнопке. –

Смежные вопросы