NodeStudio.Controls
0.6.4
dotnet add package NodeStudio.Controls --version 0.6.4
NuGet\Install-Package NodeStudio.Controls -Version 0.6.4
<PackageReference Include="NodeStudio.Controls" Version="0.6.4" />
<PackageVersion Include="NodeStudio.Controls" Version="0.6.4" />
<PackageReference Include="NodeStudio.Controls" />
paket add NodeStudio.Controls --version 0.6.4
#r "nuget: NodeStudio.Controls, 0.6.4"
#:package NodeStudio.Controls@0.6.4
#addin nuget:?package=NodeStudio.Controls&version=0.6.4
#tool nuget:?package=NodeStudio.Controls&version=0.6.4
NodeStudio.Controls
NodeStudio 节点图编辑器控件库(WPF,.NET 8)——画布、工具箱、属性面板与深色主题
基于 Nodify 的完整节点编辑器界面控件,配合核心库 NodeStudio 使用:提供节点画布(拖拽/连线/框选/多选删除/复制粘贴)、工具箱(Expander 工具组 + 节点图标拖放)、右侧属性面板(节点操作控件)、块内部子图编辑器、深色样式库与统一弹窗/菜单。
功能
- 节点画布:拖拽创建、拉线连接、选中节点相关连线高亮(#ffa500)、多选/框选、复制粘贴(含块内部节点与连线);多选复制粘贴:Ctrl+C 复制、Ctrl+V / 右键粘贴(单个粘贴到鼠标位置,多选按原位置整体平移并恢复相互连线)、Delete 删除选中(一次确认);快捷键上下文感知——焦点在输入框(表达式/数值编辑)中时 Ctrl+C/X/V、Delete 归文本编辑(复制/粘贴文本),点画布空白处切回焦点才是节点复制粘贴
- 通用端口:每个节点自带「运行」bool 输入(false 跳过执行)与「运行状态」int 输出(0=未运行/1=成功/2=失败,引擎执行后写入)——悬停端口可看到"运行状态:成功"这类可读提示,接条件节点即可按上游运行结果分支
- 运行控制:全局运行/暂停/连续运行;**右键任意节点「运行」**可单节点执行——不重置输入(已连接端口取上游上次输出值,未连接端口取手动值/默认值),不重跑上游;节点块右键运行即执行其内部子图;运行中画布只读保护;左下角显示图运行结果(外层/块内部按层级切换,耗时保留 3 位小数)
- 块内部编辑器:双击节点块进入子图,面包屑导航返回;循环参数编辑;块输入/块输出虚拟节点右键增删/重命名端口(类型可选,含自定义类型)
- 属性面板:选中节点显示其操作控件(
NodeBaseControl子类,XAML 自定义);内置条件/计算/延时/队列节点均带深色操作界面,条件/计算/队列节点的面板直接显示各输入/输出端口的最近一次运行值(无需悬停端口看 ToolTip),运行后自动刷新 - 面板宽度可配:编辑器控件提供
PanelRatio属性(0.15–0.85)设置右侧操作面板的默认占宽比例(默认 0.286);拖动中间分隔条仍可临时微调 - 跨块/跨图数据流:数据入队/数据出队节点 + 全局数据队列(生产者-消费者:入队缓冲、出队取走、跨轮保留);出队节点的队列名下拉候选自动包含所有层级的数据入队节点
- 属性多态序列化:属性声明为基类/接口、运行时为派生实例(含
List<T>泛型列表混装多种派生类)自动打$type标识——派生类完整属性随图保存,导入时还原为原派生类(嵌套递归、词典值、端口手动值均支持;不打标时与旧版一致,旧图文件兼容) - 反序列化完成钩子:重写
NodeViewModel.OnDeserialized()即可在图导入的全部还原(属性/端口/位置/端口手动值)完成后做自定义初始化 - 深色风格:统一深色主题 +
Styles.xaml可复用样式库——覆盖按钮/输入框/复选框/单选/开关/滑块/进度条/列表/标签页/滚动条/表格/分组框/展开器/提示/菜单等常用控件,附 24 个内置配色资源(详见下文「内置深色样式」) - 序列化接入:文件菜单导出/导入 JSON 图(经核心库
GraphSerializer)
快速开始
dotnet add package NodeStudio.Controls
项目需 net8.0-windows(或更高 windows TFM)并启用 WPF:
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/NodeStudio.Controls;component/Views/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
// 编辑器 ViewModel + 节点块
var editor = new EditorViewModel();
var block = new NodeBlockViewModel("计算块");
block.AddInput<int>("数值A").AddOutput<int>("结果");
editor.AddNode(block);
工具箱
左侧工具箱(ToolboxControl,绑定 NodeToolbox)以 Expander 工具组展示节点分类,每组标题条显示组名与节点数,可折叠:
- 节点条目 = 图标 + 名称:图标取自
[Node(Icon = "\uE8EF")](默认 Segoe MDL2 Assets 码点,浅蓝色);图片图标用Icon = "...", IconType = NodeIconType.Image——推荐嵌入资源(图片生成操作设为 Resource,填/程序集名;component/路径,随程序集分发);也支持文件路径/pack:///https URI。文件不存在、无法解码或码点无效时,工具箱显示会抛出包含节点名的明确异常 - 全局默认图标:可修改
ToolboxItem.DefaultIcon = "\uXXXX"(在扫描工具箱前设置),对所有未配图标的节点生效 - 拖拽创建:按住节点条目拖到画布即可生成节点(
ToolboxItem拖放协议) - 深色风格:标题条
DarkHeaderBackground、悬停DarkListHover,与整套样式一致
内置深色样式
合并 Styles.xaml 后即可使用(App.xaml 合并方式见上)。所有样式为显式 Key 样式,通过 StaticResource 引用;滚动条为隐式样式(全局自动生效,无需引用)。
控件样式(Views/Styles.xaml)
| 控件 | 样式 Key |
|---|---|
| 按钮 | IconButtonStyle / DialogButtonStyle(次)/ DialogPrimaryButtonStyle(主)/ DialogCloseButtonStyle(关闭) |
| 文本输入 | DialogTextBoxStyle / DarkPasswordBoxStyle |
| 选择 | DarkComboBoxStyle + DarkComboBoxItemStyle / DarkCheckBoxStyle / DarkRadioButtonStyle / DarkSwitchStyle(开关) |
| 取值 | DarkSliderStyle / DarkProgressBarStyle |
| 列表 | DarkListBoxStyle + DarkListBoxItemStyle |
| 分页 | DarkTabControlStyle + DarkTabItemStyle |
| 滚动条 | 隐式 <Style TargetType="ScrollBar">(无按钮、蓝色滑块、悬停/按下变色);别名 DarkScrollBarStyle |
| 表格 | DarkDataGridStyle + DarkDataGridColumnHeaderStyle + DarkDataGridRowStyle + DarkDataGridCellStyle + DarkDataGridRowHeaderStyle + DarkDataGridEditingTextBoxStyle |
| 容器 | DarkGroupBoxStyle / DarkExpanderStyle / DarkSeparatorStyle |
| 菜单/提示 | DarkContextMenuStyle / DarkMenuItemStyle / DarkToolTipStyle |
| 节点 | CustomNodeStyle |
内置配色(SolidColorBrush 资源,可直接引用)
<Border Background="{StaticResource DarkAccent}"/>
<TextBlock Foreground="{StaticResource DarkTextPrimary}"/>
底色:DarkWindowBackground / DarkPanelBackground / DarkHeaderBackground / DarkInputBackground
边框:DarkBorder / DarkBorderStrong / DarkBorderHover / DarkGridLine
强调:DarkAccent / DarkAccentHover / DarkAccentPressed / DarkAccentLight / DarkTextAccent
文字:DarkTextPrimary / DarkTextSecondary / DarkTextMuted / DarkTextOnAccent
危险:DarkDanger / DarkDangerPressed
状态:DarkRowHover / DarkListHover / DarkAlternatingRow / DarkScrollThumb / DarkScrollThumbHover
表格使用要点
- 单元格文本居中:
DarkDataGridCellStyle已设Horizontal/VerticalContentAlignment=Center;文本对齐还需在列的ElementStyle中设TextAlignment=Center - 列级只读:
<DataGridTextColumn IsReadOnly="True">(数据类属性需带 setter 才能编辑) - 编辑框深色:列上加
EditingElementStyle="{StaticResource DarkDataGridEditingTextBoxStyle}"
自定义节点界面
节点被选中时在右侧属性面板显示的操作控件,继承 NodeBaseControl 并用 XAML 搭建:
<ctrl:NodeBaseControl x:Class="MyApp.MyNodeControl"
xmlns:ctrl="clr-namespace:NodeStudio.Controls.Views;assembly=NodeStudio.Controls">
<TextBox Text="{Binding Subject.Value, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource DialogTextBoxStyle}"/>
</ctrl:NodeBaseControl>
public class MyNodeControl : NodeBaseControl
{
public MyNodeControl(MyNodeViewModel vm) : base(vm) => InitializeComponent();
}
// ViewModel 中(业务属性无需转发——节点属性用 SetProperty 即可自动刷新 UI;ViewModel 只做命令/交互):
public override Control GetControl() => new MyNodeControl(this);
ViewModel 注册(通常零注册):在 ViewModel 类上标注 [NodeViewModel(typeof(MyNode))](需提供公共构造函数 MyNodeViewModel(MyNode node) 包装传入实例),拖拽/复制粘贴/导入图时框架自动发现该映射并恢复操作控件,无需任何注册代码。
程序集延迟加载(插件宿主)时一键注册;依赖注入、非标准构造等特殊场景用 lambda 显式注册(优先级最高):
// 插件程序集加载后调用(不传参则扫描当前已加载的全部程序集)
NodeViewModelFactory.RegisterFromAssemblies(pluginAssembly);
// 特殊场景:lambda 显式注册,优先级高于 [NodeViewModel] 自动发现
NodeViewModelFactory.RegisterVmFactory<MyNode>(node => new MyNodeViewModel((MyNode)node));
属性多态序列化
节点属性声明为抽象基类/接口、运行时保存派生实例——包括最常见的"List<T> 泛型列表混装多种派生类"——序列化时自动写入 $type 类型标识,导入时还原为原派生类,派生类特有属性完整保留(普通属性行为不变,旧图文件兼容读取):
public abstract class Shape { public string Label { get; set; } = ""; }
public class CircleShape : Shape { public double Radius { get; set; } }
public class RectShape : Shape { public double Width { get; set; } public double Height { get; set; } }
public class MyNode : NodeBase
{
public Shape? Main { get; set; } // 单值多态:声明抽象类
public List<Shape> Shapes { get; set; } = new(); // 列表混装多种派生类
public Dictionary<string, Shape> Map { get; set; } = new(); // 词典值多态
}
序列化后列表元素逐个携带类型标识:
"Shapes": [
{ "$type": "MyApp.CircleShape", "Radius": 2.5, "Label": "主圆" },
{ "$type": "MyApp.RectShape", "Width": 3, "Height": 4, "Label": "横幅" }
]
规则与支持范围:
- 打标条件:运行时类型 ≠ 声明类型(或声明为
object/抽象类/接口);对象型值把$type并入 JSON 对象首键,基元型值包成{ "$type": …, "$value": … } - 支持嵌套递归(派生对象内部再有多态属性/多态列表)、词典值、输入端口手动值;还原时校验派生类型可赋值给声明类型,不兼容则该属性跳过(保持默认值,不中断导入)
- 限制:集合容器类型不保真(
IList<T>等接口声明还原为List<T>,元素类型完整保真);含$type的嵌套对象需可无参构造;属性图循环引用不支持
反序列化完成钩子(OnDeserialized)
图导入时每个节点的还原时序:节点构造函数 + ViewModel 构造(Init(),仅构造态,看不到保存的数据)→ 属性/端口定义/位置/端口手动值还原 → OnDeserialized()。
需要"还原后初始化"(重算派生字段、同步外部资源等),重写 NodeViewModel.OnDeserialized() 即可:
public class MyNodeViewModel : NodeViewModel
{
public MyNodeViewModel(MyNode node) { Subject = node; Init(); }
public override Control GetControl() => new MyNodeControl(this);
// 全部框架还原就绪后调用(块内部子图的节点随递归各自触发);异常会被框架吞掉,不影响导入
public override void OnDeserialized()
{
var node = (MyNode)Subject; // 此处已能看到还原后的属性与端口手动值
// ... 自定义初始化
}
}
注意:重写
Init()做不到这件事——它在 ViewModel 构造时、属性还原之前调用;复制粘贴走INode.Clone()实例克隆,不经过此钩子(粘贴场景的初始化在Clone()里做)。
依赖
- NodeStudio (>= 0.6.2)
- Nodify 7.3.0
- CommunityToolkit.Mvvm 8.4.2
- Microsoft.Xaml.Behaviors.Wpf 1.1.142
许可证
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0-windows7.0 is compatible. net9.0-windows was computed. net10.0-windows was computed. |
-
net8.0-windows7.0
- CommunityToolkit.Mvvm (>= 8.4.2)
- Microsoft.Xaml.Behaviors.Wpf (>= 1.1.142)
- NodeStudio (>= 0.6.2)
- Nodify (>= 7.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.6.4 | 83 | 9/18/2026 |
| 0.6.3 | 85 | 9/17/2026 |
| 0.6.2 | 86 | 9/16/2026 |
| 0.6.1 | 93 | 9/13/2026 |
| 0.6.0 | 96 | 9/11/2026 |
| 0.5.0 | 91 | 9/10/2026 |
| 0.4.11 | 103 | 9/8/2026 |
| 0.4.10 | 101 | 9/5/2026 |
| 0.4.9 | 100 | 9/3/2026 |
| 0.4.8 | 97 | 9/2/2026 |
| 0.4.6 | 106 | 8/30/2026 |
| 0.4.5 | 108 | 8/24/2026 |
| 0.4.4 | 101 | 8/21/2026 |
| 0.4.0 | 111 | 8/17/2026 |
| 0.3.0 | 116 | 8/16/2026 |
| 0.2.0 | 112 | 8/13/2026 |
| 0.1.0 | 106 | 8/11/2026 |
1) 计算节点/条件节点表达式新增对象路径引用 {…}:{input} 引用输入原始值、{input[0]} 取列表/数组元素、{input.Area} 取属性、{input[0].Area} 链式(.属性 与 [索引] 任意交替),可与四则/数学函数/比较/逻辑自由组合;属性缺失、索引越界、null 链断在运行期报明确中文错误(条件节点中失败路线不激活、节点标红);旧语法 [名称] 原样兼容,旧图文件零迁移。2) 修复编辑器快捷键劫持文本复制粘贴的问题:焦点在输入框(表达式/数值编辑)中时 Ctrl+C/X/V、Delete 归文本编辑(复制/粘贴文本),不再误复制/粘贴节点;点画布空白处切回焦点仍为节点快捷键。3) README 补充对象路径语法与快捷键上下文说明。