• WPF中的图表设计器 – 3


    [原文地址] http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part3.aspx
    [原文作者] sukram

    [译者]WizRay

    WPFDiagramDesignerFlowChart

    简介

    在一个典型的图表编辑器中,有很多种技术能够把不同的元素连接起来。有一种实现是在工具栏中提供一种“连接线”元素,用户可以将这种元素拖动到DesignerCanvas上,然后拖动他的两个端点到需要连接的元素上。另一种实现是不同的元素自己提供一些“控点”,用户可以从这些“控点”上拖出一条连接线连接到其他元素上。在本文中,我将实现第二种策略的实现。

    用例:怎么连接两个元素

    大家肯定都知道怎么在一个设计工具中连接两个元素,但是在这儿我还是要展示一些细节,这会使我们更加清楚的看清每个动作中有哪些类被涉及到。

    WPFDiagramDesigner01

    如果你把鼠标移到一个元素表面,在他的四边会出现四个“Connector”类型的元素。这个默认的布局定义在ConnectorDecoratorTemplat中,有一部分定义也出现在DesignerItem的模板中。现在移动鼠标到其中一个Connector上,这时,鼠标指针会变成一个十字。

    WPFDiagramDesigner02

    现在,如果按下鼠标左键然后开始拖动,刚才的Connector元素会创建出ConnectorAdorner类型的Adorner。这种Adorner能够在拖动起始的元素到当前鼠标位置画一条线。当用户拖动鼠标时,这个Adorner不断的对DesignerCanvas使用Hit-test来检测当前鼠标是否在一个可能作为终点的Connector上。

    WPFDiagramDesigner03

    如果当鼠标在一个Connector上时,释放鼠标左键,ConnectorAdorner会创建一个新的Connection对象并把它加入到DesignerCanvas中。如果在没有Connector的位置释放鼠标左键,则不会创建Connection对象。

    WPFDiagramDesigner04

    和DesignerItem一样,Connection类型实现了ISelectable接口。如果一个实现了Connection接口的对象被选中后,就能看到在一条连接线的两个端点上有两个矩形框。这些是名为ConnectionAdorner类型的Adorner,它能够在Connection对象被选中后自动显示出来。

    注意:一个ConnectorAdorner属于一个Connector,一个ConnectionAdorner属于一个Connection。

    WPFDiagramDesigner05

    两个端点上的矩形表示两个Thumb控件,他们是ConnectionAdorner对象的一部分,允许你能够修改已经建立的连接。

    WPFDiagramDesigner06

    E.g. 如果用户拖动连接线的一个端点,在另一个Connector上释放鼠标,用户能够修改已经建立的连接。

    注意:ConnectorAdorner和ConnectionAdorner的行为有些类似,但是他们对于Adorner类型的使用方式是不同的。

    怎样制作粘性连接

    Connector的默认布局定义在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的中心到DesignerCanvas的相对位置。由于Connector类实现了INotifyPropertyChanged接口,使得它能够对属性变化通知其他部分。作为WPF布局流水线的一部分,当一个DesignerItem的尺寸、位置发生变化时,Connector的LayoutUpdated事件会被自动触发。这时,它的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类型。当一个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();
    }
    }
    ...
    }

    这是Source部分的代码,Sink的设定非常类似。最终,事件处理程序会更新Connection的外观。

    自定义Connector的布局

    默认的的样式和Connector的数量可能不是我们所需的。下面这个例子是一个使用自定义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>

    WPFDiagramDesigner07

    问题在于,Connector只有在鼠标悬浮时才可见。如果你尝试使用左侧或右侧的Connector会出现问题。这个问题可以通过使用名为DesignerItem.ConnectorDecoratorTemplate的Attached Property来解决。通过自定义样式,下面的示例能够解决这个问题:

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

    WPFDiagramDesigner08

    这个方案提供了还不错的体验,不过需要复杂的布局,这使得这个方案不是所有情况下都适用。为此,在这儿提供了RelativePositionPanel来允许你将元素定位在面板的边缘。下面的示例通过设定RelativePosition属性将三个按钮置于RelativePositionPanel上,RelativePosition是一个Attached Property。

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


    这个面板在安排Connector的布局时非常好用:

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


    WPFDiagramDesigner09



    (全文完)


    以下为广告部分

    您部署的HTTPS网站安全吗?

    如果您想看下您的网站HTTPS部署的是否安全,花1分钟时间来 myssl.com 检测以下吧。让您的HTTPS网站变得更安全!

    SSL检测评估

    快速了解HTTPS网站安全情况。

    安全评级(A+、A、A-...)、行业合规检测、证书信息查看、证书链信息以及补完、服务器套件信息、证书兼容性检测等。

    SSL证书工具

    安装部署SSL证书变得更方便。

    SSL证书内容查看、SSL证书格式转换、CSR在线生成、SSL私钥加解密、CAA检测等。

    SSL漏洞检测

    让服务器远离SSL证书漏洞侵扰

    TLS ROBOT漏洞检测、心血漏洞检测、FREAK Attack漏洞检测、SSL Poodle漏洞检测、CCS注入漏洞检测。

  • 相关阅读:
    ios 正则表达式
    2016/2/26 jQuery的技术 1,安装 2,语法选择器$ 事件触发 3,常用jQuery函数
    2016/2/26 <marquee></marquee>实现多种滚动效果
    2016/2/25 onchange 应用
    2016/2/25 1、<表单验证<form></form> 2、正则表达式 3、事件
    2016/2/25 1, margin auto 垂直方向测试 无效 2,margin重叠 3,哪些是块状哪些是内联 4,display:block inline 导航栏把内联转块状最常见+ 扩展
    2016/2/25 html+css学习资源
    2016/2/24 css画三角形 border的上右下左的调整 以及内区域的无限变小 边界透明
    2016/2/24 1,css有几种引入方式 2,div除了可以声明id来控制,还可以声明什么控制? 3,如何让2个div,并排显示。4,清除浮动 clear:left / right / both
    2016/2/24 1,dotctype有几种? 2,了解html的发展历史
  • 原文地址:https://www.cnblogs.com/zhuqil/p/1628282.html
Copyright © 2020-2023  润新知