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,如需转载请自行联系原作者


相关文章
|
1月前
|
XML 开发框架 前端开发
WPF学习之基础知识篇
WPF(Windows Presentation Foundation)具有一个复杂且强大的架构,旨在提供丰富的用户界面、图形、动画和多媒体功能。
59 9
|
6月前
|
前端开发 C#
WPF学习小记
WPF学习小记
|
3月前
|
C# 机器学习/深度学习 搜索推荐
WPF与机器学习的完美邂逅:手把手教你打造一个具有智能推荐功能的现代桌面应用——从理论到实践的全方位指南,让你的应用瞬间变得高大上且智能无比
【8月更文挑战第31天】本文详细介绍如何在Windows Presentation Foundation(WPF)应用中集成机器学习功能,以开发具备智能化特性的桌面应用。通过使用Microsoft的ML.NET框架,本文演示了从安装NuGet包、准备数据集、训练推荐系统模型到最终将模型集成到WPF应用中的全过程。具体示例代码展示了如何基于用户行为数据训练模型,并实现实时推荐功能。这为WPF开发者提供了宝贵的实践指导。
40 0
|
3月前
|
开发者 C# UED
WPF与多媒体:解锁音频视频播放新姿势——从界面设计到代码实践,全方位教你如何在WPF应用中集成流畅的多媒体功能
【8月更文挑战第31天】本文以随笔形式介绍了如何在WPF应用中集成音频和视频播放功能。通过使用MediaElement控件,开发者能轻松创建多媒体应用程序。文章详细展示了从创建WPF项目到设计UI及实现媒体控制逻辑的过程,并提供了完整的示例代码。此外,还介绍了如何添加进度条等额外功能以增强用户体验。希望本文能为WPF开发者提供实用的技术指导与灵感。
134 0
|
3月前
|
存储 开发者 C#
WPF与邮件发送:教你如何在Windows Presentation Foundation应用中无缝集成电子邮件功能——从界面设计到代码实现,全面解析邮件发送的每一个细节密武器!
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中集成电子邮件发送功能,详细介绍了从创建WPF项目到设计用户界面的全过程,并通过具体示例代码展示了如何使用`System.Net.Mail`命名空间中的`SmtpClient`和`MailMessage`类来实现邮件发送逻辑。文章还强调了安全性和错误处理的重要性,提供了实用的异常捕获代码片段,旨在帮助WPF开发者更好地掌握邮件发送技术,提升应用程序的功能性与用户体验。
54 0
|
3月前
|
API C# Shell
WPF与Windows Shell完美融合:深入解析文件系统操作技巧——从基本文件管理到高级Shell功能调用,全面掌握WPF中的文件处理艺术
【8月更文挑战第31天】Windows Presentation Foundation (WPF) 是 .NET Framework 的关键组件,用于构建 Windows 桌面应用程序。WPF 提供了丰富的功能来创建美观且功能强大的用户界面。本文通过问题解答的形式,探讨了如何在 WPF 应用中集成 Windows Shell 功能,并通过具体示例代码展示了文件系统的操作方法,包括列出目录下的所有文件、创建和删除文件、移动和复制文件以及打开文件夹或文件等。
71 0
|
3月前
|
C# Windows 监控
WPF应用跨界成长秘籍:深度揭秘如何与Windows服务完美交互,扩展功能无界限!
【8月更文挑战第31天】WPF(Windows Presentation Foundation)是 .NET 框架下的图形界面技术,具有丰富的界面设计和灵活的客户端功能。在某些场景下,WPF 应用需与 Windows 服务交互以实现后台任务处理、系统监控等功能。本文探讨了两者交互的方法,并通过示例代码展示了如何扩展 WPF 应用的功能。首先介绍了 Windows 服务的基础知识,然后阐述了创建 Windows 服务、设计通信接口及 WPF 客户端调用服务的具体步骤。通过合理的交互设计,WPF 应用可获得更强的后台处理能力和系统级操作权限,提升应用的整体性能。
98 0
|
3月前
|
前端开发 C# 容器
WPF/C#:实现导航功能
WPF/C#:实现导航功能
62 0
|
开发框架 前端开发 JavaScript
WPF+ASP.NET SignalR实现简易在线聊天功能
WPF+ASP.NET SignalR实现简易在线聊天功能
205 0
|
自然语言处理 编译器 C#
【WPF】实现动态切换语言(国际化)以及动态换肤功能
以下内容,手把手从搭建到最终实现,完成多语言切换以及换装功能。
439 0
【WPF】实现动态切换语言(国际化)以及动态换肤功能