WPF:从WPF Diagram Designer Part 3学习如何给设计器增加连接线功能

  通过前面介绍的从WPF Diagram Designer Part 1学习控件模板、移动、改变大小和旋转从WPF Diagram Designer Part 2学习面板、缩略图、框线选择和工具箱,我们学会了如何建立图形设计器的基本移动、选择、大小、旋转、缩略图、框选等基本功能。对于建模支持来说,流程图是必不可少的一种图形,那么我们如何让图形设计器支持在设计对象之间画上箭头呢?本篇将介绍图形设计器中的连接。

WPF Diagram Designer - Part 3

  画连接线存在多种实现方式,一种是在工具箱中提供一个连接元素,然后由设计人员选择后在两个组件中拖拽;还有一种就是由组件自身提供连接点,用户点击这个连接点后拖拽到另一个组件的连接点之上,这篇文章采用的是第二种方案。由于在建模中可能会存在多种不同的关系,所以在OpenExpressAppMetaModelEngine中的图形设计器将采用第一种方式,但是会提供第二种方式的快捷方式。

如何连接

  通过以下连接说明,我们可以知道存在连接Connection和连接点Connector两个概念,这两个概念分别由两个装饰对象来支持显示和操作。

如果在连接点上释放鼠标,则生成一个Connection对象,这个对象作为DesignerCanvas的子对象。

Connection与DesingerItem一样,也实现了ISelectable选择接口,当选择连接时,Connection之上的ConnectionAdorner显示两个矩形 每个矩形是一个Thumb控件,可以拖动更改连接点

将鼠标移到一个元素上面,元素四周会出现四个Connector,这个是在ConnectorDecoratorTemplate中进行定义的,其中在DesignerItem的模板也定义了一部分内容。
当鼠标移动到其中一个 Connector上,鼠标
指针会变成会十字形状

 

当在connector上点击鼠标左键进行拖动时,connector将生成一个ConnectorAdorner,显示当前鼠标位置与源连接点的连线,当鼠标移动时,DesignerCanvas将在不断检查是否鼠标在潜在的目标连接点上

连接点 Connector

连接点是显示在设计元素之上的可供连接线关联的位置,它的Position属性代表连接点中心相对于DesignCanvas位置,其实现代码如下:

代码
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));
}
}

...

}

连接点装饰模板 ConnectorDecoratorTemplate

DesignerItem的样式文件中包含了连接点控件如下:

<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="{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>

连接Connection

两个连接点之间连线后生成连接对象Connection,Connection有两个属性SourceSink,分别代码源Connector和目的Connector,当这两个Connector的Position改变时会通知Connection调用UpdatePathGeometry算法来更新连接线路径。

Connection的代码如下:

 

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

....

}

定制化连接点布局

  • ConnectorDecoratorTemplate

有时缺省的四个连接点可能并不是我们所需要的,如一个三角形DesignerItem的定义如下:

 

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

这个三角形显示的默认连接点如下图左边所示,但是我们需要的是下面所示的连接点,那么我们是如何定制化连接点布局的呢?

设计器通过DesignerItem的附加属性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>

 

  • RelativePositionPanel和RelativePositionPanel.RelativePosition

以上三角形的连接点属于比较规格的图形,有时候遇到不规则图形时可能就比较难按照上面这种布局方式去设计了,于是我们设计了一个处理相对位置布局的一个RelativePositionPanel ,并给Connector 加了一个附加属性RelativePositionPanel.RelativePosition来处理使用相对位置来设置连接点的情况。

以下为定义五角星的示例:


上图五角星的定义如下:
 

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

MetaModelEngine将增加一种布局方式:按绝对位置布局连接点

 

 

欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ]

 

 

 


   

posted on 2010-08-19 09:56  周 金根  阅读(9624)  评论(3编辑  收藏  举报

导航