Зарегистрируйте пользовательскую форму, чтобы я мог наследовать ее от нескольких проектов, не копируя форму в папку репозитория объектов - PullRequest
8 голосов
/ 17 сентября 2010

У меня есть пользовательский фрейм, который мне нужно наследовать в нескольких проектах.Этот фрейм включает в себя некоторый код и некоторые компоненты, и он находится где-то на диске, в своем собственном каталоге проекта.Я не хочу COPY помещать его в папку репозитория объектов, что мне не подходит: в итоге у меня будет две копии формы, одна в моем репозитории с поддержкой Mercurial, однав хранилище объектов Delphi.Абсолютно не очень хорошая идея.

Я хочу, чтобы мой кадр был в пакете, и чтобы пакет делал все, что требуется, чтобы сделать кадр известным в IDE и позволить IDE создавать новых братьев и сестер данного объекта.фрейм, без фактического добавления фрейма в каждый отдельный проект.

То, что я сделал до сих пор, проблемы, с которыми я столкнулся, решения, которые я пытался:

  1. Я добавил свой фрейм впакет, зарегистрировавший мой фрейм, используя RegisterClass и RegisterNoIcon. Проблема: Когда я захожу в какой-то другой проект и пытаюсь открыть производный фрейм для редактирования, он говорит, что не может найти мой оригинальный фрейм.
  2. Чтобы решить проблему «1», я решил зарегистрировать свой фрейм как пользовательский модуль.Поэтому я позвонил RegisterCustomModule(TMyFrameName, TCustomModule). Проблема: В «другом» проекте, который я открываю производный фрейм, IDE не создает компоненты в моем исходном фрейме, и IDE жалуется на отсутствие одного из «унаследованных» компонентов.
  3. Чтобы исправить «2», я решил протянуть руку помощи IDE, позвонив по номеру InitInheritedComponent(Self, TFrame).Это помогло, когда я попытался открыть фрейм в «другом» проекте, все было воссоздано, и я смог увидеть фрейм, как и ожидал. Проблема: Когда я сохраняю фрейм, он забывает все о унаследованных компонентах, рассматривает каждый отдельный компонент как новый компонент, добавленный в этот конкретный фрейм.Если я смотрю в сохраненный DFM, все начинается с «объекта», ничто не начинается с «унаследованного», как я ожидал.

К сожалению, я застрял в проблеме «3».Я пробовал копаться в Classes.pas, ToolsAPI, DesignIntf ​​и DesignEditors, но не нашел ничего полезного.Очевидно, атрибут «унаследованный», который я надеялся увидеть в DFM, генерируется TWriter, когда его свойство «TWriter.Ancestor» назначается перед потоковой передачей TComponent, но у меня нет способа его настроить, в среде IDE его нужно установитьвверх.И я не могу убедить IDE сделать это для меня.

Вот кумулятивные, соответствующие части кода:

TTestFrame = class(TFrame)
public
  constructor Create(Owner:TComponent);override;
end;

constructor TTestFrame.Create(Owner: TComponent);
begin
  inherited;
  if csDesignInstance in ComponentState then InitInheritedComponent(Self, TFrame);
end;

procedure Register;
begin
  RegisterClass(TTestFrame);
  RegisterNoIcon([TTestFrame]);
  RegisterCustomModule(TTestFrame, TCustomModule);
end;

Любые идеи, кроме ", сдавайтесь иположить ваши вещи в хранилище объектов "?Спасибо!


Редактировать

Почему мне нужно это сделать и почему решения, которые зависят от фактических путей, записанных в файлы моего проекта, не работают:Я хочу поддержать ветвление: когда одна ветвь имеет смысл ожидать, что несколько версий одного и того же проекта будут «живыми» в разных каталогах на одной машине.Следствие, я не могу иметь несколько версий одного и того же проекта в одном и том же месте одновременно.

Чтобы убедиться, что это работает, я решил сделать так, чтобы мои проекты не зависели от того, где они живут, и обеспечить соблюдениеэто у меня есть все в нашей команде Clone (терминология Mercurial) или Check Out (терминология SVN) наших проектов в разных каталогах.Жестко запрограммированный путь в моей системе не годится для системы моего коллеги: если кто-либо из нас совершит ошибку, запрограммировав какой-либо путь в приложение, то вскоре у него будет тормозить один из нас, поэтомуошибка исправляется.

Это, конечно, проблема с формами и фреймами, которые являются частью некоторой библиотеки (поэтому они не находятся в каталоге нашего проекта), от которой нам нужно наследовать!Чтобы получить поддержку IDE при работе с этими файлами, нам нужно временно добавить их в проект, и нам не нужно забывать удалять их после завершения.Если мы забудем и нажмем / вернем изменения, изменения могут затормозить сборку для наших коллег (поскольку у них библиотеки извлечены в разных местах).

Чтобы решить эту проблему, я попытался добавить эти кадры и формы в пакет времени разработки (пакеты загружаются с использованием полного пути в IDE, но путь не является частью файлов проекта, поэтому все в порядке). К сожалению, это не удалось, и вот почему я опубликовал этот вопрос.

Ответы [ 3 ]

3 голосов
/ 18 сентября 2010

Существует несколько аспектов этой проблемы:

  • Использование фреймов из пакета во время разработки для включения их в формы.
  • Создание нового фрейма в проекте, который наследуется от фрейма в пакете.
  • Разрешение различным ветвям проекта использовать разные версии пакета и, следовательно, фреймы, не сталкиваясь с жестко заданными путями в dpr или dproj.

Разрешение различным веткам проекта использовать разные версии ваших собственных пакетов

  • Создание переменной среды в Delphi IDE с помощью Tools | Варианты | Переменные среды и укажите ее в папке, где находятся фреймы для вашей текущей ветви. Для этого обсуждения мы назовем эту переменную среды «MyLib» и укажем ее в папку «D: \ Wh независимо \ Version1».
  • Зайдите в редактор реестра и экспортируйте эту запись в файл "MyLibEnvironmentVariable.reg". Передайте этот файл под контроль исходного кода и отредактируйте его, чтобы удалить все другие переменные среды, которые также присутствовали в том же разделе реестра.
  • Экспорт содержимого ключа Known Packages вашей установки Delphi.
  • Редактировать экспортируемый файл - Удалите все значения, которые не являются вашими собственными пакетами. - Измените значения ваших собственных пакетов, например, с D:\\Whatever\\Version1\xxx.bpl на $(MyLib)\\xxx.bpl.
  • Удалите все ключи, указывающие на ваши собственные пакеты, из ключа Known Packages и импортируйте только что отредактированный файл. Это эффективно изменяет ваши регистрации пакетов на использование MyLib var и гарантирует, что IDE теперь будет также использовать MyLib var для поиска ваших пакетов.

Использование рамок из пакета во время разработки

  • Создайте пакет "LibraryPackage", сохраните его в папке D: \ Wh независимо \ Version1.
  • Добавьте фрейм «LibraryFrame», сохраните его в папке D: \ Wh независимо \ Version1.
  • Поместите некоторые элементы управления на рамку или сделайте ее уродливой, чтобы вы могли ее визуально распознать.
  • Добавьте Register; процедуру к модулю LibraryFrame и поместите RegisterComponents('MyFrames', [TLibraryFrame]); в ее реализацию.
  • Сборка пакета и установка его в IDE.
  • Удалите жестко закодированный путь к этому пакету из пути к библиотеке Option Environment Environment и / или измените его на использование переменной среды $ (MyLib).
  • Измените запись реестра для этого пакета, чтобы использовать переменную среды MyLib.
  • Рамка все еще не будет доступна в палитре инструментов | Рамки, но она будет доступна на собственной странице палитры инструментов MyFrames, и вы можете включить ее в любую форму. Среда IDE добавит имя модуля фрейма в условие использования форм. Он не должен ничего добавлять к dpr, но он все равно может добавить полностью жестко заданный путь к вашему dproj. Чтобы избежать этого, вам придется выполнить экспорт / изменить путь bpl, чтобы использовать трюк $ (MyLib) / двойной щелчок также для этого пакета.

Наследование от рамы в пакете

Теперь, когда все вышеперечисленное настроено, Frame из пакета по-прежнему недоступен для наследования в любом другом проекте, кроме библиотеки, в которой он был «создан». Однако теперь довольно просто сделать его доступным для наследования. Просто добавьте LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, в dpr вашего проекта, и теперь рамка будет отображаться в списке «наследуемых элементов», когда вы используете «Add New | Other» для добавления чего-либо в ваш проект.

К сожалению, однако, когда вы выберете его, IDE будет жаловаться: Cannot open file "D:\Whatever\SecondFormReusingFrame\LibraryFrame_fr.pas". The system cannot find the file specified.

Очевидно, теперь он ожидает библиотечный фрейм в папке проекта вместо того, чтобы пытаться найти его в папке MyLib. В среде IDE также выделяется тип TLibraryFrame в форме, хотя нажатие Ctrl приводит к выводу правильного файла ...

Изменение LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, на $(MyLib)\LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame},помогает в том, что IDE больше не жалуется на то, что не нашел LibraryFrame_fr.pas. Но это приводит к тому, что когда вы сохраняете все, среда IDE «любезно» меняет ее на относительный путь. В моем случае ..\FrameToBeReusedSecond\LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame},, который побеждает весь объект упражнения, поскольку вновь вводит зависимость от имен путей, хотя и частично.

Однако, оставить LibraryFrame_fr in 'LibraryFrame_fr.pas' {LibraryFrame}, в dpr тоже не вариант. Когда вы перезагружаете проект, IDE жалуется на то, что не может найти фрейм, даже если его можно найти в пути поиска.

Также оставить это как есть и игнорировать жалобу IDE при выводе из этого фрейма, на самом деле не вариант. IDE не добавляет соответствующий dfm ... И хотя вы можете отредактировать использование в dpr с MyOwnFrame_fr in 'MyOwnFrame_fr.pas' до MyOwnFrame_fr in 'MyOwnFrame_fr.pas' {LibraryFrame1} и добавить заглушку dfm вручную, IDE все еще не может найти библиотеку LibraryFrame_fr.pas. ...

В общем, Cosmin, кажется, IDE слишком настроен на то, что он хочет и ожидает. Я думаю, что то, что вы хотите, достижимо, но только в том случае, если вы хотите и можете иметь свои библиотечные папки всегда в одном и том же месте относительно ваших проектов.

1 голос
/ 17 сентября 2010

А что не так с простым добавлением фрейма, который вы хотите использовать повторно, в проект, в котором вы хотите его повторно использовать?

, например

  • Project1.dpr использует Form1 и Frame1, вы хотите повторно использовать Frame1.
  • Запустите новый проект форм VCL в другой папке.
  • Когда вы щелкаете по Рамке на «Стандартной» странице палитры инструментов, появляется сообщение, что ее нет.
  • Добавьте модуль Frame1 к этому проекту через менеджера проектов (чтобы он был добавлен в список использований в dpr).
  • Теперь, щелкнув Рамки на «Стандартной» странице палитры инструментов, вы увидите Рамку 1, доступную для выбора.
  • И щелкните правой кнопкой мыши проект в менеджере проектов, а затем выберите Добавить новый | other и переход к «наследуемым элементам», также показывает Frame1 как наследуемый элемент.

Редактировать

Если вы не хотите включать жестко закодированный путь к фрейму в dpr, всегда есть уловка использования переменных среды IDE.

  • Перейти в Инструменты | Варианты | Переменные среды.
  • Добавьте переменную MYLIB и укажите ее в папке, подходящей для текущей ветви.
  • Добавьте файл, содержащий путь к папке, к вашим источникам и добавьте его в систему контроля версий. Это может быть экспорт ключа реестра, который теперь содержит значение MYLIB.
  • Добавьте $ (MYLIB) к пути к библиотеке вашего проекта.
  • Добавьте рамки к вашему проекту. Теперь они должны быть включены в dpr без пути (потому что их можно найти в пути к библиотеке).
  • При интеграции ветвей: убедитесь, что исходный файл со значением для MYLIB изменен соответствующим образом.
  • При переключении веток: активируйте правильное значение для MYLIB. Если вы добавили файл .reg в систему управления версиями: просто дважды щелкните по нему, чтобы изменить значение ключа реестра MYLIB.
0 голосов
/ 21 сентября 2010

Когда мы создавали процесс сборки из последней компании, в которой я работал, мы хотели сделать то же самое.Мы использовали subversion, и у нас был проект для наших общих компонентов, который содержал проект (Finalbuilder) для создания всех наших общих пакетов.

Затем я собирался использовать технику, аналогичную переменной Marjan (MyLib).И запустите Delphi из пакетного файла с последним набором компонентов.

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

c: \ BDS \ Components \ D12 \ Bpl (это может отличаться от разработчика к разработчику)

Но код, который находился в

c: \ BDS \ MyProject \ Shared, всегда был связан при компиляции, потому что он находился в относительном пути поиска при создании проекта

c: \ BDS \ MyProjectExperimental также будет использовать правильно разветвленный код при сборке.

Мы фактически обошли проблему хранилища, используя CodeSmith (генератор кода) для генерации фреймов, с дополнительным бонусом, что они будутпоместите в нужную папку с правильными соглашениями об именах (и любые изменения, относящиеся к отрасли).Мы сделали это для экономии времени, но (случайно) все вместе избежали этой проблемы.

Для шаблонов CodeSmith потребовалось немного времени для настройки первого кадра, но затем мы легко адаптировали егосоздавать другие подклассы без слишком большого количества циклов мозга (для таких вещей, как dataModules).

cfEditFrame.dfm.cst

<%@ CodeTemplate Language="C#" TargetLanguage="Delphi" Src="" Inherits="" Debug="False" Description="cfEditFrames.dfm Template" ResponseEncoding="ASCII" %>  
<%@ Property Name="TypeName" Type="System.String" Default="TypeName" Optional="False" Category="Strings" Description="Name of Type. eg Account; AgeMonths" %>  
inherited cf<%=TypeName%>EditFrame: Tcf<%=TypeName%>EditFrame  
  Width = 425  
  Height = 63  
end  

cfEditFrame.cst - один сценарий, который нам нужно было вызвать для генерацииновый подклассный фрейм

<%@ CodeTemplate Language="C#" TargetLanguage="Delphi" Src="" Inherits="" Debug="False" Description="cfSDMEditComp Template." ResponseEncoding="ASCII" %>  
<%@ Property Name="OutputFolder" Type="System.String" Default="..\\SharedNonInstalled" Optional="False" Category="Strings" Description="" %>  
<%@ Property Name="TypeName" Type="System.String" Default="TypeName" Optional="False" Category="Strings" Description="Name of Type. eg Account; AgeMonths." %>    
<%@ Register Name="EditFramesPasTemplate" Template="cfEditFrames.pas.cst" %>  
<%@ Register Name="EditFramesDfmTemplate" Template="cfEditFrames.dfm.cst" %>  
<%@ Import NameSpace="System.IO" %>  
<script runat="template">  

public override void Render(TextWriter writer)  
{  
  EditFramesPasTemplate cfEditFramesPasTemplate = new EditFramesPasTemplate();  
  this.CopyPropertiesTo(cfEditFramesPasTemplate);  
  cfEditFramesPasTemplate.RenderToFile(String.Format("{0}\\Edit\\cf{1}EditFrames.pas", OutputFolder, TypeName), true);  

  EditFramesDfmTemplate cfEditFramesDfmTemplate = new EditFramesDfmTemplate();  
  this.CopyPropertiesTo(cfEditFramesDfmTemplate);  
  cfEditFramesDfmTemplate.RenderToFile(String.Format("{0}\\Edit\\cf{1}EditFrames.dfm", OutputFolder, TypeName), true);  
}

</script>

Могут быть и другие генераторы исходного кода, которые делают это точно так же, если не лучше.У нас был CodeSmith, и поэтому мы его использовали.Надеюсь, вышеупомянутые файлы отформатированы ОК.Я НАСТОЛЬКО новичок и надеюсь, что рендеринг HTML-подобного кода правильный.

Поскольку в этих кадрах есть свойства и компоненты, которые вы хотите наследовать, вам нужно сделать одну странную вещь.Вам нужно сделать это в два этапа.Сначала вам нужно добавить свойства фрейма в первый класс, а затем добавить компоненты в его подкласс.

Например, мы добавляем пользовательские свойства в cfBaseEditFrames.TcfBaseEditFrame.

Затем подкласс этого класса в cfEditFrames.TcfEditFrame = class (TcfBaseEditFrame).Здесь мы добавляем наши компоненты (в нашем примере это TActionList и TImageList)

При регистрации их в пакете мы добавили

RegisterCustomModule (TcfBaseEditFrame, TWinControlCustomModule);

Мызатем убедитесь, что этот пакет находится в нашей проектной группе и нет проблем с открытием новых подклассов фреймов.

Последнее замечание: по памяти было важно, чтобы добавочный компонент был добавлен к кадру-потомку (TcfEditFrame).Невозможно добавить компоненты в TcfBaseEditFrame и свойства в TcfEditFrame.

BaseEditFrames.pas

unit BaseEditFrames;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs;

type
  TBaseEditFrame = class(TFrame)
  private
    { Private declarations }
    FNewFormProperty: string;
  published
    { Published declarations }
    property NewFormProperty: string read FNewFormProperty write FNewFormProperty;
  end;

implementation

{$R *.dfm}

end.

BaseEditFrames.dfm

object BaseEditFrame: TBaseEditFrame
  Left = 0
  Top = 0
  Width = 320
  Height = 240
  TabOrder = 0
end

EditFrames.pas

unit EditFrames;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ActnList, BaseEditFrames;

type
  TEditFrame = class(TBaseEditFrame)
    ActionList: TActionList;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.dfm}

end.

EditFrames.dfm

object EditFrame: TEditFrame
  Left = 0
  Top = 0
  Width = 320
  Height = 240
  TabOrder = 0
  object ActionList: TActionList
    Left = 72
    Top = 16
  end
end

FramePackage.dpk

package FramePackage;

{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO OFF}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DESCRIPTION 'Inheritable Frames'}
{$IMPLICITBUILD ON}

requires
  rtl,
  vcl,
  designide;

contains
  RegisterFramePackage in 'RegisterFramePackage.pas',
  BaseEditFrames in 'BaseEditFrames.pas' {BaseEditFrame: TFrame},
  EditFrames in 'EditFrames.pas' {EditFrame: TFrame};

end.
unit RegisterFramePackage;

interface

procedure Register;

implementation

uses Classes, DesignIntf, WCtlForm, BaseEditFrames;

procedure Register;
begin
  RegisterCustomModule(TBaseEditFrame, TWinControlCustomModule);
end;

end.

Вам необходимо установить этот пакет времени разработки.Затем вы можете создать рамку в другом проекте, например:

EditFrameDescendants.pas

unit EditFrameDescendants;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, EditFrames, ActnList;

type
  TEditFrameDescendant = class(TEditFrame)
    Action1: TAction;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.dfm}

end.

EditFrameDescendants.dfm

inherited EditFrameDescendant: TEditFrameDescendant
  ParentFont = False
  inherited ActionList: TActionList
    object Action1: TAction
      Caption = 'Action1'
    end
  end
end

Вы сможете открыть EditFrameDescendantотредактируйте его удивительный «NewFormProperty» и добавьте действия в его список действий.У меня работает .... Удачи

...