Formulare und Projekte erstellen

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Erweitern der IDE mit speziellen Interfaces


Sie haben die Möglichkeit, den in Delphi vorinstallierten Formular- und Projekt-Experten eigene Experten hinzuzufügen. Mit der Objektablage ist es zwar möglich, statische Vorlagen zu erstellen, die in einem Projekt verwendbar sind, doch ist ein Experte in der Regel leistungsfähiger, da er dynamisch ist. Er kann Benutzereingaben anfordern und je nach Eingabe unterschiedliche Dateien erzeugen.

Ein Formular- oder Projekt-Experte erstellt üblicherweise eine oder mehrere Dateien. Es ist jedoch von Vorteil, anstelle echter Dateien unbenannte, nicht gespeicherte Module zu erzeugen. Werden diese dann vom Benutzer gespeichert, fragt die IDE nach einem Dateinamen. Ein Experte erzeugt diese Module mit Hilfe eines Creator-Objekts.

Eine Creator-Klasse implementiert eine Creator-Schnittstelle, die von IOTACreator abgeleitet ist. Der Experte übergibt ein Creator-Objekt an die Methode CreateModule des Moduldienstes, und die IDE ruft vom Creator-Objekt die Parameter ab, die sie zur Erstellung des Moduls benötigt.

Beispielsweise implementiert ein Formular-Experte, der ein neues Formular erstellt, GetExisting und GetUnnamed derart, dass GetExisting den Wert False zurückgibt und GetUnnamed den Wert True. Dadurch wird ein namenloses Modul erzeugt (der Benutzer muss einen Namen wählen, um die Datei speichern zu können), das nicht auf einer vorhandenen Datei beruht (der Benutzer muss die Datei speichern, auch wenn er keine Änderungen vornimmt). Andere Creator-Methoden teilen der IDE mit, welche Art von Datei erzeugt wird (Projekt-, Unit- oder Formulardatei), stellen den Dateiinhalt bereit, oder liefern den Formular- oder Vorgängernamen bzw. andere wichtige Informationen. In ein neu erstelltes Formular oder Projekt kann der Experte anschließend weitere Module oder Komponenten einfügen.

Soll ein Formular- oder Projekt-Experte eine neue Datei erzeugen, muss in der Regel der Dateiinhalt bereitgestellt werden. Dazu schreiben Sie eine neue Klasse, die IOTAFile implementiert. Kann Ihr Experte den Standardinhalt verarbeiten, können Sie mit jeder Funktion, die IOTAFile liefert, einen nil-Wert zurückgeben.

Nehmen Sie z.B. an, in Ihrer Abteilung wäre es Vorschrift, dass ein standardisierter Kommentarblock zu Beginn jeder Quelldatei eingefügt werden muss. Sie können diesen in Form einer statischen Vorlage in der Objektablage ablegen, aber dann muss ein Autor seinen Namen sowie das Erstellungsdatum bei jeder Benutzung manuell eintragen. Einfacher wäre es, einen Creator zu verwenden, der den Kommentarblock dynamisch einträgt, wenn die Quelldatei erstellt wird.

Dazu benötigen Sie einen Experten, der neue Units und Formulare erstellt. Die meisten Creator-Funktionen liefern den Wert 0, einen Leerstring oder andere Standardwerte, mit denen die IDE angewiesen wird, neue Units oder Formulare auf die übliche, standardisierte Art und Weise zu erstellen. Überschreiben Sie GetCreatorType entsprechend, um der Tools API mitzuteilen, welche Art von Modul erzeugt werden soll, eine Unit oder ein Formular. Für eine Unit geben Sie sUnit zurück. Zur Erstellung eines Formulars geben Sie sForm zurück. Benutzen Sie zur Vereinfachung des Codes nur eine Klasse, die den Creator-Typ als Argument für den Konstruktor übernimmt. Speichern Sie den Creator-Typ in einem Datenelement, damit GetCreatorType dessen Wert zurückgeben kann. Implementieren Sie NewImplSource und NewIntfSource so, dass die erforderlichen Dateiinhalte geliefert werden.

TCreator = class(TInterfacedObject, IOTAModuleCreator)
public
constructor Create(const CreatorType: string);
  { IOTAModuleCreator }
  function GetAncestorName: string;
  function GetImplFileName: string;
  function GetIntfFileName: string;
  function GetFormName: string;
  function GetMainForm: Boolean;
  function GetShowForm: Boolean;
  function GetShowSource: Boolean;
  function NewFormFile(const FormIdent, AncestorIdent: string): IOTAFile;
  function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;
  function NewIntfSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;
  procedure FormCreated(const FormEditor: IOTAFormEditor);
  { IOTACreator }
  function GetCreatorType: string;
  function GetExisting: Boolean;
  function GetFileSystem: string;
  function GetOwner: IOTAModule;
  function GetUnnamed: Boolean;
private
  FCreatorType: string;
end;
class PACKAGE Creator : public IOTAModuleCreator {
public:
__fastcall Creator(const AnsiString creator_type)
: ref_count(0), creator_type(creator_type) {}
virtual __fastcall ~Creator();
// IOTAModuleCreator
virtual AnsiString __fastcall GetAncestorName();
virtual AnsiString __fastcall GetImplFileName();
virtual AnsiString __fastcall GetIntfFileName();
virtual AnsiString __fastcall GetFormName();
virtual bool __fastcall GetMainForm();
virtual bool __fastcall GetShowForm();
virtual bool __fastcall GetShowSource();
virtual _di_IOTAFile __fastcall NewFormFile(
const AnsiString FormIdent, const AnsiString AncestorIdent);
virtual _di_IOTAFile __fastcall NewImplSource(
const AnsiString ModuleIdent, const AnsiString FormIdent,
const AnsiString AncestorIdent);
virtual _di_IOTAFile __fastcall NewIntfSource(
const AnsiString ModuleIdent, const AnsiString FormIdent,
const AnsiString AncestorIdent);
virtual void __fastcall FormCreated(
const _di_IOTAFormEditor FormEditor);
// IOTACreator
virtual AnsiString __fastcall GetCreatorType();
 virtual bool __fastcall GetExisting();
virtual AnsiString __fastcall GetFileSystem();
virtual _di_IOTAModule __fastcall GetOwner();
virtual bool __fastcall GetUnnamed();
protected:
// IInterface
virtual HRESULT __stdcall QueryInterface(const GUID&, void**);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
private:
long ref_count;
const AnsiString creator_type;
};

Die meisten Elemente von TCreator geben 0, nil oder Leerstrings zurück. Die logischen Methoden liefern den Wert True, mit Ausnahme von GetExisting, die False zurückgibt. Die wichtigste Methode ist GetOwner, die einen Zeiger auf das aktuelle Projektmodul liefert (oder nil, wenn kein Projekt existiert). Die Ermittlung des aktuellen Projekts bzw. der aktuellen Projektgruppe ist nicht trivial. Vielmehr muss GetOwner sämtliche geöffneten Module durchlaufen. Wird eine Projektgruppe gefunden, muss sie die einzige geöffnete Projektgruppe sein, damit GetOwner ihr aktuelles Projekt zurückgeben kann. Andernfalls liefert die Funktion das erste gefundene Projektmodul (oder nil, wenn keine Projekte geöffnet sind).

function TCreator.GetOwner: IOTAModule;
var
  I: Integer;
  Svc: IOTAModuleServices;
  Module: IOTAModule;
  Project: IOTAProject;
  Group: IOTAProjectGroup;
begin
  { Aktuelles Projekt zurückgeben }
  Supports(BorlandIDEServices, IOTAModuleServices, Svc);
  Result := nil;
  for I := 0 to Svc.ModuleCount - 1 do
  begin
    Module := Svc.Modules[I];
    if Supports(Module, IOTAProject, Project) then
    begin
      { Das erste Projektmodul speichern}
      if Result = nil then
        Result := Project;
    end
    else if Supports(Module, IOTAProjectGroup, Group) then
    begin
      { Projektgruppe gefunden, aktives Projekt zurückgeben}
      Result := Group.ActiveProject;
      Exit;
    end;
  end;
end;
_di_IOTAModule __fastcall Creator::GetOwner()
{
// Aktuelles Projekt zurückgeben.
_di_IOTAProject result = 0;
  _di_IOTAModuleServices svc = interface_cast<IOTAModuleServices>(BorlandIDEServices);
  for (int i = 0; i < svc->ModuleCount; ++i)
  begin
    _di_IOTAModule module = svc->Modules[i];
_di_IOTAProject project;
_di_IOTAProjectGroup group;
if (module->Supports(project)) {
// Das erste Projektmodul speichern.
if (result == 0)
result = project;
} else if (module->Supports(group)) {
// Projektgruppe gefunden, aktives Projekt zurückgeben.
result = group->ActiveProject;
break;
}
}
return result;
}

Der Creator gibt mit NewFormSource den Wert nil zurück, damit eine Standardformulardatei erstellt wird. Die Methoden NewImplSource und NewIntfSource erstellen eine IOTAFile-Instanz, welche die Dateiinhalte zurückgibt.

Die Klasse TFile implementiert die Schnittstelle IOTAFile. Sie liefert -1 als Dateialter (was bedeutet, dass die Datei nicht existiert) und gibt die Dateiinhalte als String zurück. Zur Vereinfachung der Klasse TFile erzeugt der Creator den String, den die TFile-Klasse einfach weitergibt.

TFile = class(TInterfacedObject, IOTAFile)
public
constructor Create(const Source: string);
  function GetSource: string;
  function GetAge: TDateTime;
private
  FSource: string;
end;
constructor TFile.Create(const Source: string);
begin
  FSource := Source;
end;
  function TFile.GetSource: string;
begin
  Result := FSource;
end;
  function TFile.GetAge: TDateTime;
begin
  Result := TDateTime(-1);
end;
class File : public IOTAFile {
public:
__fastcall File(const AnsiString source);
virtual __fastcall ~File();
AnsiString __fastcall GetSource();
System::TDateTime __fastcall GetAge();
protected:
// IInterface
virtual HRESULT __stdcall QueryInterface(const GUID&, void**);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
private:
long ref_count;
AnsiString source;
};
__fastcall File::File(const AnsiString source)
: ref_count(0), source(source)
{}
AnsiString __fastcall File::GetSource()
{
return source;
}
System::TDateTime __fastcall File::GetAge()
{
return -1;
}

Sie können die Dateiinhalte in einer Ressourcendatei speichern, damit sie leichter zu ändern sind. Das folgende Beispiel codiert den Quelltext zur Vereinfachung direkt im Experten. Zudem wird vorausgesetzt, dass ein Formular vorhanden ist. Sie können folgende Änderung vornehmen: Wenn FormIdent leer ist, erstellen Sie eine einfache Unit, wenn nicht, eine Formular-Unit. Das Grundgerüst für den Code entspricht dem IDE-Standard (abgesehen von den Kommentaren am Anfang), Sie können es jedoch beliebig verändern.

function TCreator.NewImplSource(
                    const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;
var
  FormSource: string;
begin
  FormSource :=
  '{ ----------------------------------------------------------------- ' +   #13#10 +
  '%s - description'+   #13#10 +
  'Copyright ©  %y Your company, inc.'+   #13#10 +
  'Created on %d'+   #13#10 +
  'By %u'+   #13#10 +
  ' ----------------------------------------------------------------- }' +   #13#10 +   #13#10;
  return TFile.Create(Format(FormSource, ModuleIdent, FormIdent,
AncestorIdent));
}
_di_IOTAFile __fastcall Creator::NewImplSource(
const AnsiString ModuleIdent,
const AnsiString FormIdent,
const AnsiString AncestorIdent)
{
const AnsiString form_source =
"/*-----------------------------------------------------------------\n"
" %m - description\n"
" Copyright \xa9  %y Your company, inc.\n"
" Created on %d\n"
" By %u\n"
"  ---------------------------------------------------------------*/\n"
"\n"
"#include <vcl.h>\n"
"#pragma hdrstop\n"
"\n"
"#include \"%m.h\"\n"
"//-----------------------------------------------------------------\n"
"#pragma package(smart_init)\n"
"#pragma resource \"*.dfm\"\n"
"T%f *%f;\n"
"//-----------------------------------------------------------------\n"
"__fastcall T%m::T%m(TComponent* Owner)\n"
"        : T%a(Owner)\n"
"{\n"
"}\n"
"//----------------------------------------------------------------\n";
  return new File(expand(form_source, ModuleIdent, FormIdent,
AncestorIdent));
}

Zuletzt müssen noch zwei Formular-Experten erstellt werden. Einer benutzt sUnit als Creator-Typ, der andere sForm. Als Erleichterung für den Endbenutzer können Sie mit INTAServices Menübefehle zum Aktivieren der Experten in das Menü Datei > Neu einfügen. Die Aktivierung erfolgt, indem die OnClick-Behandlungsroutine eines Menübefehls die Execute-Funktion des Experten aufruft.

Einige Experten müssen die Menübefehle entsprechend den aktuellen Vorgängen in der IDE aktivieren oder deaktivieren. So sollte z.B. ein Experte, der ein Projekt für eine Quelltextprüfung übernimmt, seinen Menübefehl Einchecken deaktivieren, wenn in der IDE keine Dateien geöffnet sind. Wie Sie diese Fähigkeit in Ihren Experten integrieren, wird unter Einen Experten von IDE-Ereignissen unterrichten erläutert.

Siehe auch