2011-12-28 2 views
3

В моем пользовательском компоненте я создал некоторые TAction-s в качестве подкомпонентов. Все они опубликованы, но я не мог назначить их во время разработки, поскольку они недоступны через инспектора объектов.Как сделать подкомпонент TAction-s доступным во время разработки?

Как вы можете сделать их «итерируемыми» инспектором объектов? Я попытался установить Владелец действий Владельцу настраиваемого компонента (который является формой Хостинга), чтобы добиться успеха.

EDIT: Похоже, что Embarcadero изменило поведение IDE Delphi, связанное с этой проблемой. Если вы используете версии Delphi до XE, вы должны использовать решение из моего собственного ответа. Для XE и выше вы должны использовать решение от Craig Peterson.

EDIT: Я добавил свой собственный ответ, который решает проблему, т. Е. Создав экземпляр TCustomActionList в моем настраиваемом компоненте и установив его владельца в форму хостинга (владелец настраиваемого компонента). Однако я не очень доволен этим решением, так как я думаю, что экземпляр TCustomActionList является излишним. Поэтому я все еще надеюсь найти лучшее решение.

EDIT: Добавить образец кода

uses 
    .., ActnList, ..; 

type 
    TVrlFormCore = class(TComponent) 
    private 
    FCancelAction: TBasicAction; 
    FDefaultAction: TBasicAction; 
    FEditAction: TBasicAction; 
    protected 
    procedure DefaultActionExecute(ASender: TObject); virtual; 
    procedure CancelActionExecute(ASender: TObject); virtual; 
    procedure EditActionExecute(ASender: TObject); virtual; 
    public 
    constructor Create(AOwner: TComponent); override; 
    published 
    property DefaultAction: TBasicAction read FDefaultAction; 
    property CancelAction : TBasicAction read FCancelAction; 
    property EditAction : TBasicAction read FEditAction; 
    end; 

implementation 

constructor TVrlFormCore.Create(AOwner: TComponent); 
begin 
    inherited; 
    FDefaultAction := TAction.Create(Self); 
    with FDefaultAction as TAction do 
    begin 
    SetSubComponent(True); 
    Caption := 'OK'; 
    OnExecute := DefaultActionExecute; 
    end; 

    FCancelAction := TAction.Create(Self); 
    with FCancelAction as TAction do 
    begin 
    SetSubComponent(True); 
    Caption := 'Cancel'; 
    OnExecute := Self.CancelActionExecute; 
    end; 

    FEditAction := TAction.Create(Self); 
    with FEditAction as TAction do 
    begin 
    SetSubComponent(True); 
    Caption := 'Edit'; 
    OnExecute := Self.EditActionExecute; 
    end; 
end; 
+0

Просто проверяю: вы положили ваш компонент в пакет и установили, что в вашей IDE? –

+0

@Marjan Venema: Yupe, он установлен отлично. В настоящее время мне нужно выполнить проводку в событии OnCreate формы. – Luthfi

+0

@menjaraz: в коде нет ничего особенного, только создано несколько TAction-s и задано в качестве подкомпонента. Но я скоро отправлю код. – Luthfi

ответ

2

Насколько я могу судить, вы не должны так поступать.

Простой способ сделать то, что вы хотите, это создать новые автономные действия, которые могут работать с любым компонентом TVrlFormCore и установить целевой объект в обратном вызове HandlesTarget. Посмотрите на StdActns.pas для примера. Действия будут недоступны автоматически, когда sommeone упадет ваш компонент в форме, но они могут добавить их в свой список действий вручную, используя команду Новые стандартные действия .... Есть хорошая статья о регистрации стандартных действий here.

Если вы действительно хотите автосоздать действия, необходимые для установки действия Owner собственности на форму, вам необходимо указать Name. Это все, что нужно, но это действительно вводит кучу вопросов, которые вы должны работать вокруг:

  • форма владеет действиями так он добавит их опубликованный раздел своей декларации и будет автоматически создавать их как часть потоковога обработать. Чтобы обойти это, вы можете просто отключить потоковое воспроизведение, перезаписав метод действия WriteState и пропустить унаследованное поведение.
  • Поскольку вы не записываете состояние, ни одно из свойств не будет сохранено. Чтобы избежать путаницы с вашими пользователями, вы должны переключить действие с TCustomAction вместо TAction, чтобы оно ничего не отображало. Возможно, так можно правильно создать поток действий, но вы не сказали, нужно ли это.
  • Вам необходимо зарегистрировать бесплатные уведомления, если форма освобождает действие, прежде чем вы сможете.
  • Если кто-то сбросит больше одного из ваших компонентов, имена действий будут конфликтовать. Существует несколько способов справиться с этим, но самым чистым, вероятно, будет переопределить метод SetName компонента и использовать его имя в качестве префикса для имен действий. Если вы это сделаете, вам нужно использовать RegisterNoIcon с новым классом, чтобы они не отображались в форме.
  • В области структуры IDE действия будут отображаться непосредственно под формой, а не в виде вложений, например, в ActionList. Я не нашел пути вокруг этого; ни один из SetSubComponent, GetParentComponent/HasParent, или GetChildren не имеет никакого эффекта, поэтому это может быть жестко закодированное поведение. Вы также можете удалить действие из панели структуры, отдельно от компонента.

Я уверен, что он может быть улучшен, но это работает без каких-либо пользовательских редакторов свойств:

type 
    TVrlAction = class(TCustomAction) 
    protected 
    procedure WriteState(Writer: TWriter); override; 
    end; 

    TVrlFormCore = class(TComponent) 
    private 
    FDefaultAction: TVrlAction; 
    protected 
    procedure DefaultActionExecute(ASender: TObject); virtual; 
    procedure Notification(AComponent: TComponent; 
     Operation: TOperation); override; 
    procedure SetName(const NewName: TComponentName); override; 
    public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
    public 
    property DefaultAction: TVrlAction read FDefaultAction; 
    end; 

procedure Register; 

implementation 

// TVrlAction 

procedure TVrlAction.WriteState(Writer: TWriter); 
begin 
    // No-op 
end; 

// TVrlFormCore 

constructor TVrlFormCore.Create(AOwner: TComponent); 
begin 
    inherited; 
    FDefaultAction := TVrlAction.Create(AOwner); 
    with FDefaultAction do 
    begin 
    FreeNotification(Self); 
    Name := 'DefaultAction'; 
    Caption := 'OK'; 
    OnExecute := DefaultActionExecute; 
    end; 
end; 

destructor TVrlFormCore.Destroy; 
begin 
    FDefaultAction.Free; 
    inherited; 
end; 

procedure TVrlFormCore.DefaultActionExecute(ASender: TObject); 
begin 

end; 

procedure TVrlFormCore.Notification(AComponent: TComponent; 
    Operation: TOperation); 
begin 
    inherited; 
    if Operation = opRemove then 
    if AComponent = FDefaultAction then 
     FDefaultAction := nil; 
end; 

procedure TVrlFormCore.SetName(const NewName: TComponentName); 
begin 
    inherited; 
    if FDefaultAction <> nil then 
    FDefaultAction.Name := NewName + '_DefaultAction'; 
end; 

procedure Register; 
begin 
    RegisterComponents('Samples', [TVrlFormCore]); 
    RegisterNoIcon([TVrlAction]); 
end; 
+0

Я пробовал настройку Владельца в форме подхода (с кучей проблем, о которых вы упомянули, LOL!) Без успеха. То же самое относится и к решению TVrlAction. Он устраняет проблему с потоковой обработкой, но действия в пакете Object Inspector не отображаются.Я начинаю подозревать, что я должен поддерживать предопределенный интерфейс или предопределенный класс. – Luthfi

+0

@DonnVall Вы тоже установили имя? В вашем опубликованном источнике вы этого не сделали, и как владелец, так и имя должны показать его. В моих тестах это были единственные изменения, необходимые для того, чтобы ваш код «работал». –

+0

Да, да. И в моей «версии», и в вашей версии. После публикации кода я провел больше тестирования, включая установку их имен. С вашей версией я был улажен, назвав действия с помощью переопределенного метода SetName (не мог вспомнить, следую ли я также именованию их в конструкторе). Итак, вы пытались выполнить код и показать действия в инспекторе объектов? Хорошо, я повторю это снова. – Luthfi

0

Вы не можете назначить их, потому что они только для чтения дизайн:

property DefaultAction: TBasicAction read FDefaultAction; 
property CancelAction : TBasicAction read FCancelAction; 
property EditAction : TBasicAction read FEditAction; 

Вы должны изменить свой интерфейс класса для:

property DefaultAction: TBasicAction read FDefaultAction write FDefaultAction; 
property CancelAction : TBasicAction read FCancelAction write FCancelAction; 
property EditAction : TBasicAction read FEditAction write FEditAction; 

или написать соответствующий сеттер для каждого действия.

Edit:

Что вам нужно, то

  1. реализовать свои 3 пользовательские действия, как Predefined Actions (см StdActns.pas для образцов).

  2. зарегистрировать их по телефону ActnList.RegisterActions. (См RAD Studio documentation)

  3. добавить к образуют TActionList и/или TActionManager, чтобы ваш Predefined Actions появится в списке предопределенных действий в редакторе списка действий потомка каждого Tcontrol в.

Вы можете сделать обширный поиск по Google для этой темы и найти конкретный пример.

1

EDIT: Используйте это решение для версий Delphi До Delphi XE. Для XE и более поздних версий используйте Craig Peterson answer (что не требует избыточного экземпляра TCustomActionList).

После вмешательства и использования информации от Craig Peterson's answer, я решил создать экземпляр TCustomActionList в моем настраиваемом компоненте. Пока это единственный способ получить список действий в Object Inspector.

Вот код:

uses 
    ..., ActnList, ...; 

type 
    TVrlAction=class(TCustomAction) 
    protected 
    procedure WriteState(Writer: TWriter); override; 
    published 
    property Caption; 
    end; 

    TVrlActionList=class(TCustomActionList) 
    protected 
    procedure WriteState(Writer: TWriter); override; 
    end; 

    TVrlFormCore = class(TVrlItemSource) 
    protected 
    procedure Notification(AComponent: TComponent; Operation: TOperation); override; 
    procedure SetName(const NewName: TComponentName); override; 
    public 
    constructor Create(AOwner: TComponent); override; 
    end; 

implementation 

{ TVrlAction } 

procedure TVrlAction.WriteState(Writer: TWriter); 
begin 
end; 

{ TVrlActionList } 

procedure TVrlActionList.WriteState(Writer: TWriter); 
begin 
end; 

{ TVrlFormCore } 

constructor TVrlFormCore.Create(AOwner: TComponent); 
begin 
    inherited; 
    FActions := TVrlActionList.Create(AOwner); 

    FDefaultAction := TVrlAction.Create(AOwner); 
    with FDefaultAction as TVrlAction do 
    begin 
    FreeNotification(Self); 
    Caption := 'OK'; 
    OnExecute := DefaultActionExecute; 
    end; 
    FActions.AddAction(TContainedAction(FDefaultAction)); 

    FCancelAction := TVrlAction.Create(AOwner); 
    with FCancelAction as TVrlAction do 
    begin 
    FreeNotification(Self); 
    Caption := 'Cancel'; 
    OnExecute := Self.CancelActionExecute; 
    end; 
    FActions.AddAction(TContainedAction(FCancelAction)); 

    FEditAction := TVrlAction.Create(AOwner); 
    with FEditAction as TVrlAction do 
    begin 
    FreeNotification(Self); 
    Caption := 'Edit'; 
    OnExecute := Self.EditActionExecute; 
    end; 
    FActions.AddAction(TContainedAction(FEditAction)); 
end; 

procedure TVrlFormCore.Notification(AComponent: TComponent; 
    Operation: TOperation); 
begin 
    inherited; 
    if Operation=opRemove then 
    begin 
    if AComponent = FMaster then 
     FMaster := nil 
    else if (AComponent is TVrlFormCore) then 
     FDetails.Remove(TVrlFormCore(AComponent)) 
    else if AComponent=FDefaultAction then 
     FDefaultAction := nil 
    else if AComponent=FCancelAction then 
     FCancelAction := nil 
    else if AComponent=FEditAction then 
     FEditAction := nil; 
    end; 
end; 

procedure TVrlFormCore.SetName(const NewName: TComponentName); 
begin 
    inherited; 
    if FActions<>nil then 
    FActions.Name := NewName + '_Actions'; 

    if FDefaultAction <> nil then 
    FDefaultAction.Name := NewName + '_DefaultAction'; 
    if FCancelAction <> nil then 
    FCancelAction.Name := NewName + '_CancelAction'; 
    if FEditAction <> nil then 
    FEditAction.Name := NewName + '_EditAction'; 
end; 
Смежные вопросы