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

简介:

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

WPF Diagram Designer - Part 3

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

如何连接

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

将鼠标移到一个元素上面,元素四周会出现四个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将增加一种布局方式:按绝对位置布局连接点







 本文转自 jingen_zhou 51CTO博客,原文链接:http://blog.51cto.com/zhoujg/517450,如需转载请自行联系原作者


相关文章
|
7月前
|
C# Windows
2000条你应知的WPF小姿势 基础篇<78-81 Dialog/Location/WPF设备无关性>
2000条你应知的WPF小姿势 基础篇<78-81 Dialog/Location/WPF设备无关性>
55 0
|
C# 前端开发
WPF - 图形设计器(Diagram Designer)
原文:WPF - 图形设计器(Diagram Designer)   OpenExpressApp计划中包括建模工具,计划是采用MetaEdit+模型来作为元模型,使用codeproject的《WPF Diagram Designer》一系列文章来做为设计器实现参考,本篇介绍一下codeprojcet的这四个文章,推荐给对图形设计器感兴趣的人去看看,通过WPF的模板功能和其他功能可以很方便的设计出图形编辑器。
3381 0
|
Web App开发
艾伟:WinForm控件开发总结(三)------认识WinForm控件常用的Attribute
在前面的文章里我们制作了一个非常简单的控件。现在我们回过头来看看这些代码透露出什么信息。   这个类是直接从Control类派生出来的,自定义控件都是直接从Control类派生出来的。这个类定义了一个属性TextAlignment,用来控制文本在控件中显示的位置:           ...
978 0
|
C#
wpf控件设计时支持(2)
原文:wpf控件设计时支持(2) 这篇介绍在wpf设计时集合项属性添加项的定义和自定义控件右键菜单的方法 集合项属性设计时支持   1.为集合属性设计器识别具体项类型 wpf设计器允许定义集合项的类型,如新发布的WPF的DataGrid控件,其中的Columns包括一下几种类型,Columns集合属性是以下几个类型的抽象类集合.
1078 0
|
C# 容器 数据可视化
wpf控件设计时支持(3)
原文:wpf控件设计时支持(3)     wpf设计时调试 编辑模型 装饰器 1.wpf设计时调试   为了更好的了解wpf设计时框架,那么调试则非常重要,通过以下配置可以调试控件的设计时代码 (1)将启动项目配置成外部的visual studio ide启动程序devenv.
1064 0
|
C# .NET 开发框架
wpf控件设计时支持(1)
原文:wpf控件设计时支持(1)    这部分内容几乎是大家忽略的内容,我想还是来介绍一下. 本篇源码下载 1.属性元数据 在vs IDE中,在asp.net,winfrom等开发环境下,右侧的PropertyGrid属性面板,会对属性进行分类,这有利于了解控件属性的用途.
994 0
|
C#
WPF设计の自定义窗体
原文:WPF设计の自定义窗体   效果图如下:     实现思路:  1.继承Window类 2.为自定义的CustomWindow类设计窗体样式(使用Blend很方便!) 3.为窗体增加最大最小化和关闭按钮,并实现鼠标拖拽改变窗体大小(使用Derek Bartram的WindowResizer.
1015 0
|
调度 Windows C#
理解 UWP 视图的概念,让 UWP 应用显示多个窗口(多视图)
原文 理解 UWP 视图的概念,让 UWP 应用显示多个窗口(多视图) UWP 应用多是一个窗口完成所有业务的,事实上我也推荐使用这种单一窗口的方式。不过,总有一些特别的情况下我们需要用到不止一个窗口,那么 UWP 中如何使用多窗口呢? 本文内容 为什么 UWP 需要多窗口? UWP 视图的概念 UWP 多窗口 管理多个 UWP 视图 参考资料 为什么 UWP 需要多窗口? 多窗口在传统 Win32 的开发当中是司空见惯的事儿了,不过我个人非常不喜欢,因为 Windows 系统上的多窗口太多坑。
1590 0
|
C#
使用Blend设计出符合效果的WPF界面
原文:使用Blend设计出符合效果的WPF界面 之前不会用blend,感觉好难的,但美工给出的效果自己有没办法实现,所以研究了一下blend,感觉没有想象中的那么难 废话不多说,开始界面设计 今天拿到美工给的一个界面效果图 这个界面说实话,还可以吧,勉强说得过去。
2202 0
|
C# Windows
WPF 3D中多个模型如何设置某一个在最前?
原文:WPF 3D中多个模型如何设置某一个在最前? 问题:我们的模型包括导入的3D solid模型和axis坐标轴模型,当模型旋转的时候,3D会将axis挡住。
992 0