[原文地址] 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异常,并收到下面的异常消息:
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 { get; set; }
bool IsGroup { get; set; }
}
这个观点是用已经实现了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网站变得更安全!
快速了解HTTPS网站安全情况。
安全评级(A+、A、A-...)、行业合规检测、证书信息查看、证书链信息以及补完、服务器套件信息、证书兼容性检测等。
安装部署SSL证书变得更方便。
SSL证书内容查看、SSL证书格式转换、CSR在线生成、SSL私钥加解密、CAA检测等。
让服务器远离SSL证书漏洞侵扰
TLS ROBOT漏洞检测、心血漏洞检测、FREAK Attack漏洞检测、SSL Poodle漏洞检测、CCS注入漏洞检测。