Inhaltsverzeichnis
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.navigatorundassets.categories) haben dieselbecontainerId(assets-explorer). Der Host gruppiert sie zu einem Accordion in der Seitenleiste. - Titelleisten-Buttons — Die Menüeinträge unter
view/titlemitwhen:view == assets.navigatorerscheinen als Buttons in der Titelleiste des Navigator-Panels. - Kontextmenü — Die Einträge unter
view/item/contextmitviewItem == asseterscheinen 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
onStartupder übliche Weg.
Navigator Frame
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
- Start — Der Host registriert die Views und Commands aus dem Manifest.
- Aktivierung — Wegen
onStartupwird die DLL sofort geladen undActivateaufgerufen. - Seitenleiste — Der Host erzeugt das Accordion „assets-explorer“ mit den Panels „Anlagen“ und „Kategorien“.
- Titelleiste — Die Buttons „Aktualisieren“ und „Hinzufügen“ erscheinen in der Titelleiste des Anlagen-Panels.
- Baumknoten auswählen — Der Frame setzt
viewItem, und das Kontextmenü wird kontextabhängig aufgebaut. - Rechtsklick — Bei
viewItem == assetzeigt 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