惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
Microsoft Security Blog
Microsoft Security Blog
S
SegmentFault 最新的问题
Forbes - Security
Forbes - Security
爱范儿
爱范儿
Stack Overflow Blog
Stack Overflow Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
CERT Recently Published Vulnerability Notes
S
Schneier on Security
Scott Helme
Scott Helme
C
Check Point Blog
T
Tenable Blog
博客园 - 三生石上(FineUI控件)
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
Google DeepMind News
Google DeepMind News
人人都是产品经理
人人都是产品经理
N
News and Events Feed by Topic
B
Blog
P
Privacy International News Feed
I
Intezer
T
Threatpost
Google DeepMind News
Google DeepMind News
L
LangChain Blog
Last Week in AI
Last Week in AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
LINUX DO - 最新话题
博客园_首页
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News | PayPal Newsroom
The Hacker News
The Hacker News
S
Security @ Cisco Blogs
罗磊的独立博客
PCI Perspectives
PCI Perspectives
Y
Y Combinator Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
大猫的无限游戏
大猫的无限游戏
U
Unit 42
Hacker News: Ask HN
Hacker News: Ask HN
D
Docker
AI
AI
小众软件
小众软件
博客园 - 叶小钗
H
Help Net Security
TaoSecurity Blog
TaoSecurity Blog
P
Privacy & Cybersecurity Law Blog

博客园 - 挑战

从存储过程中读取相关信息 Blend Step by Step书籍笔记(第一章) WPF非轮询方式实时更新数据库变化SqlDependency 动态解析XAML文本构建WPF的UI 解决为'*********' 的游标已存在问题 数据表死锁查询和处理 SQL Server操作XML(六)XML FLOWR SQL Server操作XML(五)XML Query-XQuery SQL Server操作XML(四)XML数据类型 SQL Server操作XML(三)OPENXML函数功能 SQL Server操作XML(二)XML子句实例 SQL Server操作XML(一)XML子句 数据绑定 最为详尽的WPF类继承关系 LinQ数据访问 照猫画虎WPF之二数据绑定 照猫画虎WPF之一:命名空间 解决WPF部署后客户端访问安全性问题 C#读取文本播放相应语音
WPF Diagram Designer Part 3:连接Item
挑战 · 2012-03-31 · via 博客园 - 挑战

  • 介绍

     在典型的图形设计器中,有多种技术手段实现item之间的连接:

    (1)在工具箱中,提供连接元素,用户可以将其先拖拽到Designer Canvas上,然后,通过连接点将源Item和目标Item连接起来。

    (2)Item本身具备连接点,用户可以直接拖拽到其余Item上。

     本文,采用第(2)种方式实现。

  • 用例(连接Item)

      相信,大家已经知道:在设计器应用程序中,如何去连接Item,但是这里会介绍一些细节问题,以此来说明在连接过程中,各个活动所涉及到的相关类。  

     (1)如果将鼠标移动到designer item上时,将会在Item的四周出现类型为Connector四个元素。其默认的布局定义在ConnectorDecoratorTemplate,它是DesignerItem 模板的一部分。当鼠标移动到其中一个Connector之上时,鼠标变为十字形。

   

       (2)如果点击鼠标左键并拖拽,connector将会生成一个类型为ConnectorAdorneradorner,该adorner负责绘制源connector与当前鼠标位置之间的路径。在拖拽过程中,adorner同时针对DesignerCanvas进行hit-testing,以便确认鼠标是否处于目标Itemconnector之上。

   

       (3)如果在一个Connector元素上方释放鼠标,ConnectorAdorner将会创建一个Connection实例,并将其添加到designer canvas的子控件集合中。如果鼠标在其它地方释放,将不会创建Connection实例。

     

       (4)正如DesignerItem一样,Connection也实现ISelectable接口。如果一个Connection实例被选中,将会在connection path的两端看到两个rectangles。他们属于类型为ConnectionAdorneradorner,会在Connection被选中后,自动出现。

         注意:ConnectorAdorner属于Connector,而ConnectionAdorner属于Connection 

     

        (5)这两个rectangles代表Thumb控件,它们是ConnectionAdorner实例的一部分,该实例允许修改当前连接。

      

         (6)例如,拖拽connection目标Item端的thumb并释放,可以重新连接当前connection。

          注意:ConnectorAdornerConnectionAdorner在功能上很相似,不同的是它们使用Adorner类的方式。

       

  • Connection连接到Item 

       Connectors的默认布局是ConnectorDecoratorTemplate,它是DesignerItem模板的一部分。

View Code

 <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中心位置相对于designer canvas的相对位置。由于Connector类实现了INotifyPropertyChanged接口,因此它能通知客户端该属性值的变化。当Item改变位置或者改变自身尺寸时,自动触发connector的LayoutUpdated事件,作为WPF布局程序的一部分。当Position属性更新之后,会触发事件通知客户端。

View Code

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 / 2this.Height / 2));
           }
       }

    ...

 }

     Connection拥有SourceSink属性,其类型都是Connector。当sourcesink connector被设置时,会立即注册事件Handler监听connectorPropertyChanged事件。

View Code

  

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();
          }
      }

     ....

  } 

     这段代码仅仅显示了source connector,但是sink connector也一样。事件handler最后会更新connection path。

  • 定制Connector模板


    默认的布局和connectors数目有时无法满足需求。可以通过有自定义模板DragThumbTemplate三角形来完成以下练习(参见前文如何自定义DragThumbTemplate 

    View Code

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

 

        问题是仅仅在鼠标置于Item上方时,connectors才可见。如果尝试触及connector左右两边时会存在问题。但通过名为DesignerItem.ConnectorDecoratorTemplate 的附件属性可以解决该问题,它允许为connector decorator自定义模板。其用法为:

View Code

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

  

     该解决方案提供了另外一种方法,但是需要较为巧妙的布局,通常是行不通的。为此,提供了RelativePositionPanel,允许相对于panel 的边缘来放置items。下面的例子是通过设置RelativePosition附加属性,RelativePositionPanel放置三个按钮。

View Code

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

   当安放Connnector位置时,该Panel离得非常近。

View Code

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