WPF-Diagram | English
WPF流程图控件
https://referencesource.microsoft.com/?wt.mc_id=MVP_380318
二次开发(开发者文档)—— WPF-Diagram 控件
本文档面向需要基于本控件进行二次开发的工程师,说明项目结构、关键类型、扩展点以及典型自定义场景(自定义节点、端口、连线与数据源转换)。
目录
- 项目与模块概览
- 关键类型与关系
- 在 XAML 中集成 Diagram
- 数据驱动与 GraphSource(推荐)
- 自定义功能节点(执行逻辑与 UI)
- 自定义端口与连线
- 布局、样式与模板
- 运行/调试/扩展建议
- 常见参考位置
- 项目与模块概览
本仓库的控件主要代码位于:
..\Source\Control\HeBianGu.Diagram.DrawingBox—— 控件实现(核心)..\Source\Control\HeBianGu.Diagram.Presenter—— 演示/Presenter 层(示例节点数据、样式与命令)..\Source\App\HeBianGu.Test.Diagram—— 演示应用(如有)
核心功能分层:
Diagram:整体容器(Diagram.xaml.cs)—— 管理NodeLayer、LinkLayer、Nodes/Links 数据、命令与路由事件(如AddLinked、ItemsChanged、RunningPartChanged)。Part:基础元素基类(Part)——Node、Link、Port都继承自Part。Node/Port/Link:图元实现,包含位置、连接关系与显示逻辑。GraphSource:数据源转换抽象,负责把业务 POCO(例如Unit/Wire)转换为Node/Link实例。IFlowable/IFlowableNode/IFlowablePort/IFlowableLink:执行/运行时相关接口,支持节点/端口/连线的运行逻辑(用于流程执行功能)。
主要布局算法在 Layout 子目录(如 TreeLayout、GridLayout、ForceDirectedLayout 等)。
- 关键类型与关系(快捷参考)
HeBianGu.Diagram.DrawingBox.Diagram:主控件,XAML 使用diag:Diagram。Node:代表一个可拖拽的节点(显示 + 内容)。节点的Content通常是业务数据(POCO)或实现IFlowableNode的对象。Port:节点上的连接点。通过Port.Create(node)创建并添加到节点。Link:连接线,使用Link.Create(from, to)或Link.Create(fromNode, toNode, fromPort, toPort)创建。GraphSource<TNode, TLink>:继承后实现ConvertToNode与ConvertToLink,将业务数据转换成Node、Port、Link。Part:Node/Port/Link的公共父类,提供选择、删除、状态、事件等功能。
- 在 XAML 中集成 Diagram(示例)
- 引入命名空间:
xmlns:diag="clr-namespace:HeBianGu.Diagram.DrawingBox;assembly=HeBianGu.Diagram.DrawingBox"- 在窗口或控件中放置 Diagram:
<diag:Diagram x:Name="Diagram"
NodesSource="{Binding NodesSource}"
FlowableMode="Link" />说明:NodesSource 接受一个 IList(默认是 ObservableCollection<Node>)。最常用的方式是将业务模型通过 GraphSource 转换为 Node 集合,再把该集合赋值给 Diagram.NodesSource。
- 数据驱动与 GraphSource(推荐)
推荐使用 GraphSource<TNodeData, TLinkData> 将你的业务 POCO 转为 Node/Link。示例参考:GraphSource\UnitGraphSource.cs。
示例:
- 业务模型:
Unit、Wire(见GraphSource\Model) - 转换器:
UnitGraphSource的ConvertToNode(Unit)中创建Node、创建Port并把Unit作为Node.Content。
创建并使用:
List<Unit> units = LoadYourUnits();
List<Wire> wires = LoadYourWires();
var graph = new UnitGraphSource(units, wires); // GraphSource 会在构造时创建 Node 列表
Diagram.NodesSource = graph.NodeSource; // 或者绑定到 ViewModel优点:
- 清晰分离业务数据与展示控件
- 支持序列化 / 反序列化(保存/加载)
- 可以在
ConvertToNode中设置Node.Location、添加Port、为Node.Content赋具体对象(可能实现执行接口)
- 自定义功能节点(执行逻辑与 UI)
实现要点:
方法 A(推荐,基于 IFlowableNode):
- 定义业务节点类并实现
IFlowableNode(或继承FlowableNodeData示例类):见HeBianGu.Diagram.Presenter\Presenter\Node\FlowableNodeData.xaml.cs。 - 在
ConvertToNode时把该对象作为Node.Content:node.Content = new MyFlowableNodeData { ... }。 - 执行时,控件会调用
node.GetContent<IFlowableNode>().TryInvokeAsync(...)等方法,实现节点运行逻辑(同步或异步)。
关键接口(摘录):
IFlowableNode(继承IFlowable):定义Invoke、InvokeAsync、TryInvokeAsync,并有UseStart、State等属性。IFlowablePort/IFlowableLink:若你的端口/连线有运行逻辑,也应实现对应接口。
示例:一个简单的 IFlowableNode 实现(伪代码):
public class MyTaskNode : IFlowableNode
{
public string ID { get; set; }
public FlowableState State { get; set; }
public Task<IFlowableResult> InvokeAsync(Part pre, Node current) { ... }
public IFlowableResult Invoke(Part pre, Node current) { ... }
public Task<IFlowableResult> TryInvokeAsync(Part pre, Node current) { ... }
public void Clear() { ... }
}
// 在 GraphSource.ConvertToNode
Node node = new Node();
node.Content = new MyTaskNode { ID = unit.Id };方法 B(自定义 Node 控件子类):
- 如果需要扩展视觉树或重写鼠标/布局行为,可以继承
Node并创建自定义Style/ControlTemplate(Node本身在Part\Node.xaml.cs中)。 - 将自定义
Node实例加入NodesSource。
UI 自定义:
- 节点模板/样式:
Diagram.NodeStyle、Node的默认样式(见Part\Node.xaml/资源)可通过Style或DataTemplate覆盖。 - 节点内部
Content的渲染,可通过为内容类型定义DataTemplate来实现(例如为MyTaskNode定义一个DataTemplate)。
示例 DataTemplate:
<DataTemplate DataType="{x:Type presenter:MyTaskNode}">
<Border Background="#FFF" Padding="6">
<StackPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Message}" FontSize="12" />
</StackPanel>
</Border>
</DataTemplate>在保留父节点功能和样式下,通过重写节点Invoke方法重写执行算法
public class InfoNodeData : GeometryNodeDataBase, ICloneable
{
public InfoNodeData()
{
}
protected override Geometry GetGeometry()
{
return GeometryFactory.Create("F1M13,4L12,4 12,3C12,2.449 12.449,2 13,2 13.551,2 14,2.449 14,3 14,3.551 13.551,4 13,4 M11,3L11,9 11,13C11,13.552 10.551,14 10,14 9.733,14 9.482,13.896 9.293,13.707 9.104,13.518 9,13.267 9,13 9,12.733 9.104,12.482 9.293,12.293 9.482,12.104 9.733,12 10,12 10.276,12 10.5,11.776 10.5,11.5 10.5,11.224 10.276,11 10,11L4,11 4,3C4,2.449,4.449,2,5,2L11.278,2C11.106,2.295,11,2.634,11,3 M3,14C2.733,14 2.482,13.896 2.293,13.707 2.104,13.518 2,13.267 2,13 2,12.733 2.104,12.482 2.293,12.293 2.482,12.104 2.733,12 3,12L8.267,12C8.093,12.301 8,12.644 8,13 8,13.358 8.101,13.698 8.277,14z M13,1L5,1C3.897,1,3,1.897,3,3L3,11C2.466,11 1.964,11.208 1.586,11.586 1.208,11.964 1,12.466 1,13 1,13.534 1.208,14.036 1.586,14.414 1.964,14.792 2.466,15 3,15L10,15C11.103,15,12,14.103,12,13L12,9 12,5 13,5C14.103,5 15,4.103 15,3 15,1.897 14.103,1 13,1");
}
public override IFlowableResult Invoke(Part previors, Node current)
{
return base.Invoke(previors, current);
}
}- 自定义端口与连线
- 创建端口:
Port.Create(node),然后port.Content = yourPortData,并设置port.Dock = Dock.Left/Top/...。 - 创建连线:
Link.Create(fromNode, toNode, fromPort, toPort)或Link.Create(fromNode, toNode)。 - 如果端口/连线需要特殊初始化或数据生成,实现接口:
ILinkInitializer、ILinkDataCreator、IPortDataCreator,控件会在Link.Create中调用它们(请参考Link.xaml.cs)。
连线样式与路径:
Diagram.LinkDrawer决定连线路径绘制(BezierLinkDrawer、LineLinkDrawer、ArcLinkDrawer等实现位于LinkDrawer目录),可以替换或自定义自己的LinkDrawer。
- 布局、样式与模板
Diagram.Layout接受ILayout实例(如TreeLayout、GridLayout、ForceDirectedLayout),可以通过设置Diagram.Layout来改变布局。Diagram.NodeStyle与Diagram.LinkStyle可以在 XAML 中设置为StaticResource,从而统一控制样式。- 节点/端口/连线的默认样式资源在
Part目录下的 XAML/资源中定义,扩展时建议复用并修改。
- 运行/调试/扩展建议
- 运行逻辑:流程运行入口由
Diagram的Start命令触发,会检索UseStart为 true 的起始节点并递归执行节点/端口/连线(见Diagram.xaml.cs与Node.StartPort)。 - 调试方法:
- 在
GraphSource.ConvertToNode中临时设置断点,确认Node.Content、Port与Link建立正确; - 使用
Diagram.RefreshData()强制刷新并观察NodeLayer与LinkLayer的Children; - 确认
DataTemplate/Style的DataType与Node.Content的类型一致。
- 在
- 性能:大量节点时尽量减少复杂绑定与视觉树深度,必要时使用布局策略或分区策略优化渲染。
- 常见参考位置(仓库内)
..\Source\Control\HeBianGu.Diagram.DrawingBox\Diagram.xaml.cs—— Diagram 控件实现..\Source\Control\HeBianGu.Diagram.DrawingBox\Part\Node.xaml.cs—— Node 实现..\Source\Control\HeBianGu.Diagram.DrawingBox\Part\Port.xaml.cs—— Port 实现..\Source\Control\HeBianGu.Diagram.DrawingBox\Part\Link.xaml.cs—— Link 实现..\Source\Control\HeBianGu.Diagram.DrawingBox\GraphSource\UnitGraphSource.cs—— GraphSource 使用示例..\Source\Control\HeBianGu.Diagram.Presenter\Presenter\Node\FlowableNodeData.xaml.cs——IFlowableNode示例实现



