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注入漏洞检测。

posted @ 2009-12-24 22:39  麒麟  阅读(3407)  评论(6编辑  收藏  举报