Tutorial: Sidebar-View Add-in

Dieses Tutorial zeigt, wie ein Add-in ein Panel in der Seitenleiste der Shell bereitstellt. Sidebar-Views eignen sich für navigierende Inhalte: Baumstrukturen, Listen, Filteransichten. Der Host bettet den Frame in die Seitenleiste ein und steuert das Accordion-Verhalten.

Zurück zur Übersicht.

Was gebaut wird

Ein Add-in namens „Asset Navigator“, das:

  • ein Sidebar-Panel im Accordion „Assets“ registriert
  • einen TreeView mit Anlagekategorien anzeigt
  • Kontextmenü-Aktionen für Baumknoten bereitstellt
  • Titelleisten-Buttons für Aktualisieren und Hinzufügen bietet

Manifest

{
  "id": "demo.asset-navigator",
  "name": "asset-navigator",
  "displayName": "Asset Navigator",
  "version": "1.0.0",
  "publisher": "demo",
  "kind": "native",
  "main": "bin/AssetNavigator.dll",
  "engineVersion": "^1.0.0",
 
  "activationEvents": ["onStartup"],
 
  "contributes": {
    "commands": [
      {
        "command": "assets.nav.refresh",
        "title": "Aktualisieren",
        "category": "Assets",
        "icon": "media/refresh.svg"
      },
      {
        "command": "assets.nav.add",
        "title": "Anlage hinzufügen",
        "category": "Assets",
        "icon": "media/add.svg"
      },
      {
        "command": "assets.nav.edit",
        "title": "Bearbeiten",
        "category": "Assets"
      },
      {
        "command": "assets.nav.delete",
        "title": "Löschen",
        "category": "Assets"
      }
    ],
 
    "views": {
      "sidebar": [
        {
          "id": "assets.navigator",
          "name": "Anlagen",
          "icon": "media/navigator.svg",
          "containerId": "assets-explorer"
        },
        {
          "id": "assets.categories",
          "name": "Kategorien",
          "icon": "media/categories.svg",
          "containerId": "assets-explorer"
        }
      ]
    },
 
    "menus": {
      "view/title": [
        {
          "command": "assets.nav.refresh",
          "when": "view == assets.navigator",
          "group": "navigation"
        },
        {
          "command": "assets.nav.add",
          "when": "view == assets.navigator",
          "group": "navigation"
        }
      ],
      "view/item/context": [
        {
          "command": "assets.nav.edit",
          "when": "view == assets.navigator && viewItem == asset",
          "group": "modification",
          "order": 1
        },
        {
          "command": "assets.nav.delete",
          "when": "view == assets.navigator && viewItem == asset",
          "group": "modification",
          "order": 2
        }
      ]
    },
 
    "viewsWelcome": [
      {
        "viewId": "assets.navigator",
        "content": "Noch keine Anlagen vorhanden.\n[Erste Anlage erstellen](command:assets.nav.add)",
        "when": "!hasAssets",
        "group": "main",
        "order": 1
      }
    ]
  }
}

Einige Dinge fallen hier auf:

  • Accordion — Beide Views (assets.navigator und assets.categories) haben dieselbe containerId (assets-explorer). Der Host gruppiert sie zu einem Accordion in der Seitenleiste.
  • Titelleisten-Buttons — Die Menüeinträge unter view/title mit when: view == assets.navigator erscheinen als Buttons in der Titelleiste des Navigator-Panels.
  • Kontextmenü — Die Einträge unter view/item/context mit viewItem == asset erscheinen nur beim Rechtsklick auf einen Baumknoten vom Typ „asset“.
  • Welcome-Inhalt — Wenn keine Anlagen vorhanden sind (!hasAssets), zeigt das Panel einen Begrüßungstext mit einem Link, der den Hinzufügen-Command auslöst.
  • onStartup — Das Add-in wird beim Start aktiviert, weil das Sidebar-Panel sofort sichtbar sein soll. Für Sidebar-Views ist onStartup der übliche Weg.
unit AssetNavigatorFrame;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, Controls, ComCtrls,
  WvdS.Document.Host.Api;
 
type
  TAssetNavigatorFrame = class(TWvdSDocumentFrame)
  private
    FHost: IHost;
    FTree: TTreeView;
    procedure HandleTreeSelectionChanged(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); override;
    procedure Initialize(const AHost: IHost);
    procedure RefreshData;
  end;
 
implementation
 
constructor TAssetNavigatorFrame.Create(AOwner: TComponent);
begin
  inherited;
  FTree := TTreeView.Create(Self);
  FTree.Parent := Self;
  FTree.Align := alClient;
  FTree.OnSelectionChanged := @HandleTreeSelectionChanged;
end;
 
procedure TAssetNavigatorFrame.Initialize(const AHost: IHost);
begin
  FHost := AHost;
  RefreshData;
end;
 
procedure TAssetNavigatorFrame.RefreshData;
var
  RootNode, ChildNode: TTreeNode;
begin
  FTree.Items.Clear;
 
  RootNode := FTree.Items.Add(nil, 'Fahrzeuge');
  FTree.Items.AddChild(RootNode, 'PKW-001');
  FTree.Items.AddChild(RootNode, 'LKW-002');
 
  RootNode := FTree.Items.Add(nil, 'Gebäude');
  FTree.Items.AddChild(RootNode, 'Hauptgebäude');
  FTree.Items.AddChild(RootNode, 'Lager West');
 
  if FTree.Items.Count > 0 then
    FHost.Context.SetContext('hasAssets', 'true')
  else
    FHost.Context.SetContext('hasAssets', '');
end;
 
procedure TAssetNavigatorFrame.HandleTreeSelectionChanged(Sender: TObject);
begin
  if Assigned(FTree.Selected) and (FTree.Selected.Level > 0) then
    // Blattknoten = eine Anlage → Kontextmenü "asset" aktivieren
    FHost.Context.SetContext('viewItem', 'asset')
  else
    // Gruppenknoten → kein Kontextmenü
    FHost.Context.SetContext('viewItem', '');
end;
 
end.

Der Frame setzt Context Keys, die das Kontextmenü steuern. Wenn ein Blattknoten ausgewählt ist, wird viewItem auf asset gesetzt, und die Menüeinträge mit viewItem == asset werden sichtbar. Bei Gruppenknoten verschwindet das Kontextmenü.

Plugin-Klasse

unit AssetNavigatorPlugin;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  WvdS.Document.Host.Api;
 
type
  TAssetNavigatorPlugin = class(TInterfacedObject, IPlugin)
  private
    FHost: IHost;
    FNavigatorFrame: TObject; // Referenz für Commands
  public
    constructor Create(const AHost: IHost);
    procedure Activate(const AContext: IExtensionContext);
    procedure Deactivate;
  end;
 
implementation
 
uses
  AssetNavigatorFrame;
 
type
  TRefreshHandler = class(TInterfacedObject, ICommandHandler)
  private
    FFrame: TAssetNavigatorFrame;
  public
    constructor Create(AFrame: TAssetNavigatorFrame);
    procedure Execute;
  end;
 
{ TAssetNavigatorPlugin }
 
constructor TAssetNavigatorPlugin.Create(const AHost: IHost);
begin
  inherited Create;
  FHost := AHost;
end;
 
procedure TAssetNavigatorPlugin.Activate(const AContext: IExtensionContext);
begin
  // Sidebar-Panel registrieren
  AContext.Subscribe(
    FHost.SideBar.RegisterPanel('assets.navigator', 'Anlagen',
      'media/navigator.svg', TAssetNavigatorFrame)
  );
 
  // Command-Handler registrieren
  AContext.Subscribe(
    FHost.Commands.RegisterCommand('assets.nav.refresh', 'Aktualisieren',
      TRefreshHandler.Create(nil)) // Frame-Referenz wird später gesetzt
  );
 
  AContext.Subscribe(
    FHost.Commands.RegisterCommand('assets.nav.add', 'Anlage hinzufügen',
      TAddHandler.Create(FHost))
  );
 
  AContext.Subscribe(
    FHost.Commands.RegisterCommand('assets.nav.edit', 'Bearbeiten',
      TEditHandler.Create(FHost))
  );
 
  AContext.Subscribe(
    FHost.Commands.RegisterCommand('assets.nav.delete', 'Löschen',
      TDeleteHandler.Create(FHost))
  );
end;
 
procedure TAssetNavigatorPlugin.Deactivate;
begin
end;
 
{ TRefreshHandler }
 
constructor TRefreshHandler.Create(AFrame: TAssetNavigatorFrame);
begin
  inherited Create;
  FFrame := AFrame;
end;
 
procedure TRefreshHandler.Execute;
begin
  if Assigned(FFrame) then
    FFrame.RefreshData;
end;
 
end.

Zusammenspiel

  1. Start — Der Host registriert die Views und Commands aus dem Manifest.
  2. Aktivierung — Wegen onStartup wird die DLL sofort geladen und Activate aufgerufen.
  3. Seitenleiste — Der Host erzeugt das Accordion „assets-explorer“ mit den Panels „Anlagen“ und „Kategorien“.
  4. Titelleiste — Die Buttons „Aktualisieren“ und „Hinzufügen“ erscheinen in der Titelleiste des Anlagen-Panels.
  5. Baumknoten auswählen — Der Frame setzt viewItem, und das Kontextmenü wird kontextabhängig aufgebaut.
  6. Rechtsklick — Bei viewItem == asset zeigt der Host „Bearbeiten“ und „Löschen“.

Weiter zum Menü/Toolbar-Merge Tutorial oder zurück zur Übersicht.

Zuletzt geändert: den 15.03.2026 um 02:33