65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 图形设计器 - 第 3 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (239投票s)

2008年2月24日

CPOL

4分钟阅读

viewsIcon

718970

downloadIcon

22984

连接项

WPF FlowChart Designer
  • 第 1 部分 - 特性:在画布上拖动、缩放和旋转项
  • 第 2 部分 - 特性:工具箱、拖放、套索选择

引言

在典型的图形设计器中,存在不同的连接项的技术。一种方法是在工具箱中提供连接元素,用户可以将它们拖放到设计器画布上,然后将端点拖动到源项和目标项。另一种方法是项本身提供连接点,用户可以从这些连接点拖动连接到其他项。第二种策略是我将在本文中解释的。

用例:如何连接项

我相信您知道如何在设计器应用程序中连接项,但我仍将详细说明,以便更容易识别哪个类涉及哪个活动。

当您将鼠标悬停在设计器项上时,每个侧面会出现四个 Connector 类型的视觉元素。此默认布局定义在 ConnectorDecoratorTemplate 中,并且是默认 DesignerItem 模板的一部分。现在将鼠标悬停在其中一个连接器上,光标会变成一个十字。 WPF Diagram Designer: Connecting items
如果您单击鼠标左键并开始拖动,连接器将创建一个 ConnectorAdorner 类型的装饰器。此装饰器负责绘制源连接器和当前鼠标位置之间的路径。在拖动过程中,装饰器会持续对 DesignerCanvas 进行命中测试,以检查鼠标是否悬停在潜在的目标连接器上。 WPF Diagram Designer: Connecting items
如果您在 Connector 元素上释放鼠标按钮,ConnectorAdorner 会创建一个新的 Connection 实例并将其添加到设计器画布的子元素中。如果鼠标按钮在其他地方释放,则不会创建 Connection 实例。 WPF Diagram Designer: Connecting items
DesignerItem 类似,Connection 实现了 ISelectable 接口。如果一个 Connection 实例被选中,您将在连接路径的每个末端看到两个矩形。它们属于一个 ConnectionAdorner 类型的装饰器,当 Connection 实例被选中时,该装饰器会自动显示。
注意: ConnectorAdorner 属于 Connector,而 ConnectionAdorner 属于 Connection
WPF Diagram Designer: Connecting items
这两个矩形中的每一个都代表一个 Thumb 控件,它们是 ConnectionAdorner 实例的一部分,允许您修改现有的连接。 WPF Diagram Designer: Connecting items
例如,如果您将连接的目标 thumb 拖动到另一个连接器并将其释放到那里,您可以重新连接现有连接。
注意: ConnectorAdornerConnectionAdorner 在功能上是相似的,但在如何使用 Adorner 类方面有所不同。
WPF Diagram Designer: Connecting items

连接如何“粘合”到项上?

连接器的默认布局定义在 ConnectorDecoratorTemplate 中,这是 DesignerItem 模板的一部分

 <ControlTemplate x:Key="ConnectorDecoratorTemplate" TargetType="{x:Type 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>

Connector 类有一个 Position 属性,指定连接器中心点相对于设计器画布的相对位置。由于 Connector 类实现了 INotifyPropertyChanged 接口,因此它可以通知客户端属性值已更改。现在,当设计器项更改其位置或大小后,连接器的 LayoutUpdated 事件将作为 WPF 布局过程的一部分自动触发。这时 Position 属性会更新,并自身触发一个事件来通知客户端。

 public class Connector : Control, INotifyPropertyChanged
 {
     private Point position;
     public Point Position
     {
         get { return position; }
         set
         {
             if (position != value)
             {
                 position = value;
                 OnPropertyChanged("Position");
             }
         }
     }

       public Connector()
       {
           // fired when layout changes
           base.LayoutUpdated += new EventHandler(Connector_LayoutUpdated);
       }

       void Connector_LayoutUpdated(object sender, EventArgs e)
       {
           DesignerCanvas designer = GetDesignerCanvas(this);
           if (designer != null)
           {
               //get center position of this Connector relative to the DesignerCanvas
               this.Position = this.TransformToAncestor(designer).Transform
                    (new Point(this.Width / 2, this.Height / 2));
           }
       }

    ...

 }

现在我们切换到 Connection 类。Connection 类有一个 Source 和一个 Sink 属性,两者都是 Connector 类型。当源连接器或目标连接器被设置时,我们立即注册一个事件处理程序来监听连接器的 PropertyChanged 事件。

  public class Connection : Control, ISelectable, INotifyPropertyChanged
  {
       private Connector source;
       public Connector Source
       {
           get
           {
               return source;
           }
           set
           {
               if (source != value)
               {
                   if (source != null)
                   {
                       source.PropertyChanged -=
                           new PropertyChangedEventHandler(OnConnectorPositionChanged);
                       source.Connections.Remove(this);
                   }

                   source = value;

                   if (source != null)
                   {
                       source.Connections.Add(this);
                       source.PropertyChanged +=
                           new PropertyChangedEventHandler(OnConnectorPositionChanged);
                   }

                   UpdatePathGeometry();
               }
           }
       }

      void OnConnectorPositionChanged(object sender, PropertyChangedEventArgs e)
      {
          if (e.PropertyName.Equals("Position"))
          {
              UpdatePathGeometry();
          }
      }

     ....

  } 

此代码片段仅显示源连接器,但目标连接器的工作方式类似。事件处理程序最终会更新连接路径的几何形状,就是这样。

自定义连接器布局

默认布局和连接器数量可能并不总是满足您的需求。以一个三角形路径的例子为例,它具有自定义的 DragThumbTemplate(请参阅上一篇文章,了解如何自定义 DragThumbTemplate)。

 <Path IsHitTestVisible="False"
       Fill="Orange"
       Stretch="Fill"
       Data="M 0,10 5,0 10,10 Z">
   <s:DesignerItem.DragThumbTemplate>
      <ControlTemplate>
         <Path Fill="Transparent" Stretch="Fill"
                   Data="M 0,10 5,0 10,10 Z"/>
      </ControlTemplate>
   </s:DesignerItem.DragThumbTemplate>
 </Path>       

WPF Diagram Designer: Custom connectors

这里的问题是连接器仅在鼠标悬停在项上时可见。如果您尝试触及左侧或右侧的连接器,可能会遇到一些问题。但解决方案以名为 DesignerItem.ConnectorDecoratorTemplate 的附加属性的形式出现,它允许您为连接器装饰器定义自定义模板。用法最好通过示例来解释

 <Path IsHitTestVisible="False"
       Fill="Orange"
       Stretch="Fill"
       Data="M 0,10 5,0 10,10 Z">
   <!-- Custom DragThumb Template -->
   <s:DesignerItem.DragThumbTemplate>
      <ControlTemplate>
         <Path Fill="Transparent" Stretch="Fill"
               Data="M 0,10 5,0 10,10 Z"/>
      </ControlTemplate>
   <s:DesignerItem.DragThumbTemplate>
   <!-- Custom ConnectorDecorator Template -->
   <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> 

WPF Diagram Designer: Custom connectors

此解决方案提供了更好的结果,但仍然需要一些棘手的布局,这可能不总是可行的。为此,我提供了一个 RelativePositionPanel,它允许您相对于面板边界定位项。下面的示例通过设置 RelativePosition 属性(一个附加属性)来定位 RelativePositionPanel 上的三个按钮。

 <c:RelativePositionPanel>
    <Button Content="TopLeft" c:RelativePositionPanel.RelativePosition="0,0"/>
    <Button Content="Center" c:RelativePositionPanel.RelativePosition="0.5,0.5"/>
    <Button Content="BottomRight" c:RelativePositionPanel.RelativePosition="1,1"/>
 </ControlTemplate>

当需要排列连接器时,这个面板会非常有用

 <Path IsHitTestVisible="False"
       Fill="Orange"
       Stretch="Fill"
       Data="M 9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7 Z">
   <!-- Custom DragThumb Template -->
   <s:DesignerItem.DragThumbTemplate>
      <ControlTemplate>
         <Path Fill="Transparent" Stretch="Fill"
               Data="M 9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7 Z"/>
      </ControlTemplate>
   </s:DesignerItem.DragThumbTemplate>
   <!-- Custom ConnectorDecorator Template -->
   <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>
 </Path>  

WPF Diagram Designer: Custom connectors

Outlook(展望)

在下一篇文章中,我将重点介绍命令

  • 剪切、复制、粘贴
  • 组合项
  • 对齐项
  • Z 顺序(置于顶层、置于底层等)

历史

  • 2008 年 2 月 24 日 -- 提交原始版本
© . All rights reserved.