首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF Diagram Designer 學習筆記(三):連接線與連接點的完整實現機制

     編輯:關於.NET

WPF Diagram Designer 系列第三部分,核心攻克「節點間連接線」功能。採用「連接點」模式:節點邊緣提供連接點,使用者拖曳連接點至目標節點連接點形成連線。本文深度剖析 Connector、Connection、ConnectorDecoratorTemplate、ConnectionAdorner 四大核心類別與模板的協作機制。

連接交互流程

  1. 進入連接模式:滑鼠移至節點 → 顯示四個方向連接點(ConnectorDecoratorTemplate)
  2. 發起連接:點擊連接點拖曳 → 建立 ConnectorAdorner 顯示橡皮筋預覽線
  3. 目標檢測:拖曳過程中即時檢測滑鼠下方是否有其他節點的連接點
  4. 完成連接:在目標連接點釋放 → 建立 Connection 物件加入 Canvas
  5. 選中連線:點擊連線 → ConnectionAdorner 顯示端點調整控制點

連接交互流程示意圖

ConnectionAdorner 端點調整

滑鼠游標變十字形

ConnectorAdorner 預覽線

目標連接點高亮

連接完成

連線選中狀態

核心類別實現

1. Connector:連接點模型

代表節點上可被連接的錨點,實作 `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; }
}

2. Connection:連線模型

連接兩個 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);
    }
}

3. ConnectorDecoratorTemplate:連接點視覺化模板

定義於 `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>

4. 連接點自定義佈局:針對非矩形節點

三角形節點需自定義連接點位置:

<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>

預設四點 vs 自定義三角形連接點佈局

自定義佈局效果

5. RelativePositionPanel:相對位置佈局面板(不規則形狀)

五角星等複雜形狀,使用相對座標 (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 輔助視覺層

  • ConnectorAdorner:拖曳連接時的橡皮筋預覽線
  • ConnectionAdorner:連線選中時的端點調整控制點(Thumb)
  • RubberbandAdorner:框選多節點(Part 2 介紹)

所有 Adorner 附著於 AdornerLayer.GetAdornerLayer(designerCanvas),位於視覺樹最上層,不影響佈局。

設計器畫布擴展:DesignerCanvas

  • 處理滑鼠事件:`OnMouseMove` 偵測拖曳連接點、框選
  • 動態增減 Adorner:`adornerLayer.Add(adorner)` / `adornerLayer.Remove(adorner)`
  • 命中測試:`VisualTreeHelper.HitTest` 判斷滑鼠下方是否為 Connector

MetaModelEngine 擴展規劃

  • 支援「工具箱拖拽連線元素」模式(適合多種關係類型:關聯、繼承、依賴)
  • 新增「絕對位置佈局連接點」模式
  • 連線樣式樣板化:實線/虛線、箭頭樣式、顏色、標籤
  • 路由演算法優化:避開節點、正交折線自動轉彎

總結

WPF Diagram Designer 的連接機制展示了 WPF 視覺化編程的精髓:

  1. 模型與視圖分離:`Connector`/`Connection` 純邏輯模型,`Template` 定義外觀
  2. Adorner 層疊架構:編輯時輔助視覺(預覽線、控制點)不干擾主視覺樹
  3. 相對/絕對佈局彈性:`Grid`/`UniformGrid`/`RelativePositionPanel` 適應各種節點形狀
  4. 事件驅動更新:`INotifyPropertyChanged` 連鎖觸發路徑重算

這套機制可直接應用於流程圖編輯器、ER 圖設計器、網路拓撲圖、狀態機編輯器等領域。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved