Inhaltsverzeichnis

TWvdSDocumentFrame

Der TWvdSDocumentFrame ist die Basisklasse für alle visuellen Inhalte, die ein Add-in bereitstellt — sowohl für Dokument-Tabs als auch für Sidebar-Panels. Er ist ein gewöhnlicher LCL-TFrame, den der Host in die Shell einbettet. Das Add-in gestaltet den Frame-Inhalt, der Host kümmert sich um den Rahmen: Tab-Titel, Icon, Dirty-State und Lebenszyklus.

Zurück zur SDK-Übersicht oder zur Hauptübersicht.

Warum ein Frame

Die Entscheidung für TFrame als Basisklasse ist bewusst getroffen. Ein Frame ist ein Container, der sich in beliebige übergeordnete Controls einbetten lässt, ohne ein eigenes Fenster zu erzeugen. Das unterscheidet ihn von einem TForm, das immer ein Top-Level-Fenster ist. Da der Host die Kontrolle über die Shell behält, darf ein Add-in kein eigenes Fenster erzeugen. Der Frame passt sich in das vom Host bereitgestellte Layout ein — sei es ein Dokument-Tab, ein Sidebar-Panel oder ein Bottom-Panel.

Dokumenttypen

Ein Add-in registriert Dokumenttypen über eine IWvdSDocumentFactory beim IDocumentService. Jeder Dokumenttyp hat eine eindeutige Bezeichnung und eine Art:

type
  TDocumentKind = (
    dkSingleInstance,   // Nur ein Tab dieses Typs gleichzeitig
    dkMultiInstance     // Mehrere Tabs möglich
  );

IWvdSDocumentFactory

type
  IWvdSDocumentFactory = interface
    function CreateFrame(AOwner: TComponent): TWvdSDocumentFrame;
    function GetDocumentType: string;
    function GetDocumentKind: TDocumentKind;
    function GetDisplayName: string;
    function GetIcon: string;
  end;

Die Factory erzeugt auf Anfrage einen neuen Frame. Der Host ruft CreateFrame auf, wenn ein Dokument geöffnet wird, und bettet den zurückgegebenen Frame in einen Tab ein. AOwner ist der Tab-Container des Hosts — der Frame wird automatisch freigegeben, wenn der Tab geschlossen wird.

GetDocumentType liefert den eindeutigen Typnamen (z.B. assets.editor). Dieser Name muss mit dem Wert übereinstimmen, der bei IDocumentService.OpenDocument verwendet wird.

// Beispiel: Factory-Implementierung
type
  TAssetEditorFactory = class(TInterfacedObject, IWvdSDocumentFactory)
    function CreateFrame(AOwner: TComponent): TWvdSDocumentFrame;
    function GetDocumentType: string;
    function GetDocumentKind: TDocumentKind;
    function GetDisplayName: string;
    function GetIcon: string;
  end;
 
function TAssetEditorFactory.CreateFrame(AOwner: TComponent): TWvdSDocumentFrame;
begin
  Result := TAssetEditorFrame.Create(AOwner);
end;
 
function TAssetEditorFactory.GetDocumentType: string;
begin
  Result := 'assets.editor';
end;
 
function TAssetEditorFactory.GetDocumentKind: TDocumentKind;
begin
  Result := dkMultiInstance;
end;

TDocumentContext

Jeder geöffnete Dokument-Tab erhält einen TDocumentContext, über den das Add-in den Tab-Zustand steuern kann.

type
  IDocumentContext = interface
    procedure SetTitle(const ATitle: string);
    procedure SetIcon(const AIcon: string);
    procedure SetDirty(AValue: Boolean);
    function GetDirty: Boolean;
    procedure SetReadOnly(AValue: Boolean);
    function GetReadOnly: Boolean;
  end;

Frame-Callbacks

Der TWvdSDocumentFrame stellt virtuelle Methoden bereit, die der Host bei bestimmten Ereignissen aufruft:

type
  TWvdSDocumentFrame = class(TFrame)
  protected
    function DoCanClose: Boolean; virtual;
    procedure DoActivate; virtual;
    procedure DoDeactivate; virtual;
    procedure DoSave; virtual;
  end;

Vollständiges Beispiel

unit MyDocumentFrame;
 
interface
 
uses
  Classes, Controls, Forms, StdCtrls,
  WvdS.Document.Host.Api;
 
type
  TMyDocumentFrame = class(TWvdSDocumentFrame)
  private
    FEdit: TMemo;
    FContext: IDocumentContext;
    procedure HandleEditChange(Sender: TObject);
  protected
    function DoCanClose: Boolean; override;
    procedure DoSave; override;
    procedure DoActivate; override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure SetDocumentContext(const AContext: IDocumentContext);
  end;
 
implementation
 
constructor TMyDocumentFrame.Create(AOwner: TComponent);
begin
  inherited;
  FEdit := TMemo.Create(Self);
  FEdit.Parent := Self;
  FEdit.Align := alClient;
  FEdit.OnChange := @HandleEditChange;
end;
 
procedure TMyDocumentFrame.SetDocumentContext(const AContext: IDocumentContext);
begin
  FContext := AContext;
  FContext.SetTitle('Neues Dokument');
end;
 
procedure TMyDocumentFrame.HandleEditChange(Sender: TObject);
begin
  if Assigned(FContext) then
    FContext.SetDirty(True);
end;
 
function TMyDocumentFrame.DoCanClose: Boolean;
begin
  if Assigned(FContext) and FContext.GetDirty then
  begin
    case MessageDlg('Speichern?', mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
      mrYes: begin DoSave; Result := True; end;
      mrNo: Result := True;
      else Result := False;
    end;
  end
  else
    Result := True;
end;
 
procedure TMyDocumentFrame.DoSave;
begin
  // Speicherlogik hier
  if Assigned(FContext) then
    FContext.SetDirty(False);
end;
 
procedure TMyDocumentFrame.DoActivate;
begin
  // Kontextwerte setzen, damit Menüs korrekt reagieren
end;
 
end.

Ein durchgehendes Tutorial mit Factory und Registrierung findet sich unter Tutorial: Document-View Add-in.

Weiter zur Inter-Add-in-Kommunikation oder zurück zur SDK-Übersicht.