树状视图很多插件都可能用到,通常作为一个简单的列表使用,比如管理项目依赖、展示搜索结果。那么 Vscode 的 TreeView 具体怎么玩呢?
基础操作
1.创建视图
在 package.json 的 contributes.views
节点下定义视图:
json
{
"name": "custom-view-samples",
...
"contributes": {
"views": {
"my-tree-view": [
{
"id": "myTreeView",
"name": "My Tree View"
}
]
}
},
...
}
创建视图:
less
vscode.window.createTreeView('myTreeView', {});
2.实现树节点
首先实现树节点的数据结构:
typescript
class MyTreeItem extends vscode.TreeItem {
constructor(
public readonly label: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState
) {
super(label, collapsibleState);
this.tooltip = `MyTreeItem - ${this.label}`;
this.iconPath = new vscode.ThemeIcon('file'); this.command = { title: 'Click on me', command: 'myExtension.myTree.onClick', arguments: [ { label } ] }; }
}
vscode.TreeItem
预定义了诸多属性,允许我们控制 TreeItem 的外观与行为:
-
label:用于节点展示文本
-
iconPath:用于设置节点图标,可通过
new vscode.ThemeIcon('file');
引用 Vscode 内置图标 -
description:节点描述,设置之后将展示在 label 内容之后
-
tooltip:节点提示,当鼠标悬停时展示
-
command:用于设置鼠标点击时绑定的操作,这是一个对象:
-
title:名称
-
command:命令 ID
-
arguments:参数数组
-
collapsibleState:控制节点是否能够展开
-
contextValue:用来控制树状视图的特殊行为,与 action(视图绑定的操作)产生联动,在定义 action 时,通过判断节点 contextValue 来实现在指定节点上绑定操作,如:
// package.json "contributes": { "menus": { "view/item/context": [ { "command": "myExtension.myTree.deleteNode", "when": "viewItem == nodeA" // nodeA 为 contextValue } ] }}
3.为视图提供数据
通过实现vscode.TreeDataProvider
为树状视图提供数据,主要实现以下方法:
- getChildren(element?: T): ProviderResult<T[]>;
在加载根节点时,入参为undefined;入参有值时,表示加载该节点的子节点。T 是泛型,需要替换为自己实现的树节点类型。如:
kotlin
getChildren(element?: MyTreeItem): vscode.ProviderResult<MyTreeItem[]> {
if (element) {
return Promise.resolve(this.loadItemsOfRootNode());
} else {
return Promise.resolve(this.loadItemsUnder(element));
}
}
4.注册 TreeDataProvider
通过调用 registerTreeDataProvider 注册视图数据 Provider:
dart
vscode.window.registerTreeDataProvider(
'myTreeDataProvider', // 视图 ID
new MyTreeDataProvider()
);
或者在创建视图时绑定:
php
vscode.window.createTreeView('myTreeView', { treeDataProvider: new MyTreeDataProvider()});
5.刷新视图
要实现视图刷新,我们需要实现 TreeDataProvider 的事件触发器属性:
-
_onDidChangeTreeData
-
onDidChangeTreeData
这两个属性一个 public 可见,一个 private 可见。实现如下:
typescript
private _onDidChangeTreeData: vscode.EventEmitter<Dependency | undefined | null | void> = new vscode.EventEmitter<Dependency | undefined | null | void>();
readonly onDidChangeTreeData: vscode.Event<Dependency | undefined | null | void> = this._onDidChangeTreeData.event;
我们可以通过 _onDidChangeTreeData 触发刷新:
kotlin
this._onDidChangeTreeData.fire();
Vscode 在监听到该事件后会重新渲染视图。其他 module 可以访问 onDidChangeTreeData
属性监听刷新事件:
javascript
myTreeDataProvider.onDidChangeTreeData(() => { console.log('do something') });
进阶
1.实现拖拽
首先实现 vscode.TreeDragAndDropController
,定义如下:
typescript
export interface TreeDragAndDropController<T> { readonly dropMimeTypes: readonly string[]; readonly dragMimeTypes: readonly string[]; handleDrag?(source: readonly T[], dataTransfer: DataTransfer, token: CancellationToken): Thenable<void> | void; handleDrop?(target: T | undefined, dataTransfer: DataTransfer, token: CancellationToken): Thenable<void> | void;}
dropMimeTypes
和 dragMimeTypes
的值是与树状视图的 id 关联的,也就是用来告诉 Vscode 该控制器只处理从 dragMimeTypes
指定的视图 A 拖拽到 dropMimeTypes
指定的视图 B,如:
csharp
dropMimeTypes: readonly string[] = ['application/vnd.code.tree.myExtension_myTreeView'];dragMimeTypes: readonly string[] = ['application/vnd.code.tree.myExtension_myTreeView'];
这里的 'application/vnd.code.tree.myExtension_myTreeView'
对应了 package.json 里定义的:
json
"views": { "automan": [ { "id": "myExtension.myTreeView", "name": "MyTreeView" } ]},
注意命名转换规则,vscode 对此要求比较严格但官方文档解释又很简略:
- 点号转换为下划线
- 点好分割出的每个词必须首字母小写
接下来需要实现 handleDrop
和 handleDrag
的逻辑。handleDrag
为拖拽开始时要做的动作,在这个方法里我们能知道拖拽开始于哪个元素,同时我们需要将数据使用 MIME_TYPE 进行标记、以传递给 handleDrop
方法:
php
handleDrag(source: readonly MyTreeItem[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Thenable<void> | void { dataTransfer.set(MIME_TYPE, new vscode.DataTransferItem(source));}
接着在 handleDrop
中取出数据,实现移动逻辑:
php
handleDrop(target: MyTreeItem | undefined, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Thenable<void> | void { const transferItem = dataTransfer.get(MIME_TYPE); if (!transferItem) { return; }
...
}
将控制器绑定到视图上:
php
vscode.window.createTreeView('myTreeView', { treeDataProvider: new MyTreeDataProvider(), showCollapseAll: true, canSelectMany: true, dragAndDropController: new MyTreeDragAndDropController()});
在创建视图的同时设置 canSelectMany,以允许同时拖拽多个项目。