2012-04-02 8 views
4

Bitte so scenerio beachten:So ändern Sie TComponentProperty so, dass nur bestimmte Elemente in der Dropdown-Liste angezeigt werden?

Ich habe Komponente TMenuItemSelector genannt, die zwei veröffentlichten Eigenschaften haben: PopupMenu - ermöglicht eine Instanz von TPopupMenu aus der Form und MenuItem die jede Instanz von TMenuItem aus der Form holen kann zu holen. Menüpunkte aus dieser PopupMenu sichtbar sind in einer Dropdown-Liste

Ich möchte Eigenschaftseditor für MenuItem Eigenschaft in einer Art und Weise ändern, dass, wenn PopupMenu dann nur zugeordnet ist.

Ich weiß, dass ich meinen eigenen Nachkomme von TComponentProperty schreiben und GetValues Methode überschreiben muss. Das Problem ist, dass ich nicht weiß, wie man auf das Formular zugreifen kann, auf dem TMenuItemSelector liegt.

Original-TComponentProperty wird mit dieser Methode alle verfügbaren Instanzen zu durchlaufen:

procedure TComponentProperty.GetValues(Proc: TGetStrProc); 
begin 
    Designer.GetComponentNames(GetTypeData(GetPropType), Proc); 
end; 

Allerdings scheint Designer vorkompilierte zu sein, so habe ich keine Ahnung, wie GetComponentNames funktioniert.

Das ist, was ich habe, so weit, ich denke nur, was die mir fehlt ist die Implementierung von GetValues:

unit uMenuItemSelector; 

interface 

uses 
    Classes, Menus, DesignIntf, DesignEditors; 

type 
    TMenuItemSelector = class(TComponent) 
    private 
    FPopupMenu: TPopUpMenu; 
    FMenuItem: TMenuItem; 
    procedure SetPopupMenu(const Value: TPopUpMenu); 
    procedure SetMenuItem(const Value: TMenuItem); 
    published 
    property PopupMenu: TPopUpMenu read FPopupMenu write SetPopupMenu; 
    property MenuItem: TMenuItem read FMenuItem write SetMenuItem; 
    end; 

type 
    TMenuItemProp = class(TComponentProperty) 
    public 
    function GetAttributes: TPropertyAttributes; override; 
    procedure GetValues(Proc: TGetStrProc); override; 
    end; 

procedure Register; 

implementation 

procedure Register; 
begin 
    RegisterPropertyEditor(TypeInfo(TMenuItem), TMenuItemSelector, 'MenuItem', TMenuItemProp); 
    RegisterComponents('Test', [TMenuItemSelector]); 
end; 

{ TMenuItemSelector } 

procedure TMenuItemSelector.SetMenuItem(const Value: TMenuItem); 
begin 
    FMenuItem := Value; 
end; 

procedure TMenuItemSelector.SetPopupMenu(const Value: TPopUpMenu); 
begin 
    FPopupMenu := Value; 
end; 

{ TMenuItemProperty } 

function TMenuItemProp.GetAttributes: TPropertyAttributes; 
begin 
    Result := inherited GetAttributes + [paValueList, paSortList]; 
end; 

procedure TMenuItemProp.GetValues(Proc: TGetStrProc); 
begin 
    //How to filter MenuItems from the form in a way that only 
    //MenuItems which belong to TMenuItemSelector.PopupMenu are displayed? \ 
    //And how to get to that form? 
    //inherited; 

end; 

end. 

Wer helfen könnte?

Danke.

+0

Verwenden Sie 'Designer.Root', um an das Formular zu gelangen, denke ich. –

Antwort

7

Wenn TMenuItemProp.GetValues() aufgerufen wird, müssen Sie auf dem TMenuItemSelector Objekt suchen, deren MenuItem Objekt bearbeitet wird, sehen, ob das Objekt ein PopupMenu zugewiesen hat, und wenn ja, dann die Schleife durch die Elemente wie neded, zB:

procedure TMenuItemProp.GetValues(Proc: TGetStrProc); 
var 
    Selector: TMenuItemSelector; 
    I: Integer; 
begin 
    Selector := GetComponent(0) as TMenuItemSelector; 
    if Selector.PopupMenu <> nil then 
    begin 
    with Selector.PopupMenu.Items do 
    begin 
     for I := 0 to Count-1 do 
     Proc(Designer.GetComponentName(Items[I])); 
    end; 
    end else 
    inherited GetValues(Proc); 
end; 

BTW, müssen Sie TMenuItemSelector und TMenuItemProp in separaten Paketen implementieren. Mit Ausnahme der Funktion RegisterComponents() (die in einem Laufzeitpaket implementiert ist) darf Entwurfszeitcode nicht in ausführbare Laufzeitdateien kompiliert werden. Es verstößt gegen die EULA, und Embarcaderos Design-Time-Pakete dürfen nicht verteilt werden. Sie müssen TMenuItemSelector in einem Runtime-only-Paket implementieren und dann implementieren TMenuItemProp und Register() in einem Entwurfszeit-only-Paket, das das Runtime-only-Paket und uses das Gerät Requires die TMenuItemSelector in deklariert wird, zB:

unit uMenuItemSelector; 

interface 

uses 
    Classes, Menus; 

type 
    TMenuItemSelector = class(TComponent) 
    private 
    FPopupMenu: TPopUpMenu; 
    FMenuItem: TMenuItem; 
    procedure SetPopupMenu(const Value: TPopUpMenu); 
    procedure SetMenuItem(const Value: TMenuItem); 
    protected 
    procedure Notification(AComponent: TComponent; Operation: TOperation); override; 
    published 
    property PopupMenu: TPopUpMenu read FPopupMenu write SetPopupMenu; 
    property MenuItem: TMenuItem read FMenuItem write SetMenuItem; 
    end; 

implementation 

{ TMenuItemSelector } 

procedure TMenuItemSelector.Notification(AComponent: TComponent; Operation: TOperation); 
begin 
    inherited; 
    if Operation = opRemove then 
    begin 
    if AComponent = FPopupMenu then 
    begin 
     FPopupMenu := nil; 
     FMenuItem := nil; 
    end 
    else if AComponent = FMenuItem then 
    begin 
     FMenuItem := nil; 
    end; 
    end; 
end; 

procedure TMenuItemSelector.SetMenuItem(const Value: TMenuItem); 
begin 
    if FMenuItem <> Value then 
    begin 
    if FMenuItem <> nil then FMenuItem.RemoveFreeNotification(Self); 
    FMenuItem := Value; 
    if FMenuItem <> nil then FMenuItem.FreeNotification(Self); 
    end; 
end; 

procedure TMenuItemSelector.SetPopupMenu(const Value: TPopUpMenu); 
begin 
    if FPopupMenu <> Value then 
    begin 
    if FPopupMenu <> nil then FPopupMenu.RemoveFreeNotification(Self); 
    FPopupMenu := Value; 
    if FPopupMenu <> nil then FPopupMenu.FreeNotification(Self); 
    SetMenuItem(nil); 
    end; 
end; 

end. 

.

unit uMenuItemSelectorEditor; 

interface 

uses 
    Classes, DesignIntf, DesignEditors; 

type 
    TMenuItemSelectorMenuItemProp = class(TComponentProperty) 
    public 
    function GetAttributes: TPropertyAttributes; override; 
    procedure GetValues(Proc: TGetStrProc); override; 
    end;  

procedure Register; 

implementation 

uses 
    Menus, uMenuItemSelector; 

procedure Register; 
begin 
    RegisterComponents('Test', [TMenuItemSelector]); 
    RegisterPropertyEditor(TypeInfo(TMenuItem), TMenuItemSelector, 'MenuItem', TMenuItemSelectorMenuItemProp); 
end; 

{ TMenuItemSelectorMenuItemProp } 

function TMenuItemSelectorMenuItemProp.GetAttributes: TPropertyAttributes; 
begin 
    Result := inherited GetAttributes + [paValueList, paSortList] - [paMultiSelect]; 
end; 

procedure TMenuItemSelectorMenuItemProp.GetValues(Proc: TGetStrProc); 
var 
    Selector: TMenuItemSelector; 
    I: Integer; 
begin 
    Selector := GetComponent(0) as TMenuItemSelector; 
    if Selector.PopupMenu <> nil then 
    begin 
    with Selector.PopupMenu.Items do 
    begin 
     for I := 0 to Count-1 do 
     Proc(Designer.GetComponentName(Items[I])); 
    end; 
    end else 
    inherited GetValues(Proc); 
end; 

end. 
+0

Der letzte Absatz geht von Laufzeitpaketen aus. Wenn Sie diese nicht verwenden, ist es ein wenig einfacher, aber Sie unterliegen immer noch den Umverteilungsbeschränkungen. –

+1

Auch wenn Laufzeitpakete in der Anwendung deaktiviert sind, die die Komponente verwendet, müssen (und sollten) Sie den Laufzeitcode und den Entwurfszeitcode in separate Pakete aufteilen. Die IDE lädt das Designtime-Paket (so klein wie möglich) und entscheidet, ob die App statisch oder dynamisch mit dem Runtime-Code verknüpft ist.Wenn sich der Entwurfszeitcode im Laufzeitpaket befindet und das Laufzeitpaket statisch verknüpft wird, wird auch der Entwurfszeitcode verarbeitet und potenziell verknüpft, was nicht zulässig ist. Die Laufzeit-/Designzeit-Trennung wurde seit D6 erzwungen. –

+0

Danke Remy. Noch eine Frage, wie funktioniert GetComponent (0). Ich habe die Dokumentation gelesen und es heißt, dass die Komponente aus einer Zwischenablage stammt, aber wie kommt sie überhaupt in die Zwischenablage? – Wodzu