add:插件系统

This commit is contained in:
2026-04-05 14:06:49 +08:00
parent 069a478559
commit 1fca17d557
31 changed files with 3361 additions and 1438 deletions
+104
View File
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using InkCanvasForClass.PluginSdk;
namespace InkCanvasForClass.PluginHost
{
/// <summary>
/// 收集插件登记的菜单 / 工具栏 / 设置页,供宿主窗口在启动后统一挂载。
/// </summary>
public sealed class CollectingPluginRegistry : IPluginRegistry
{
private string _currentPluginId = "";
public string CurrentPluginId => _currentPluginId;
public ObservableCollection<MenuItemRegistration> MenuItems { get; } =
new ObservableCollection<MenuItemRegistration>();
public ObservableCollection<ToolbarButtonRegistration> ToolbarButtons { get; } =
new ObservableCollection<ToolbarButtonRegistration>();
public ObservableCollection<SettingsPageRegistration> SettingsPages { get; } =
new ObservableCollection<SettingsPageRegistration>();
public void SetCurrentPluginId(string pluginId)
{
_currentPluginId = pluginId ?? "";
}
public void RegisterMenuItem(string groupKey, MenuItem item)
{
if (item == null) return;
MenuItems.Add(new MenuItemRegistration(_currentPluginId, groupKey ?? "", item));
}
public void RegisterToolbarButton(Button button)
{
if (button == null) return;
ToolbarButtons.Add(new ToolbarButtonRegistration(_currentPluginId, button));
}
public void RegisterSettingsPage(string pageId, string displayName, Func<UserControl> createView)
{
if (string.IsNullOrWhiteSpace(pageId) || createView == null) return;
SettingsPages.Add(new SettingsPageRegistration(
_currentPluginId,
pageId,
displayName ?? pageId,
createView));
}
public void Clear()
{
MenuItems.Clear();
ToolbarButtons.Clear();
SettingsPages.Clear();
_currentPluginId = "";
}
}
public sealed class MenuItemRegistration
{
public MenuItemRegistration(string pluginId, string groupKey, MenuItem item)
{
PluginId = pluginId ?? "";
GroupKey = groupKey ?? "";
Item = item ?? throw new ArgumentNullException(nameof(item));
}
public string PluginId { get; }
public string GroupKey { get; }
public MenuItem Item { get; }
}
public sealed class ToolbarButtonRegistration
{
public ToolbarButtonRegistration(string pluginId, Button button)
{
PluginId = pluginId ?? "";
Button = button ?? throw new ArgumentNullException(nameof(button));
}
public string PluginId { get; }
public Button Button { get; }
}
public sealed class SettingsPageRegistration
{
public SettingsPageRegistration(string pluginId, string pageId, string displayName, Func<UserControl> createView)
{
PluginId = pluginId ?? "";
PageId = pageId ?? "";
DisplayName = displayName ?? "";
CreateView = createView ?? throw new ArgumentNullException(nameof(createView));
}
public string PluginId { get; }
public string PageId { get; }
public string DisplayName { get; }
public Func<UserControl> CreateView { get; }
}
}
+16
View File
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<UseWPF>true</UseWPF>
<RootNamespace>InkCanvasForClass.PluginHost</RootNamespace>
<AssemblyName>InkCanvas.PluginHost</AssemblyName>
<LangVersion>7.3</LangVersion>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SDK\InkCanvas.PluginSdk.csproj" />
</ItemGroup>
</Project>
+103
View File
@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Media;
namespace InkCanvasForClass.PluginSdk
{
/// <summary>
/// Ink Canvas 插件接口
/// </summary>
public interface IInkCanvasPlugin
{
/// <summary>
/// 插件唯一标识符
/// </summary>
string Id { get; }
/// <summary>
/// 插件名称
/// </summary>
string Name { get; }
/// <summary>
/// 插件描述
/// </summary>
string Description { get; }
/// <summary>
/// 插件版本
/// </summary>
Version Version { get; }
/// <summary>
/// 插件作者
/// </summary>
string Author { get; }
/// <summary>
/// 插件主页URL
/// </summary>
string Homepage { get; }
/// <summary>
/// 插件图标
/// </summary>
ImageSource Icon { get; }
/// <summary>
/// 插件初始化
/// </summary>
/// <param name="context">插件上下文</param>
void Initialize(IPluginContext context);
/// <summary>
/// 插件启动
/// </summary>
void Start();
/// <summary>
/// 插件停止
/// </summary>
void Stop();
/// <summary>
/// 插件清理
/// </summary>
void Cleanup();
/// <summary>
/// 获取插件设置界面
/// </summary>
/// <returns>设置界面控件</returns>
UserControl GetSettingsView();
/// <summary>
/// 获取插件菜单项
/// </summary>
/// <returns>菜单项列表</returns>
IEnumerable<MenuItem> GetMenuItems();
/// <summary>
/// 获取插件工具栏按钮
/// </summary>
/// <returns>工具栏按钮列表</returns>
IEnumerable<Button> GetToolbarButtons();
/// <summary>
/// 获取插件状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
string GetStatusBarInfo();
/// <summary>
/// 插件是否已启用
/// </summary>
bool IsEnabled { get; set; }
/// <summary>
/// 插件启用状态变更事件
/// </summary>
event EventHandler<bool> EnabledChanged;
}
}
+569
View File
@@ -0,0 +1,569 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Ink;
namespace InkCanvasForClass.PluginSdk
{
/// <summary>
/// 插件上下文接口,提供对主应用程序功能的访问
/// </summary>
public interface IPluginContext
{
/// <summary>
/// 主窗口实例
/// </summary>
Window MainWindow { get; }
/// <summary>
/// 当前画布
/// </summary>
System.Windows.Controls.InkCanvas CurrentCanvas { get; }
/// <summary>
/// 所有画布页面
/// </summary>
IList<System.Windows.Controls.Canvas> AllCanvasPages { get; }
/// <summary>
/// 当前页面索引
/// </summary>
int CurrentPageIndex { get; }
/// <summary>
/// 总页面数
/// </summary>
int TotalPageCount { get; }
/// <summary>
/// 浮动工具栏
/// </summary>
FrameworkElement FloatingToolBar { get; }
/// <summary>
/// 左侧面板
/// </summary>
FrameworkElement LeftPanel { get; }
/// <summary>
/// 右侧面板
/// </summary>
FrameworkElement RightPanel { get; }
/// <summary>
/// 顶部面板
/// </summary>
FrameworkElement TopPanel { get; }
/// <summary>
/// 底部面板
/// </summary>
FrameworkElement BottomPanel { get; }
/// <summary>
/// 当前绘制模式
/// </summary>
int CurrentDrawingMode { get; }
/// <summary>
/// 当前墨迹宽度
/// </summary>
double CurrentInkWidth { get; }
/// <summary>
/// 当前墨迹颜色
/// </summary>
Color CurrentInkColor { get; }
/// <summary>
/// 当前高亮笔宽度
/// </summary>
double CurrentHighlighterWidth { get; }
/// <summary>
/// 当前橡皮擦大小
/// </summary>
int CurrentEraserSize { get; }
/// <summary>
/// 当前橡皮擦类型
/// </summary>
int CurrentEraserType { get; }
/// <summary>
/// 当前橡皮擦形状
/// </summary>
int CurrentEraserShape { get; }
/// <summary>
/// 当前墨迹透明度
/// </summary>
double CurrentInkAlpha { get; }
/// <summary>
/// 当前墨迹样式
/// </summary>
int CurrentInkStyle { get; }
/// <summary>
/// 当前背景颜色
/// </summary>
string CurrentBackgroundColor { get; }
/// <summary>
/// 是否为深色主题
/// </summary>
bool IsDarkTheme { get; }
/// <summary>
/// 是否为白板模式
/// </summary>
bool IsWhiteboardMode { get; }
/// <summary>
/// 是否为PPT模式
/// </summary>
bool IsPPTMode { get; }
/// <summary>
/// 是否为全屏模式
/// </summary>
bool IsFullScreenMode { get; }
/// <summary>
/// 是否为画布模式
/// </summary>
bool IsCanvasMode { get; }
/// <summary>
/// 是否为选择模式
/// </summary>
bool IsSelectionMode { get; }
/// <summary>
/// 是否为橡皮擦模式
/// </summary>
bool IsEraserMode { get; }
/// <summary>
/// 是否为形状绘制模式
/// </summary>
bool IsShapeDrawingMode { get; }
/// <summary>
/// 是否为高亮笔模式
/// </summary>
bool IsHighlighterMode { get; }
/// <summary>
/// 是否可以撤销
/// </summary>
bool CanUndo { get; }
/// <summary>
/// 是否可以重做
/// </summary>
bool CanRedo { get; }
/// <summary>
/// 获取设置值
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>设置值</returns>
T GetSetting<T>(string key, T defaultValue = default(T));
/// <summary>
/// 设置设置值
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="value">设置值</param>
void SetSetting<T>(string key, T value);
/// <summary>
/// 保存设置
/// </summary>
void SaveSettings();
/// <summary>
/// 加载设置
/// </summary>
void LoadSettings();
/// <summary>
/// 重置设置
/// </summary>
void ResetSettings();
/// <summary>
/// 获取所有插件
/// </summary>
/// <returns>插件列表</returns>
IList<IInkCanvasPlugin> GetAllPlugins();
/// <summary>
/// 根据名称获取插件
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <returns>插件实例</returns>
IInkCanvasPlugin GetPlugin(string pluginName);
/// <summary>
/// 启用插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void EnablePlugin(string pluginName);
/// <summary>
/// 禁用插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void DisablePlugin(string pluginName);
/// <summary>
/// 卸载插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void UnloadPlugin(string pluginName);
/// <summary>
/// 显示设置窗口
/// </summary>
void ShowSettingsWindow();
/// <summary>
/// 隐藏设置窗口
/// </summary>
void HideSettingsWindow();
/// <summary>
/// 显示插件设置窗口
/// </summary>
void ShowPluginSettingsWindow();
/// <summary>
/// 隐藏插件设置窗口
/// </summary>
void HidePluginSettingsWindow();
/// <summary>
/// 显示帮助窗口
/// </summary>
void ShowHelpWindow();
/// <summary>
/// 隐藏帮助窗口
/// </summary>
void HideHelpWindow();
/// <summary>
/// 显示关于窗口
/// </summary>
void ShowAboutWindow();
/// <summary>
/// 隐藏关于窗口
/// </summary>
void HideAboutWindow();
/// <summary>
/// 显示通知
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">通知类型</param>
void ShowNotification(string message, NotificationType type = NotificationType.Info);
/// <summary>
/// 显示确认对话框
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
/// <returns>用户选择结果</returns>
bool ShowConfirmDialog(string message, string title = "确认");
/// <summary>
/// 显示输入对话框
/// </summary>
/// <param name="message">提示消息</param>
/// <param name="title">标题</param>
/// <param name="defaultValue">默认值</param>
/// <returns>用户输入内容</returns>
string ShowInputDialog(string message, string title = "输入", string defaultValue = "");
/// <summary>
/// 设置全屏模式
/// </summary>
/// <param name="isFullScreen">是否全屏</param>
void SetFullScreen(bool isFullScreen);
/// <summary>
/// 设置置顶模式
/// </summary>
/// <param name="isTopMost">是否置顶</param>
void SetTopMost(bool isTopMost);
/// <summary>
/// 设置窗口可见性
/// </summary>
/// <param name="isVisible">是否可见</param>
void SetWindowVisibility(bool isVisible);
/// <summary>
/// 最小化窗口
/// </summary>
void MinimizeWindow();
/// <summary>
/// 最大化窗口
/// </summary>
void MaximizeWindow();
/// <summary>
/// 还原窗口
/// </summary>
void RestoreWindow();
/// <summary>
/// 关闭窗口
/// </summary>
void CloseWindow();
/// <summary>
/// 设置窗口位置
/// </summary>
/// <param name="x">X坐标</param>
/// <param name="y">Y坐标</param>
void SetWindowPosition(double x, double y);
/// <summary>
/// 设置窗口大小
/// </summary>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
void SetWindowSize(double width, double height);
/// <summary>
/// 获取窗口位置
/// </summary>
/// <returns>窗口位置</returns>
(double x, double y) GetWindowPosition();
/// <summary>
/// 获取窗口大小
/// </summary>
/// <returns>窗口大小</returns>
(double width, double height) GetWindowSize();
/// <summary>
/// 清除当前画布
/// </summary>
void ClearCanvas();
/// <summary>
/// 清除所有画布
/// </summary>
void ClearAllCanvases();
/// <summary>
/// 添加新页面
/// </summary>
void AddNewPage();
/// <summary>
/// 删除当前页面
/// </summary>
void DeleteCurrentPage();
/// <summary>
/// 切换到指定页面
/// </summary>
/// <param name="pageIndex">页面索引</param>
void SwitchToPage(int pageIndex);
/// <summary>
/// 下一页
/// </summary>
void NextPage();
/// <summary>
/// 上一页
/// </summary>
void PreviousPage();
/// <summary>
/// 设置绘制模式
/// </summary>
/// <param name="mode">绘制模式</param>
void SetDrawingMode(int mode);
/// <summary>
/// 设置墨迹宽度
/// </summary>
/// <param name="width">宽度</param>
void SetInkWidth(double width);
/// <summary>
/// 设置墨迹颜色
/// </summary>
/// <param name="color">颜色</param>
void SetInkColor(Color color);
/// <summary>
/// 设置高亮笔宽度
/// </summary>
/// <param name="width">宽度</param>
void SetHighlighterWidth(double width);
/// <summary>
/// 设置橡皮擦大小
/// </summary>
/// <param name="size">大小</param>
void SetEraserSize(int size);
/// <summary>
/// 设置橡皮擦类型
/// </summary>
/// <param name="type">类型</param>
void SetEraserType(int type);
/// <summary>
/// 设置橡皮擦形状
/// </summary>
/// <param name="shape">形状</param>
void SetEraserShape(int shape);
/// <summary>
/// 设置墨迹透明度
/// </summary>
/// <param name="alpha">透明度</param>
void SetInkAlpha(double alpha);
/// <summary>
/// 设置墨迹样式
/// </summary>
/// <param name="style">样式</param>
void SetInkStyle(int style);
/// <summary>
/// 设置背景颜色
/// </summary>
/// <param name="color">颜色</param>
void SetBackgroundColor(string color);
/// <summary>
/// 保存画布
/// </summary>
/// <param name="filePath">文件路径</param>
void SaveCanvas(string filePath);
/// <summary>
/// 加载画布
/// </summary>
/// <param name="filePath">文件路径</param>
void LoadCanvas(string filePath);
/// <summary>
/// 导出为图片
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="format">格式</param>
void ExportAsImage(string filePath, string format);
/// <summary>
/// 导出为PDF
/// </summary>
/// <param name="filePath">文件路径</param>
void ExportAsPDF(string filePath);
/// <summary>
/// 撤销操作
/// </summary>
void Undo();
/// <summary>
/// 重做操作
/// </summary>
void Redo();
/// <summary>
/// 全选
/// </summary>
void SelectAll();
/// <summary>
/// 取消选择
/// </summary>
void DeselectAll();
/// <summary>
/// 删除选中项
/// </summary>
void DeleteSelected();
/// <summary>
/// 复制选中项
/// </summary>
void CopySelected();
/// <summary>
/// 剪切选中项
/// </summary>
void CutSelected();
/// <summary>
/// 粘贴
/// </summary>
void Paste();
/// <summary>
/// 注册事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
void RegisterEventHandler(string eventName, EventHandler handler);
/// <summary>
/// 注销事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
void UnregisterEventHandler(string eventName, EventHandler handler);
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="sender">事件发送者</param>
/// <param name="args">事件参数</param>
void TriggerEvent(string eventName, object sender, EventArgs args);
/// <summary>
/// 重启应用程序
/// </summary>
void RestartApplication();
/// <summary>
/// 退出应用程序
/// </summary>
void ExitApplication();
/// <summary>
/// 检查更新
/// </summary>
void CheckForUpdates();
/// <summary>
/// 打开帮助文档
/// </summary>
void OpenHelpDocument();
/// <summary>
/// 打开关于页面
/// </summary>
void OpenAboutPage();
}
}
+32
View File
@@ -0,0 +1,32 @@
using System;
using System.Windows.Controls;
namespace InkCanvasForClass.PluginSdk
{
/// <summary>
/// 方案 B:不依赖 Microsoft.Extensions.DependencyInjection 的轻量扩展注册表。
/// 宿主在适当时机将登记项挂到菜单 / 工具栏 / 设置 UI。
/// </summary>
public interface IPluginRegistry
{
/// <summary>
/// 当前正在执行注册的插件目录 Id(由宿主在加载每个插件前设置)。
/// </summary>
string CurrentPluginId { get; }
/// <summary>
/// 注册主菜单或上下文菜单中的项;<paramref name="groupKey"/> 由宿主解释(如 "Main.Plugins")。
/// </summary>
void RegisterMenuItem(string groupKey, MenuItem item);
/// <summary>
/// 注册工具栏按钮。
/// </summary>
void RegisterToolbarButton(Button button);
/// <summary>
/// 注册设置页;<paramref name="createView"/> 在打开设置时惰性创建。
/// </summary>
void RegisterSettingsPage(string pageId, string displayName, Func<UserControl> createView);
}
}
+35
View File
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<RootNamespace>InkCanvasForClass.PluginSdk</RootNamespace>
<UseWPF>true</UseWPF>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>InkCanvas.PluginSdk</PackageId>
<PackageVersion>1.0.0</PackageVersion>
<Authors>Ink Canvas Team</Authors>
<Company>Ink Canvas</Company>
<Product>Ink Canvas Plugin SDK</Product>
<Description>SDK for developing Ink Canvas plugins - 墨迹画布插件开发SDK</Description>
<PackageProjectUrl>https://github.com/your-org/ink-canvas</PackageProjectUrl>
<RepositoryUrl>https://github.com/your-org/ink-canvas</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageTags>ink;canvas;plugin;sdk;wpf;drawing</PackageTags>
<Copyright>Copyright © 2025</Copyright>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<LangVersion>7.3</LangVersion>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="README.md" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>
</Project>
+268
View File
@@ -0,0 +1,268 @@
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Media;
namespace InkCanvasForClass.PluginSdk
{
/// <summary>
/// Ink Canvas 插件基类
/// 提供插件的基本实现
/// </summary>
public abstract class InkCanvasPluginBase : IInkCanvasPlugin
{
private bool _isEnabled;
private IPluginContext _context;
/// <summary>
/// 插件唯一标识符
/// </summary>
public abstract string Id { get; }
/// <summary>
/// 插件名称
/// </summary>
public abstract string Name { get; }
/// <summary>
/// 插件描述
/// </summary>
public abstract string Description { get; }
/// <summary>
/// 插件版本
/// </summary>
public abstract Version Version { get; }
/// <summary>
/// 插件作者
/// </summary>
public abstract string Author { get; }
/// <summary>
/// 插件主页URL
/// </summary>
public virtual string Homepage => string.Empty;
/// <summary>
/// 插件图标
/// </summary>
public virtual ImageSource Icon => null;
/// <summary>
/// 插件是否已启用
/// </summary>
public virtual bool IsEnabled
{
get => _isEnabled;
set
{
if (_isEnabled != value)
{
_isEnabled = value;
OnEnabledChanged(value);
EnabledChanged?.Invoke(this, value);
}
}
}
/// <summary>
/// 插件启用状态变更事件
/// </summary>
public event EventHandler<bool> EnabledChanged;
/// <summary>
/// 插件上下文
/// </summary>
protected IPluginContext Context => _context;
/// <summary>
/// 插件初始化
/// </summary>
/// <param name="context">插件上下文</param>
public virtual void Initialize(IPluginContext context)
{
_context = context;
}
/// <summary>
/// 方案 B:在 <see cref="Initialize"/> 之后由宿主调用,用于向 <see cref="IPluginRegistry"/> 登记菜单、工具栏、设置页等。
/// </summary>
/// <param name="registry">宿主提供的注册表</param>
public virtual void RegisterExtensions(IPluginRegistry registry)
{
}
/// <summary>
/// 插件启动
/// </summary>
public virtual void Start()
{
// 默认实现为空
}
/// <summary>
/// 插件停止
/// </summary>
public virtual void Stop()
{
// 默认实现为空
}
/// <summary>
/// 插件清理
/// </summary>
public virtual void Cleanup()
{
// 默认实现为空
}
/// <summary>
/// 获取插件设置界面
/// </summary>
/// <returns>设置界面控件</returns>
public virtual UserControl GetSettingsView()
{
return new UserControl();
}
/// <summary>
/// 获取插件菜单项
/// </summary>
/// <returns>菜单项列表</returns>
public virtual IEnumerable<MenuItem> GetMenuItems()
{
return new List<MenuItem>();
}
/// <summary>
/// 获取插件工具栏按钮
/// </summary>
/// <returns>工具栏按钮列表</returns>
public virtual IEnumerable<Button> GetToolbarButtons()
{
return new List<Button>();
}
/// <summary>
/// 获取插件状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
public virtual string GetStatusBarInfo()
{
return $"{Name} v{Version} - {(IsEnabled ? "" : "")}";
}
/// <summary>
/// 启用状态变更时的处理
/// </summary>
/// <param name="isEnabled">是否启用</param>
protected virtual void OnEnabledChanged(bool isEnabled)
{
if (isEnabled)
{
Start();
}
else
{
Stop();
}
}
/// <summary>
/// 显示通知
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">通知类型</param>
protected void ShowNotification(string message, NotificationType type = NotificationType.Info)
{
_context?.ShowNotification(message, type);
}
/// <summary>
/// 显示确认对话框
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
/// <returns>用户选择结果</returns>
protected bool ShowConfirmDialog(string message, string title = "确认")
{
return _context?.ShowConfirmDialog(message, title) ?? false;
}
/// <summary>
/// 显示输入对话框
/// </summary>
/// <param name="message">提示消息</param>
/// <param name="title">标题</param>
/// <param name="defaultValue">默认值</param>
/// <returns>用户输入内容</returns>
protected string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
{
return _context?.ShowInputDialog(message, title, defaultValue) ?? defaultValue;
}
/// <summary>
/// 获取设置值
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>设置值</returns>
protected T GetSetting<T>(string key, T defaultValue = default)
{
if (_context == null) return defaultValue;
return _context.GetSetting(key, defaultValue);
}
/// <summary>
/// 设置设置值
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="value">设置值</param>
protected void SetSetting<T>(string key, T value)
{
_context?.SetSetting(key, value);
}
/// <summary>
/// 保存设置
/// </summary>
protected void SaveSettings()
{
_context?.SaveSettings();
}
/// <summary>
/// 注册事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
protected void RegisterEventHandler(string eventName, EventHandler handler)
{
_context?.RegisterEventHandler(eventName, handler);
}
/// <summary>
/// 注销事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
protected void UnregisterEventHandler(string eventName, EventHandler handler)
{
_context?.UnregisterEventHandler(eventName, handler);
}
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="sender">事件发送者</param>
/// <param name="args">事件参数</param>
protected void TriggerEvent(string eventName, object sender, EventArgs args)
{
_context?.TriggerEvent(eventName, sender, args);
}
}
}
+28
View File
@@ -0,0 +1,28 @@
namespace InkCanvasForClass.PluginSdk
{
/// <summary>
/// 通知类型枚举
/// </summary>
public enum NotificationType
{
/// <summary>
/// 信息
/// </summary>
Info,
/// <summary>
/// 成功
/// </summary>
Success,
/// <summary>
/// 警告
/// </summary>
Warning,
/// <summary>
/// 错误
/// </summary>
Error
}
}
+160
View File
@@ -0,0 +1,160 @@
# Ink Canvas Plugin SDK
Ink Canvas 插件开发SDK,用于开发墨迹画布应用的插件。
**命名空间**`InkCanvasForClass.PluginSdk`(避免与 WPF 控件 `System.Windows.Controls.InkCanvas` 在引用 `InkCanvas.*` 时产生歧义)。
## 方案 B:轻量注册表(无 Microsoft.Extensions.DependencyInjection
宿主在加载每个插件、调用 `Initialize` 之后,会调用 `InkCanvasPluginBase.RegisterExtensions(IPluginRegistry registry)`。插件可在此登记菜单项、工具栏按钮、设置页工厂;宿主窗口稍后统一挂载。注册表实现类型为 `InkCanvasForClass.PluginHost.CollectingPluginRegistry`,由主程序内 `PluginManager.Instance.ExtensionRegistry` 暴露。
## 安装
```bash
dotnet add package InkCanvas.PluginSdk
```
## 快速开始
### 1. 创建插件项目
创建一个新的类库项目,并添加对 `InkCanvas.PluginSdk` 的引用。
### 2. 实现插件接口
```csharp
using InkCanvas.PluginSdk;
using System;
using System.Windows.Controls;
namespace MyPlugin
{
public class MyPlugin : InkCanvasPluginBase
{
public override string Id => "com.example.myplugin";
public override string Name => "我的插件";
public override string Description => "这是一个示例插件";
public override Version Version => new Version(1, 0, 0);
public override string Author => "插件作者";
public override void Start()
{
// 插件启动时的逻辑
ShowNotification("插件已启动!", NotificationType.Success);
}
public override void Stop()
{
// 插件停止时的逻辑
ShowNotification("插件已停止!", NotificationType.Info);
}
public override UserControl GetSettingsView()
{
// 返回插件设置界面
return new MyPluginSettingsView();
}
}
}
```
### 3. 插件功能
#### 访问主应用程序功能
通过 `Context` 属性可以访问主应用程序的各种功能:
```csharp
// 获取当前画布
var canvas = Context.CurrentCanvas;
// 设置墨迹颜色
Context.SetInkColor(Colors.Red);
// 清除画布
Context.ClearCanvas();
// 显示通知
Context.ShowNotification("操作完成!", NotificationType.Success);
```
#### 创建菜单项
```csharp
public override IEnumerable<MenuItem> GetMenuItems()
{
var menuItem = new MenuItem
{
Header = "我的功能",
Icon = new Image { Source = MyIcon }
};
menuItem.Click += (s, e) => {
// 处理菜单点击
ShowNotification("菜单被点击了!");
};
return new[] { menuItem };
}
```
#### 创建工具栏按钮
```csharp
public override IEnumerable<Button> GetToolbarButtons()
{
var button = new Button
{
Content = "我的工具",
ToolTip = "这是一个工具按钮"
};
button.Click += (s, e) => {
// 处理按钮点击
Context.SetInkColor(Colors.Blue);
};
return new[] { button };
}
```
#### 事件处理
```csharp
public override void Start()
{
// 注册事件处理器
RegisterEventHandler("CanvasChanged", OnCanvasChanged);
RegisterEventHandler("DrawingModeChanged", OnDrawingModeChanged);
}
private void OnCanvasChanged(object sender, EventArgs e)
{
ShowNotification("画布已更改");
}
private void OnDrawingModeChanged(object sender, EventArgs e)
{
ShowNotification($"绘制模式已更改为: {Context.CurrentDrawingMode}");
}
```
## API 参考
### IInkCanvasPlugin 接口
插件必须实现的主要接口。
### IPluginContext 接口
提供对主应用程序功能的访问。
### InkCanvasPluginBase 基类
提供插件的基本实现,建议继承此类。
## 示例插件
查看 `Examples` 文件夹中的示例插件,了解如何实现各种功能。
## 许可证
MIT License
@@ -0,0 +1,137 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using InkCanvasForClass.PluginSdk;
namespace InkCanvasForClass.SamplePlugins
{
/// <summary>
/// 示例插件:在当前 <see cref="InkCanvas"/> 右上角叠加显示实时时钟(不拦截笔触命中)。
/// </summary>
public sealed class ClockOverlayPlugin : InkCanvasPluginBase
{
public override string Id => "inkcanvas.sample.clock-overlay";
public override string Name => "画布时钟示例";
public override string Description => "在画布右上角显示当前时间(HH:mm:ss)";
public override Version Version => new Version(1, 0, 0);
public override string Author => "ICC CE Sample";
private Border _host;
private TextBlock _timeText;
private DispatcherTimer _timer;
private System.Windows.Controls.InkCanvas _canvas;
private SizeChangedEventHandler _sizeHandler;
public override void Start()
{
base.Start();
var mw = Context?.MainWindow;
var canvas = Context?.CurrentCanvas as System.Windows.Controls.InkCanvas;
if (mw == null || canvas == null)
{
return;
}
mw.Dispatcher.Invoke(() =>
{
_canvas = canvas;
_host = new Border
{
Background = new SolidColorBrush(Color.FromArgb(170, 32, 32, 36)),
CornerRadius = new CornerRadius(8),
Padding = new Thickness(12, 8, 12, 8),
IsHitTestVisible = false
};
_timeText = new TextBlock
{
FontSize = 24,
FontWeight = FontWeights.SemiBold,
Foreground = Brushes.White,
FontFamily = new FontFamily("Segoe UI, Microsoft YaHei UI")
};
_host.Child = _timeText;
Panel.SetZIndex(_host, 10050);
_canvas.Children.Add(_host);
_sizeHandler = (s, e) => Reposition();
_canvas.SizeChanged += _sizeHandler;
Reposition();
_timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
_timer.Tick += (_, __) => Tick();
_timer.Start();
Tick();
});
}
private void Tick()
{
if (_timeText != null)
{
_timeText.Text = DateTime.Now.ToString("HH:mm:ss");
}
}
private void Reposition()
{
if (_canvas == null || _host == null)
{
return;
}
_host.UpdateLayout();
var left = Math.Max(8, _canvas.ActualWidth - _host.ActualWidth - 20);
InkCanvas.SetLeft(_host, left);
InkCanvas.SetTop(_host, 20);
}
public override void Stop()
{
var mw = Context?.MainWindow;
if (mw != null && (_canvas != null || _host != null))
{
mw.Dispatcher.Invoke(() =>
{
try
{
if (_timer != null)
{
_timer.Stop();
_timer = null;
}
if (_canvas != null && _sizeHandler != null)
{
_canvas.SizeChanged -= _sizeHandler;
_sizeHandler = null;
}
if (_canvas != null && _host != null && _canvas.Children.Contains(_host))
{
_canvas.Children.Remove(_host);
}
}
catch
{
// 避免卸载时异常影响宿主
}
finally
{
_host = null;
_timeText = null;
_canvas = null;
}
});
}
base.Stop();
}
}
}
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<RootNamespace>InkCanvasForClass.SamplePlugins</RootNamespace>
<AssemblyName>SampleClockPlugin</AssemblyName>
<UseWPF>true</UseWPF>
<LangVersion>7.3</LangVersion>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\SDK\InkCanvas.PluginSdk.csproj" />
</ItemGroup>
</Project>