Files
LanMountainDesktop/docs/01-插件开发/01-快速开始/02-创建第一个插件.md
2026-06-08 12:18:58 +08:00

531 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 创建第一个插件
通过这个教程,你将在 15 分钟内创建一个简单但功能完整的插件。
## 学习目标
- ✅ 使用模板创建插件项目
- ✅ 实现插件入口类
- ✅ 创建一个简单的桌面组件
- ✅ 注册组件到宿主
- ✅ 运行和测试插件
## 前置准备
确保你已经:
- ✅ 安装了 .NET 10 SDK
- ✅ 安装了插件模板(参考 [环境准备](01-环境准备.md)
- ✅ 有一个支持 C# 的 IDE
## 步骤 1: 创建项目
### 使用模板创建
```powershell
# 创建新插件项目
dotnet new lmd-plugin -n HelloWorldPlugin
# 进入项目目录
cd HelloWorldPlugin
# 还原依赖
dotnet restore
```
### 项目结构预览
```
HelloWorldPlugin/
├── HelloWorldPlugin.csproj # 项目文件
├── Plugin.cs # 插件入口(我们要修改这个)
├── plugin.json # 插件清单(我们要修改这个)
├── Components/
│ └── SampleComponent.cs # 示例组件(我们要修改这个)
├── Views/
│ └── SampleComponentView.axaml # 组件视图(我们要修改这个)
├── ViewModels/
│ └── SampleComponentViewModel.cs
├── Assets/
│ └── icon.png # 插件图标
└── Settings/
└── PluginSettingsPage.axaml # 设置页
```
## 步骤 2: 配置插件清单
编辑 `plugin.json`,修改基本信息:
```json
{
"Id": "com.example.helloworldplugin",
"Name": "Hello World Plugin",
"Version": "1.0.0",
"Author": "Your Name",
"Description": "My first LanMountainDesktop plugin - displays a greeting",
"MinHostVersion": "1.0.0",
"SdkVersion": "5.0.0",
"Dependencies": [],
"Permissions": [],
"Icon": "Assets/icon.png",
"Homepage": "https://github.com/yourusername/helloworldplugin"
}
```
### 字段说明
- **Id**: 插件唯一标识符,建议使用反向域名格式
- **Name**: 用户看到的插件名称
- **Version**: 插件版本号(语义化版本)
- **MinHostVersion**: 最低支持的宿主版本
- **SdkVersion**: 使用的 SDK 版本
## 步骤 3: 实现插件入口
编辑 `Plugin.cs`
```csharp
using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Shared.Contracts;
using HelloWorldPlugin.Components;
using HelloWorldPlugin.Views;
using HelloWorldPlugin.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace HelloWorldPlugin;
/// <summary>
/// Hello World 插件入口
/// </summary>
public class Plugin : IPlugin
{
public string Id => "com.example.helloworldplugin";
public string Name => "Hello World Plugin";
public string Version => "1.0.0";
private IPluginContext? _context;
/// <summary>
/// 插件初始化
/// </summary>
public async Task InitializeAsync(IPluginContext context)
{
_context = context;
// 记录日志
context.Logger.LogInformation("Hello World Plugin is initializing...");
// 获取组件注册表
var componentRegistry = context.Services
.GetService<IComponentRegistry>();
if (componentRegistry != null)
{
// 注册 Hello World 组件
componentRegistry.RegisterComponent<HelloWorldComponent>(
componentFactory: () => new HelloWorldComponent(),
viewFactory: (component) => new HelloWorldComponentView
{
DataContext = new HelloWorldComponentViewModel(
(HelloWorldComponent)component
)
}
);
context.Logger.LogInformation(
"HelloWorldComponent registered successfully"
);
}
// 异步操作示例(如果需要)
await Task.CompletedTask;
}
/// <summary>
/// 插件关闭
/// </summary>
public async Task ShutdownAsync()
{
_context?.Logger.LogInformation(
"Hello World Plugin is shutting down..."
);
// 清理资源(如果有)
await Task.CompletedTask;
}
}
```
### 代码说明
1. **实现 IPlugin 接口** - 定义插件的基本信息和生命周期
2. **InitializeAsync** - 插件加载时调用,注册组件和服务
3. **ShutdownAsync** - 插件卸载时调用,清理资源
4. **日志记录** - 使用 `context.Logger` 记录日志
## 步骤 4: 创建组件类
编辑 `Components/SampleComponent.cs`,重命名为 `HelloWorldComponent.cs`
```csharp
using LanMountainDesktop.PluginSdk.Components;
using LanMountainDesktop.Shared.Contracts.Components;
using System.ComponentModel;
namespace HelloWorldPlugin.Components;
/// <summary>
/// Hello World 桌面组件
/// </summary>
[Component(
Id = "com.example.helloworldplugin.helloworld",
Name = "Hello World",
Description = "Displays a friendly greeting message",
Category = "Demo",
Icon = "avares://HelloWorldPlugin/Assets/icon.png"
)]
public class HelloWorldComponent : ComponentBase
{
public override string Id => "com.example.helloworldplugin.helloworld";
public override string Name => "Hello World";
private string _greeting = "Hello, World!";
private int _clickCount = 0;
/// <summary>
/// 问候语
/// </summary>
public string Greeting
{
get => _greeting;
set => SetProperty(ref _greeting, value);
}
/// <summary>
/// 点击次数
/// </summary>
public int ClickCount
{
get => _clickCount;
set => SetProperty(ref _clickCount, value);
}
/// <summary>
/// 组件初始化
/// </summary>
public override Task InitializeAsync()
{
// 从设置加载问候语
Greeting = Settings.GetValue("Greeting", "Hello, World!");
ClickCount = Settings.GetValue("ClickCount", 0);
Logger.LogInformation("HelloWorldComponent initialized");
return Task.CompletedTask;
}
/// <summary>
/// 组件更新(定时调用)
/// </summary>
public override Task UpdateAsync()
{
// 这里可以更新组件数据
// 例如:从 API 获取数据、更新时间等
return Task.CompletedTask;
}
/// <summary>
/// 增加点击次数
/// </summary>
public void IncrementClickCount()
{
ClickCount++;
Settings.SetValue("ClickCount", ClickCount);
}
}
```
### 组件说明
- **ComponentBase** - 所有组件的基类,提供基础功能
- **属性通知** - 使用 `SetProperty` 自动触发 UI 更新
- **设置持久化** - 使用 `Settings` 保存和读取配置
- **InitializeAsync** - 组件创建时调用
- **UpdateAsync** - 定时调用(默认 1 秒),用于更新数据
## 步骤 5: 创建组件视图
编辑 `Views/SampleComponentView.axaml`,重命名为 `HelloWorldComponentView.axaml`
```xml
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:HelloWorldPlugin.ViewModels"
x:Class="HelloWorldPlugin.Views.HelloWorldComponentView"
x:DataType="vm:HelloWorldComponentViewModel">
<!-- 组件容器 -->
<Border Background="{DynamicResource CardBackgroundBrush}"
CornerRadius="{DynamicResource DesignCornerRadiusComponent}"
Padding="20"
BorderBrush="{DynamicResource CardBorderBrush}"
BorderThickness="1">
<StackPanel Spacing="12">
<!-- 标题 -->
<TextBlock Text="{Binding Component.Name}"
FontSize="18"
FontWeight="Bold"
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
<!-- 问候语 -->
<TextBlock Text="{Binding Component.Greeting}"
FontSize="16"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap" />
<!-- 点击次数 -->
<TextBlock Foreground="{DynamicResource TextFillColorTertiaryBrush}">
<Run Text="Clicks: " />
<Run Text="{Binding Component.ClickCount}" FontWeight="Bold" />
</TextBlock>
<!-- 按钮 -->
<Button Content="Click Me!"
Command="{Binding ClickCommand}"
HorizontalAlignment="Stretch"
Padding="12,8" />
</StackPanel>
</Border>
</UserControl>
```
编辑对应的代码后台 `HelloWorldComponentView.axaml.cs`
```csharp
using Avalonia.Controls;
namespace HelloWorldPlugin.Views;
public partial class HelloWorldComponentView : UserControl
{
public HelloWorldComponentView()
{
InitializeComponent();
}
}
```
### 视图说明
- **动态资源** - 使用 `{DynamicResource}` 适配主题
- **圆角规范** - 使用 `DesignCornerRadiusComponent` 保持一致性
- **数据绑定** - 使用 `{Binding}` 绑定到 ViewModel
- **响应式布局** - 使用 `StackPanel` 自动布局
## 步骤 6: 创建视图模型
编辑 `ViewModels/SampleComponentViewModel.cs`,重命名为 `HelloWorldComponentViewModel.cs`
```csharp
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HelloWorldPlugin.Components;
namespace HelloWorldPlugin.ViewModels;
/// <summary>
/// Hello World 组件视图模型
/// </summary>
public partial class HelloWorldComponentViewModel : ObservableObject
{
[ObservableProperty]
private HelloWorldComponent _component;
public HelloWorldComponentViewModel(HelloWorldComponent component)
{
_component = component;
}
/// <summary>
/// 点击按钮命令
/// </summary>
[RelayCommand]
private void Click()
{
Component.IncrementClickCount();
// 更新问候语
var greetings = new[]
{
"Hello, World!",
"你好,世界!",
"Bonjour le monde!",
"Hola, Mundo!",
"Привет, мир!",
"こんにちは、世界!"
};
var index = Component.ClickCount % greetings.Length;
Component.Greeting = greetings[index];
// 保存问候语
Component.Settings.SetValue("Greeting", Component.Greeting);
}
}
```
### ViewModel 说明
- **ObservableObject** - MVVM Toolkit 基类,提供属性通知
- **[ObservableProperty]** - 自动生成属性和通知代码
- **[RelayCommand]** - 自动生成命令
- **业务逻辑** - 处理用户交互和数据更新
## 步骤 7: 构建插件
```powershell
# 构建项目
dotnet build -c Release
# 查看输出
dir bin\Release\net10.0\
```
输出目录应包含:
- `HelloWorldPlugin.dll` - 主程序集
- `plugin.json` - 插件清单
- `Assets/` - 资源文件
- 依赖的 DLL 文件
## 步骤 8: 安装和测试
### 方法 1: 复制到插件目录
```powershell
# 创建插件目录
$pluginDir = "$env:LOCALAPPDATA\LanMountainDesktop\plugins\HelloWorldPlugin"
New-Item -ItemType Directory -Path $pluginDir -Force
# 复制文件
Copy-Item -Path "bin\Release\net10.0\*" -Destination $pluginDir -Recurse -Force
# 启动宿主应用
# (从开始菜单或安装目录启动)
```
### 方法 2: 使用符号链接(开发模式)
```powershell
# 需要管理员权限
$pluginDir = "$env:LOCALAPPDATA\LanMountainDesktop\plugins\HelloWorldPlugin"
$buildDir = "$(pwd)\bin\Release\net10.0"
New-Item -ItemType SymbolicLink -Path $pluginDir -Target $buildDir -Force
```
### 验证插件加载
1. 启动阑山桌面
2. 查看日志文件:
```powershell
Get-Content "$env:LOCALAPPDATA\LanMountainDesktop\logs\latest.log" -Tail 50
```
3. 应该看到类似输出:
```
[INFO] Hello World Plugin is initializing...
[INFO] HelloWorldComponent registered successfully
```
### 添加组件到桌面
1. 右键点击桌面空白处
2. 选择"添加组件"
3. 找到"Hello World"组件
4. 点击添加
5. 组件应该出现在桌面上
### 测试功能
1. 点击"Click Me!"按钮
2. 观察点击次数增加
3. 观察问候语在不同语言间切换
4. 重启应用,验证设置持久化
## 步骤 9: 调试插件
### 附加调试器
1. 启动阑山桌面应用
2. 在 Visual Studio 中:
- 选择"调试" → "附加到进程"
- 找到 `LanMountainDesktop.exe`
- 点击"附加"
3. 在插件代码中设置断点
4. 触发相应功能(如点击按钮)
### 查看日志
```powershell
# 实时查看日志
Get-Content "$env:LOCALAPPDATA\LanMountainDesktop\logs\latest.log" -Wait -Tail 50
```
### 常见问题
#### 插件没有加载
**检查清单**:
1. `plugin.json` 存在且格式正确
2. 插件 DLL 文件存在
3. 查看日志中的错误信息
4. 确认插件 ID 唯一
#### 组件没有显示
**检查清单**:
1. 组件已正确注册
2. 组件 ID 唯一
3. 视图文件正确编译为 AvaloniaResource
4. DataContext 正确设置
#### 按钮点击没有响应
**检查清单**:
1. Command 绑定正确
2. ViewModel 方法存在
3. 查看日志中的异常信息
## 下一步
恭喜!你已经创建了第一个插件。接下来可以:
- [插件生命周期](../02-核心概念/01-插件生命周期.md) - 深入理解插件运行机制
- [组件系统详解](../02-核心概念/02-组件系统.md) - 学习更多组件功能
- [设置系统](../02-核心概念/03-设置系统.md) - 添加设置页面
- [实战案例](../04-实战案例/01-天气组件.md) - 学习完整的实战案例
## 完整代码仓库
本教程的完整代码可以在以下位置找到:
- GitHub: https://github.com/HelloWRC/LanMountainDesktop.SamplePlugin
## 小结
在这个教程中,你学会了:
- ✅ 使用模板创建插件项目
- ✅ 配置插件清单
- ✅ 实现插件入口类
- ✅ 创建桌面组件(模型、视图、视图模型)
- ✅ 注册组件到宿主
- ✅ 使用设置系统持久化数据
- ✅ 构建、安装和测试插件
- ✅ 调试插件代码
这是一个完整但简单的插件,展示了插件开发的基本流程。在实际项目中,你可以添加更多功能,如网络请求、定时任务、复杂 UI 等。