• WPF中的图表设计器 – 4 (译)


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

    • Part 1 - 在Canvas中移动、缩放或者旋转任何类型的对象
    • Part 2 - Toolbox, drag & drop, rubberband selection
    • Part 3 - Connecting items

    介绍

    在这篇文章中,我添加下面的命令:

    • Open, Save
    • Cut, Copy, Paste, Delete
    • Print
    • Group, Ungroup
    • Align (Left, Right, Top, Bottom, Centered horizontal, Centered vertical)
    • Distribute (horizontal, vertical)
    • Order (Bring forward, Bring to top, Send backward, Send to back)

       注意:我将仅仅支持.NET 3.5平台下面的Visual Studio 8.0!

    命令

       我将直接使用WPF命令,就像在WPF SDK文档中描述一样,无需额外的基础设施。

    分组

       我的第一个分组的方法是将一个DesignerItem对象作为一个分组的容器使用。因此,创建一个DesignerItem类的新实例对象,这个实例对象的content是一个Canvas对象。在这个canvas中,我计划放置进行分组的DesignerItem。不过,在我把item放在用于分组的canvas上之前,我必须从DesignerCanvas移除他们,这因为WPF中的元素不能同时是两个元素的子元素。否者,如果您去尝试,您将得到InvalidOperationException异常,并收到下面的异常消息:

    "Specified element is already the logical child of another element. 
    Disconnect it first."

       所以,我们从DesignerCanvas上移除Item ,然后将它添加到GroupCanvas上。现在有趣的是理解WPF幕后所做的工作:当我从画布上移除的Item时,Item的模板被卸下,当我把它添加到GroupCanvas上时,一个新的模板又会被加载。现在,你还记得上一篇文章中我如何连接DesignerItems会的吗?在那里,我通过Connector去实现连接Item,Connector是被连接的DesignerItem的模板的一部分。一旦我们从DesignerCanvas删除Item,它的模版也将消失。你看到问题吗?我是通过他们的模板连接DesignerItem的,所以DesignerItem本身根本没有存在有关Connection的信息。所有Connection的相关信息是与DesignerItem的模板孤立的。

        想象一下,一个数据库关系图中,DesignerItem的content是一个数据库表。该表将永远不会认可与其他表的关系。一个解决办法是建立一个信息隧道,信息从DesignerItem的模板传到DesignerItem,再传到table。 更好的解决办法是重新设计应用程序和划整为零,如划分为:

    • Template (view)
    • Designer item (view model)
    • Database table (model)

         我在这篇文章中间将不会重新设计代码。相反,直到文章的结尾,我将使用'view-only-approach'。使用越难,好的解决方法就越有希望。(我将会在以后的文章中涉及一些支持模型设计的文章)。

         所以让我们继续,另外一种对DesignerItem分组方法,是使用下面的接口:

      public interface IGroupable
      {
      Guid ID { 
      get; }
      Guid ParentID { 
      getset; }
      bool IsGroup { getset; }
      }

         这个观点是用已经实现了IGroupable接口的DesignerItem类,成为分组结构的一部分。它如下工作:

      • 创建一个新的DesignerItem对象,它有一个唯一的ID和一个设置为true的IsGroup属性。
      • 对于每一个组成员,设置他的ParentID为父亲group的ID。

         这个非常简单,当真正地修改Item(Select, Move, Resize, Copy, ...)的时候,它才真正的起作用。对于每个操作,我们都要考虑item的分组的状态,听起来有很多工作要做。但是使用了ling,就没那么难了。因此,我们将大部分工作放在SelectionService中去实现。

         注意:Connection类没有实现IGroupable接口。所以它不是分组的直接部分。但间接地:一个connection 总是附加到一个item上。这使我可以灵活地重连/连接item。无论他们是否是一个分组的成员。

      Save

         为了保存一个图表,我选择使用XML和XAML的组合。对于DesignerItem相关的数据,我使用XML,content将序列化为XAML。在这里,请注意序列化DesignerItem的content为XAML仅仅保留视觉效果。因此,这是短期内唯一的解决办法。要创建XML文件,我使用LINQ。由于这是我第一次使用LINQ来做,使用它不要指望它是必要的“正确”的方法。

         下面是一个例子,我是如何序列DesignerItem: 

       XElement serializedItems = new XElement("DesignerItems",
                                from item 
      in designerItems
                                let contentXaml 
      = XamlWriter.Save(((DesignerItem)item).Content)
                                select 
      new XElement("DesignerItem",
                                            
      new XElement("Left", Canvas.GetLeft(item)),
                                            
      new XElement("Top", Canvas.GetTop(item)),
                                            
      new XElement("Width", item.Width),
                                            
      new XElement("Height", item.Height),
                                            
      new XElement("ID", item.ID),
                                            
      new XElement("zIndex", Canvas.GetZIndex(item)),
                                            
      new XElement("IsGroup", item.IsGroup),
                                            
      new XElement("ParentID", item.ParentID),
                                            
      new XElement("Content", contentXaml)
                                            )
                                 );

       
       

        在让关键字允许您在一个变量中存储在一个子表达式的结果,这个变量可以在随后的表达式中使用。在这里,我使用此功能将序列化的content保存在contentXaml变量,我用下面几行代码。最后,我将使用XElement类的Save方法来存储基本XML树:

     XElement.Save(fileName)

    Open

        当我们从XML文件加载一个图表的时候,我们必须从加载DesignerItem开始,因为我们需要他们的connector来创建connection。我们了解到,connector是该item模板的一部分。在我们继续以前,DesignerItem必须加载他的模板。幸运的是,控件的类提供了一个ApplyTemplate()方法。它强迫WPF布局系统加载控件模板,以便它的部分能被引用。

        在前面的一篇文章中,我提供一个自定义ConnectorDecorator模板的机制,它允许你在DesignerItem的周围自由地放置connector 。这种解决办法,在DesignerItem的Loaded事件之后, 应用自定义模板。该DesignerItem在屏幕上可见之前,而不会激发该事件。在该命令已经结束之前,屏幕不能重绘。因此,唯一的方法是将在打开命令中明确设置ConnectorDecorator自定义模板,看到SetConnectorDecoratorTemplate(item)的方法。

     注意:当定义定制的connector时,必须设置的x:Name属性。一个connection使用名字来确定其来源和接收的connector。

     <s:Connector x:Name="Left" Orientation="Left" 
        VerticalAlignment
    ="Center" HorizontalAlignment="Left"/>

    Copy, Paste, Delete, Cut

       复制和粘贴命令的工作类似于打开和保存命令,但它们只适用于所选的item ,他们读写序列化的content到剪贴板。 删除命令只是从designer canvas所有的子集中删除选定item,最后剪切命令是一个复制和删除命令的组合。

    Align, Distribute

       这些命令,没有太多的话要说,首先,除了在Align中第一个被选中的item是参考item (也称为主选择item)。这仅仅当你使用 LeftMouseButton + Ctrl, 或者LeftMouseButton + Shift选择item,而不是你去用rubberband选择。

    Order

       这个Panel类提供了一个名字为ZIndex的附加的的属性。它定义了z-plane子元素的显示的顺序。所以我们只需要更改该属性将item向前或向后。

         WPF中的图表设计器一共有四篇文章,前面三篇已经由WizRay翻译,你也可以在我的博客上找到,这是第四篇。希望这些对想实现自己WPF流程设计器和表单设计器的朋友有所帮助。(红色译者注)



    (全文完)


    以下为广告部分

    您部署的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注入漏洞检测。

  • 相关阅读:
    Unity WebGL MoonSharp崩溃问题
    UISprite(NGUI)扩展 图片镂空
    自动化交易机器人Beta猪
    如何成为一个真正在路上的Linuxer
    课堂里学不到的C与C++那些事(一)
    Android ART运行时与Dalvik虚拟机
    用Dockerfile构建docker image
    论docker中 CMD 与 ENTRYPOINT 的区别
    sshfs远程文件系统挂载
    docker镜像与容器存储结构分析
  • 原文地址:https://www.cnblogs.com/zhuqil/p/1631726.html
Copyright © 2020-2023  润新知