经过六年的沉淀,全新版本的ConPipe 2026得到了很大的进化,除了一贯优秀的视觉效果,他在便捷性、敏捷性上、灵活性上达到了前所未有的程度。新版本的推出首先要感谢所有ConPipe的用户,以及在使用过程中提出改进意见和BUG反馈的朋友,我将继续保持快速的支持响应。ConPipe 2026在控件的快速布局和布局灵活性上做了很多努力,文章的开始我想用自创的管道字体欢迎新的一年,上图:

图中Hello Conpipe 2026几个字是用不同的ConPipe控件组合起来的,虽然ConPipe组态控件不是专门用来绘制字体的,但这充分显示了ConPipe组态控件非常灵活的布局能力,下面给出这些字的Xaml代码:
xml
<Grid>
<!--H-->
<Enterprise:XMultyConnect Name="xmc1" Margin="60.97,68.04,0,0" CenterPoint="10,60" PipeDiameter="15" UpConnectConfig="0.5,0,30" ShowBottomConnect="False" Height="120" Width="45" Length="95" Angle="90" ShowTopFlange="False" />
<Enterprise:XMultyConnect Margin="93.97,78.04,0,0" CenterPoint="8,60" PipeDiameter="15" UpConnectConfig="0.5,0,30" ShowBottomConnect="False" Height="120" Width="45" Length="95" Angle="90" ConnectTo="{Binding Top, ElementName=xmc1}" AnchorPosition="Top" RotateAngle="0" ShowTopFlange="False" />
<!--e-->
<Enterprise:ArcPipeAdapter Name="apa5" Margin="143.2,101.8,0,0" InputAngle="-20" PipeDiameter="15" ArcAngle="-280" ShowInputFlange="False"/>
<Enterprise:Pipe Data="164.21,148.39 204.16,148.49" PipeDiameter="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowFluid="False" ShowFlange="False" OutputConnect="{Binding Input, ElementName=apa5}"/>
<!--l-->
<Enterprise:Pipe x:Name="p1" Data="241.4,83.95 241.4,171.45 280.2,171.45" PipeDiameter="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowFluid="False" ShowFlange="False"/>
<Enterprise:Pipe x:Name="p2" Data="296.07,83.95 296.07,171.45 334.87,171.45" PipeDiameter="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowFluid="False" ShowFlange="False"/>
<!--o-->
<Enterprise:ArcPipeAdapter Margin="336,101.87,0,0" InputAngle="0" PipeDiameter="15" ArcAngle="359.9" ShowInputFlange="False" ShowOutputFlange="False"/>
<Line X1="0" Y1="180" X2="800" Y2="180" StrokeThickness="2" Stroke="Black" />
<!--c-->
<Enterprise:ArcPipeAdapter Margin="57,245,0,0" InputAngle="30" ArcAngle="300" Height="133" Width="133" ArcDiameter="65" PipeDiameter="15"/>
<!--o-->
<Enterprise:ArcPipeAdapter Margin="175,282,0,0" InputAngle="0" PipeDiameter="15" ArcAngle="359.9" ShowInputFlange="False" ShowOutputFlange="False"/>
<!--n-->
<Enterprise:ArcPipeAdapter Name="apa1" Margin="265.33,286.81,0,0" ArcAngle="-180" ArcDiameter="25" PipeDiameter="15" Height="60" Width="60" ShowInputFlange="False" ShowOutputFlange="False"/>
<Enterprise:PipeAdapter Width="45" Margin="252.83,327.06,0,0" InputDiameter="15" OutputDiameter="15" ConnectTo="{Binding Output, ElementName=apa1}" AnchorPosition="Input" RotateAngle="90" ShowInputFlange="False" />
<Enterprise:PipeAdapter Width="45" Margin="292.83,327.06,0,0" InputDiameter="15" OutputDiameter="15" ShowInputFlange="False" ConnectTo="{Binding Input, ElementName=apa1}" AnchorPosition="Input" RotateAngle="90" />
<!--p-->
<Enterprise:ArcPipeAdapter Name="apa2" HorizontalAlignment="Center" Margin="332.44,281.87,0,0" InputAngle="180" PipeDiameter="15" ArcAngle="359.9"/>
<Enterprise:PipeAdapter Width="80" Margin="312.94,354.62,0,0" PipeDiameter="24" InputDiameter="15" OutputDiameter="15" ConnectTo="{Binding Input, ElementName=apa2}" AnchorPosition="Input" RotateAngle="90" />
<!--i-->
<Enterprise:PipeAdapter Margin="407,317,0,0" Width="65" PipeDiameter="24" InputDiameter="15" OutputDiameter="15" RotateAngle="90"/>
<Enterprise:LightButton HorizontalAlignment="Left" Margin="430,274,0,0" VerticalAlignment="Top" Height="18" Width="18"/>
<!--p-->
<Enterprise:ArcPipeAdapter Name="apa3" HorizontalAlignment="Center" Margin="458.64,281.74,0,0" InputAngle="180" PipeDiameter="15" ArcAngle="359.9"/>
<Enterprise:PipeAdapter Width="80" Margin="439.14,354.49,0,0" PipeDiameter="24" InputDiameter="15" OutputDiameter="15" ConnectTo="{Binding Input, ElementName=apa3}" AnchorPosition="Input" RotateAngle="90" />
<!--e-->
<Enterprise:ArcPipeAdapter Name="apa4" Margin="539,281.8,0,0" InputAngle="-20" PipeDiameter="15" ArcAngle="-280" ShowInputFlange="False"/>
<Enterprise:Pipe Data="561.21,329.62 600.19,328.48" PipeDiameter="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowFluid="False" ShowFlange="False" OutputConnect="{Binding Input, ElementName=apa4}"/>
<!--2-->
<Enterprise:ArcPipeAdapter x:Name="p3" Margin="605.93,287.61,0,0" InputAngle="195" PipeDiameter="7" ArcAngle="210" ShowInputFlange="False" ShowOutputFlange="False" ArcDiameter="15"/>
<Enterprise:Pipe Data="657.67,341.1 651.09,344.08" PipeDiameter="7" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowFluid="False" ShowFlange="False" InputConnect="{Binding Output, ElementName=p3}" OutputConnect="{Binding Top, ElementName=xmc3}" />
<Enterprise:XMultyConnect x:Name="xmc3" Margin="627.46,330.61,0,0" UpConnectConfig="0.15,-53.36,13" PipeDiameter="7" Length="30" ShowBottomFlange="False" ShowBottomConnect="False" ShowInputFlange="False" ShowTopFlange="False" ShowOutputFlange="False" CenterPoint="25,25" Width="50" Height="50" />
<!--0-->
<Enterprise:ArcPipeAdapter x:Name="p4" Margin="671,324.14,0,0" InputAngle="0" PipeDiameter="7" ArcAngle="359.9" ShowInputFlange="False" ShowOutputFlange="False" ArcDiameter="15" Width="30" Height="30"/>
<!--2-->
<Enterprise:ArcPipeAdapter x:Name="p5" Margin="673.13,287.6,0,0" InputAngle="195" PipeDiameter="7" ArcAngle="210" ShowInputFlange="False" ShowOutputFlange="False" ArcDiameter="15"/>
<Enterprise:Pipe Data="724.87,341.09 718.29,344.07" PipeDiameter="7" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowFluid="False" ShowFlange="False" InputConnect="{Binding Output, ElementName=p5}" OutputConnect="{Binding Top, ElementName=xmc4}" />
<Enterprise:XMultyConnect x:Name="xmc4" Margin="694.66,330.6,0,0" UpConnectConfig="0.15,-53.36,13" PipeDiameter="7" Length="30" ShowBottomFlange="False" ShowBottomConnect="False" ShowInputFlange="False" ShowTopFlange="False" ShowOutputFlange="False" CenterPoint="25,25" Width="50" Height="50" />
<!--6-->
<Enterprise:ArcPipeAdapter x:Name="p6" Margin="707.53,300.93,0,0" InputAngle="180" PipeDiameter="7" ArcAngle="-359.9" ShowInputFlange="False" ShowOutputFlange="False" ArcDiameter="12"/>
<Enterprise:PipeAdapter x:Name="pa4" Margin="736.53,336.76,0,0" Width="13" InputDiameter="7" OutputDiameter="7" RotateAngle="270.1" ConnectTo="{Binding Input, ElementName=p6}" AnchorPosition="Output" ShowInputFlange="False" ShowOutputFlange="False"/>
<Enterprise:ArcPipeAdapter x:Name="p7" Margin="730.03,310.01,0,0" InputAngle="180" PipeDiameter="7" ArcAngle="120" ShowInputFlange="False" ShowOutputFlange="False" ArcDiameter="20" Height="53" Width="53" ConnectTo="{Binding Input, ElementName=pa4}" AnchorPosition="Input" RotateAngle="0"/>
<Line X1="0" Y1="360" X2="800" Y2="360" Panel.ZIndex="-10000" StrokeThickness="2" Stroke="Black" />
</Grid>
我在每个字母或数字的Xaml代码起始的地方添加了注释,可以一目了然地看清每个字符的组成。可以看出这些字符最多由三个ConPipe控件组成,下面我就拆解H、n、e三个字母的构建。
拆解字母之前先介绍下XMultyConnect、ArcPipeAdapter、PipeAdapter三个控件。
1,XMultyConnect是X形状的管道多通连接器,他可以将不同方向的管道连接起来。XMultyConnect是由一根平直的主管道从管身的两个侧面各延申出一个支管,形成了两个固定连接端口和两个可变连接端口。主管道的角度和长度分别由Angle和Length两个属性设置,两个可变连接端口分别由UpConnectConfig和BottomConnectConfig配置。UpConnectConfig和BottomConnectConfig都是ConnectConfig类型,ConnectConfig在Xaml属性中是以字符串的形式表达的,比如BottomConnectConfig="0.7,180,40"表示下支管从主管70%的位置出发,与X轴方向呈180度顺时针角,长度为40。当我们从工具箱中拖拽一个XMultyConnect控件到窗口中,UpConnectConfig和BottomConnectConfig属性并没有序列化到Xaml属性,原因是他们有默认,所以要不显示某个支管必须要显式设置ShowUpConnect="False"或ShowBottomConnect="False"。下图展示了XMultyConnect几种不同配置的主要形态,当然不限于这几种形态。

XMultyConnect的UpConnectConfig和BottomConnectConfig属性默认不序列化到Xaml属性中,那么如果需要修改这两个配置有两个快捷途径。一是手动在Xaml窗口中输入属性名和属性值,但是不推荐这么做;第二种方法也是推荐的方法,按下键盘Shift键用鼠标左键拖动对应支管接口的兰色半透明拖拽球改变支管的角度或长度,修改后的配置会自动序列化到Xaml窗口中,这时我们能以Xaml代码的方式精确修改支管的布局。如图:

2,ArcPipeAdapter是标准圆弧的管道连接器,他用于连接两段管道,典型的应用是将管道做180度甚至更大角度的圆滑变向。InputAngle属性是输入端口的角度,这个角度是控件中心点与输入端口中点连线与X轴的角度;ArcAngle是管道输出端口从输入端口扫过的角度,在WPF中PathGeometry不支持360度这样的值,所以ArcAngle不支持完全的360度的弧度,只能是359.999这样不等于360的值;ArcDiameter属性设置内圆弧的直径。
3,PipeAdapter是不同管径的管道适配器,InputDiameter和OutputDiameter两个属性表示输入和输出端口管径,输入输出端口的管径可以不同也可以相同,管道的长度由控件的Width属性设置,不应该手动设置控件的Height属性,因为他是根据InputDiameter和OutputDiameter自动计算的。
介绍完这三个控件,我们继续拆解管道体字母。
大写字母H由两个对称的XMultyConnect连接起来,主管的角度为90度长度30,只保留了上支管且支管长度为40角度180,与主管的夹角为90度。

小写字母n由一个半圆配置的ArcPipeAdapter和两个InputDiameter、OutputDiameter相同的PipeAdapter控件组成。

小写字母e由ArcPipeAdapter和Pipe组成。ArcPipeAdapter的ArcAngle为-290度,Pipe的ShowFluid属性值为False,表示隐藏管道内的流体。

通过三个字母的拆解我们初步了解了ConPipe控件的布局以及控件之间的连接方式,接下来我将通过动图的方式进一步展示ConPipe控件的灵活性和敏捷性。回想ConPipe最初的形态,当时控件之间的连接需要手动编写连接的配置属性。比如管道的节点配置属性Data,每个管道节点的坐标都是手动在Data属性中编辑,每次修改多节点管道真的是令人崩溃。现在的软件项目都是快速迭代,快速响应用户需求的敏捷时代,也就是小步快跑的方式。我们在洽谈一个软件项目的时候,如果在了解了用户的初步需求后能极快地构建一个可演示的方案,势必能很好地展示专业能力,更有利于与用户梳理、明确需求,那么势必能给客户带来好感。
ConPipe能实现灵活性和敏捷性的核心就是基于Visual Studio 2022的WPF扩展的控件布局辅助。基于WPF扩展UI设计者可以快捷地构建多节点管道并随时调整、编辑管道节点,并通过鼠标拖拽的方式快捷地将相关的控件连接起来形成一个整体。

从上面的动图中我们可以看出,控件的连接非常地快捷,并且随时可以改变连接的位置。还有个特性就是:相互连接的控件,随便选中一个控件Ctrl+左键拖动就能实现整体移动。通常我们刚开始设计界面时控件的放置位置一般比较随意,当随着控件数量逐渐增多而需要调整控件位置时整体移动就会非常方便,不用疲于牵一发动全身式的逐个调整控件位置。上图中,ArcPipeAdapter连接到XMultyConnect,PipeAdapter也连接到XMultyConnect,XMultyConnect则是根。根的移动会带动ArcPipeAdapter、PipeAdapter一起移动,而拖动ArcPipeAdapter则没有任何结果,除非按下Ctrl键做整体移动。
Pipe管道控件是ConPipe组态UI的核心控件。每个管道节点的座标位置上都有一个兰色半透明拖拽球,每个拖拽球都有右键菜单,拖过鼠标拖拽和右键菜单可以实现管道节点的任意操作。重点要说的是,管道的快速创建。从工具箱拖拽一个Pipe管道控件到界面后,我们可以将鼠标移动到首尾的拖拽球上,按下Shift键并移动鼠标(移动时不需要按下鼠标左键),将要新增的管道会以兰色半透明的方式呈现并跟随鼠标,在合适的位置单击固定节点。如图:

如果说管道是ConPipe组态UI的核心,那么控件之间的连接特性则是核心中的核心。要特别强调的是:控件的连接是双向的。假设现有控件A和B,从控件A的拖拽球画一条连接线到控件B,这样的行为是A主动发起,B是无感的,连接的配置信息保存在控件A的属性中。控件B则是根控件,控件A不能再独立自由拖拽移动,控件B的移动会带动A一起移动,AB成为一个整体。所以,从哪个控件发起连接不应该是随意的行为,因该遵循树形结构来布局。发起连接的具体操作是:在目标控件的拖拽球上按下Ctrl键+鼠标左键拖动,在靠近要连接的控件附近就会出现可连接指示球,在连接指示球上释放鼠标左键完成连接操作。如图:

从上图可以看出,连接线拖动到管道附近时,并没有出现连接指示球。因为管道不需要被连接,管道是主动发起连接的,正确的做法是选中管道从管道的首尾拖拽球上发起连接操作。如图:

说到连接操作,就必须要说移除连接了。移除连接通常的做法是在Xaml代码中删除相关的连接配置属性,然而ConPipe的WPF扩展在VS系统右键添加了"连接操作"选项,在子菜单中有"移除连接"选项。如图:

上图右侧的连轮也有右键菜单,他的菜单路径是"链轮操作-适配链条"。之所以要有适配链条这个操作,是因为链条、链轮组成的系统不能随意改变链轮的位置,链轮的位置应该刚好以适当的力度张紧链条,这就是为什么发动机的链轮要有张紧轮的原因。同理在ConPipe中,手动调整链轮的位置后链轮和链条就不能以合适的"力度"相配合,不是"太紧"就是"太松",所以,就需要用算法重新调整链轮使链轮和链条完美配合。
写到这里不得不提的是,ConPipe 2026有三个版本,分别是Standard(标准版)、Professional(专业版)、Enterprise(企业版)。三个版本的区别除了控件数量不同,主要的区别是:标准版只能由管道发起连接,不能由其他控件发起连接,在控件的最终表现上没有区别;专业版除了从管道发起连接,其他控件也可以发起连接;企业版则主要比专业版多了链轮、齿轮、滑轮以及链轮系统、齿轮系统、滑轮系统,其他方面没有本质的不同。(不同的版本配置在文章结尾给出)
最后,用一张动图展示企业版特色:链轮、齿轮、滑轮以及链轮系统、齿轮系统、滑轮系统、综合轮系统。为了节约篇幅,我将这几个系统柔和成一个系统来演示。

xml
<Grid>
<Enterprise:Pulley x:Name="p1" Center="1100,250" OutsideCircleRadius="55" IsDrive="True" IsReverse="False" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Enterprise:PulleyBlock x:Name="p2" Center="1219.38,344.73" OutsideCircleRadius="22" Start="1216,343" End="1240,500" HorizontalAlignment="Left" VerticalAlignment="Top" PulleyColor="#FF363534" HookColor="#FFFF6F00" AxleColor="#FF161102" Color="#FF0B60FF" IsPressure="True" />
<Enterprise:Pulley x:Name="p3" Center="1300,208" OutsideCircleRadius="28" IsPressure="False" IsStatic="True" HorizontalAlignment="Left" VerticalAlignment="Top" Color="#FF21B2B2" AxleColor="#FFB23737" />
<Enterprise:Pulley x:Name="p4" Center="1400,158" OutsideCircleRadius="28" IsPressure="False" IsStatic="True" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="50" />
<Enterprise:Pulley x:Name="p5" Center="1500,130" OutsideCircleRadius="0" IsStatic="True" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Enterprise:PulleyRopeSystem x:Name="rope" Data="p1,p2,p3,p4,p5" RopeThickness="3" OnPulleyBlockStopEventHandler="rope_OnPulleyBlockStopEventHandler" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Enterprise:Pulley x:Name="p6" Center="487,109" OutsideCircleRadius="55" IsPressure="False" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Enterprise:Pulley x:Name="p7" Center="883.14,114.36" OutsideCircleRadius="28" IsPressure="False" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Enterprise:Pulley x:Name="p8" Center="1100,250" OutsideCircleRadius="28" IsPressure="False" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Enterprise:Pulley x:Name="p9" Center="938.45,213.39" OutsideCircleRadius="28" IsPressure="True" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Enterprise:PulleyRopeSystem x:Name="rope2" Data="p6,p7,p8,p9" RopeThickness="3" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Enterprise:Gear x:Name="g1" Center="487,109" M="8" TeethNumber="18" PressureAngle="20" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="0" Color="Red" />
<Enterprise:Gear x:Name="g2" Center="420,320" M="8" TeethNumber="30" PressureAngle="20" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="0" />
<Enterprise:Gear x:Name="g3" Center="420,128" M="8" TeethNumber="18" PressureAngle="20" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="0"/>
<Enterprise:Gear x:Name="g4" Center="420,128" M="8" TeethNumber="15" PressureAngle="20" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="0"/>
<Enterprise:Gear x:Name="g5" Center="284.76,489.7" M="8" TeethNumber="36" PressureAngle="20" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="0" Color="#FF7200FF" AxleColor="#FF233A5B"/>
<Enterprise:Gear x:Name="g6" Center="420,128" M="8" TeethNumber="12" PressureAngle="20" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="0"/>
<Enterprise:Gear x:Name="g7" Center="420,128" M="8" TeethNumber="14" PressureAngle="20" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="0"/>
<Enterprise:Gear x:Name="g8" Center="420,128" M="8" TeethNumber="15" PressureAngle="20" HorizontalAlignment="Left" VerticalAlignment="Top" RotateAngle="0"/>
<Enterprise:GearSystem Name="gearsys" Data="g2,g1,-70 g2,g3,-145 g3,g4,-162 g4,g6,-63 g6,g7,-3.235 g2,g5,-220 g5,g8,-125" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Top" SpeedRatio="1" />
<Enterprise:ChainWheel x:Name="c1" Center="487,320" ChainItemSize="40,15" HorizontalAlignment="Left" TeethNumber="11" VerticalAlignment="Top" Color="Blue" />
<Enterprise:ChainWheel x:Name="c2" Center="871.01,346.34" ChainItemSize="40,15" HorizontalAlignment="Left" TeethNumber="19" VerticalAlignment="Top" Color="#FF28FF00" AxleColor="#FFE03636" />
<Enterprise:ChainWheel x:Name="c3" Center="725.64,521.03" ChainItemSize="40,15" HorizontalAlignment="Left" TeethNumber="10" IsPressure="True" VerticalAlignment="Top" />
<Enterprise:ChainWheel x:Name="c4" Center="893.36,645.14" ChainItemSize="40,15" HorizontalAlignment="Left" TeethNumber="14" VerticalAlignment="Top" />
<Enterprise:ChainWheel x:Name="c5" Center="565.03,647.63" ChainItemSize="40,15" HorizontalAlignment="Left" TeethNumber="11" VerticalAlignment="Top" />
<Enterprise:ChainItemWheelSystem Name="cisys" ChainItemSize="40,15" Data="c1,c2,c3,c4,c5" Opacity="0.8" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Top" Color="#FF0667CB" ChainItemOpacity="0.8" />
<Enterprise:UnionSpinSystem Name="union" Data="cisys,c1,gearsys gearsys,g1,rope2 rope2,p8,rope" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Top"/>
</Grid>
观察上面的Xaml代码我们会发现每四五个连续的Pulley(滑轮)、PulleyBlock(带勾滑轮)、Gear(齿轮)、ChainWheel(链轮)后面就跟着一个以System结尾的控件,这种System就是将Pulley、PulleyBlock、Gear、ChainWheel组成一个有机的轮系统,使他们能以对应的传动比进行转动。最后有个UnionSpinSystem(综合系统),他又将ChainItemWheelSystem(链轮系统)、GearSystem (齿轮系统)、PulleyRopeSystem(滑轮绳索系统)粘合为一个大的综合系统,并管理不同系统的传动比。
下面我将具体介绍GearSystem(齿轮系统)、UnionSpinSystem(综合系统)两个系统的配置。GearSystem的配置保存在Data属性,格式为:<gear1,gear2,positionAngle>空格<driveGear2,drivenGear2,positionAngle>,具体含义为用gear1驱动gear2,gear2在gear1的positionAngle方位角上,不同的配置用空格隔开。UnionSpinSystem的配置同样保存在Data属性,格式为:<driveSystem1,sub1,drivenSystem1>空格<driveSystem2,sub2,drivenSystem2>,具体含义为用名为driveSystem1系统中的sub1去驱动drivenSystem1,sub1驱动的对象是被驱动系统drivenSystem1的第一个轮,不同的配置用空格隔开。
ConPipe还有许多控件就不多介绍了,在使用的过程中很容易就能掌握其用法。如果有需要或者有其他需求的欢迎给我发私信,感谢阅读!
下面就给出正式版三个不同版本的配置单。
