文章目錄
- Save
- Open
- Copy, Paste, Delete, Cut
- Align, Distribute
- Order
[原文地址] 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 { 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流程設計器和表單設計器的朋友有所協助。(紅色譯者注)