WPF Diagram Designer 系列第三部分,核心攻克「節點間連接線」功能。採用「連接點」模式:節點邊緣提供連接點,使用者拖曳連接點至目標節點連接點形成連線。本文深度剖析 Connector、Connection、ConnectorDecoratorTemplate、ConnectionAdorner 四大核心類別與模板的協作機制。
ConnectorDecoratorTemplate)ConnectorAdorner 顯示橡皮筋預覽線Connection 物件加入 CanvasConnectionAdorner 顯示端點調整控制點






代表節點上可被連接的錨點,實作 `INotifyPropertyChanged` 通知位置變更:
public class Connector : Control, INotifyPropertyChanged {
private Point _position;
public Point Position {
get => _position;
set { if (_position != value) { _position = value; OnPropertyChanged("Position"); } }
}
public Connector() {
LayoutUpdated += Connector_LayoutUpdated; // 佈局更新時計算相對 Canvas 位置
}
void Connector_LayoutUpdated(object sender, EventArgs e) {
var designer = GetDesignerCanvas(this);
if (designer != null) {
// 連接點中心點座標(相對 DesignerCanvas)
Position = this.TransformToAncestor(designer)
.Transform(new Point(Width / 2, Height / 2));
}
}
// 連接點屬於哪些 Connection
public ObservableCollection<Connection> Connections { get; } = new ObservableCollection<Connection>();
// Orientation: Left/Top/Right/Bottom 影響 Connection 路徑計算
public ConnectorOrientation Orientation { get; set; }
}
連接兩個 Connector,監聽其 Position 變更自動更新幾何路徑:
public class Connection : Control, ISelectable, INotifyPropertyChanged {
private Connector _source;
public Connector Source {
get => _source;
set {
if (_source != value) {
if (_source != null) {
_source.PropertyChanged -= OnConnectorPositionChanged;
_source.Connections.Remove(this);
}
_source = value;
if (_source != null) {
_source.Connections.Add(this);
_source.PropertyChanged += OnConnectorPositionChanged;
}
UpdatePathGeometry();
}
}
}
// Sink (Target) 同理...
void OnConnectorPositionChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == "Position") UpdatePathGeometry();
}
void UpdatePathGeometry() {
// 根據 Source.Position, Sink.Position, Orientation 計算 PathGeometry
// 支援直角折線、貝茲曲線、直線等路由演算法
PathGeometry = CalculatePath(Source.Position, Sink.Position, Source.Orientation, Sink.Orientation);
}
}
定義於 `DesignerItem` Style 中,滑鼠懸停時顯示:
<Style TargetType="{x:Type s:DesignerItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type s:DesignerItem}">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter />
<Control x:Name="PART_ConnectorDecorator" Visibility="Hidden"
Template="{StaticResource ConnectorDecoratorTemplate}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="PART_ConnectorDecorator" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ConnectorDecoratorTemplate" TargetType="Control">
<Grid Margin="-5"> <!-- 負邊距讓連接點超出節點邊界 -->
<s:Connector Orientation="Left" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<s:Connector Orientation="Top" VerticalAlignment="Top" HorizontalAlignment="Center"/>
<s:Connector Orientation="Right" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<s:Connector Orientation="Bottom" VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
</Grid>
</ControlTemplate>
三角形節點需自定義連接點位置:
<Path Data="M 0,10 5,0 10,10 Z" Fill="Orange">
<s:DesignerItem.ConnectorDecoratorTemplate>
<ControlTemplate>
<Grid Margin="0">
<s:Connector Orientation="Top" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<s:Connector Orientation="Bottom" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
<UniformGrid Columns="2">
<s:Connector Grid.Column="0" Orientation="Left"/>
<s:Connector Grid.Column="1" Orientation="Right"/>
</UniformGrid>
</Grid>
</ControlTemplate>
</s:DesignerItem.ConnectorDecoratorTemplate>
</Path>


五角星等複雜形狀,使用相對座標 (0~1) 定位連接點:
<s:DesignerItem.ConnectorDecoratorTemplate>
<ControlTemplate>
<c:RelativePositionPanel Margin="-4">
<s:Connector Orientation="Top" c:RelativePositionPanel.RelativePosition="0.5,0"/>
<s:Connector Orientation="Left" c:RelativePositionPanel.RelativePosition="0,0.385"/>
<s:Connector Orientation="Right" c:RelativePositionPanel.RelativePosition="1,0.385"/>
<s:Connector Orientation="Bottom" c:RelativePositionPanel.RelativePosition="0.185,1"/>
<s:Connector Orientation="Bottom" c:RelativePositionPanel.RelativePosition="0.815,1"/>
</c:RelativePositionPanel>
</ControlTemplate>
</s:DesignerItem.ConnectorDecoratorTemplate>

所有 Adorner 附著於 AdornerLayer.GetAdornerLayer(designerCanvas),位於視覺樹最上層,不影響佈局。
WPF Diagram Designer 的連接機制展示了 WPF 視覺化編程的精髓:
這套機制可直接應用於流程圖編輯器、ER 圖設計器、網路拓撲圖、狀態機編輯器等領域。