在前面三篇文章中我們介紹了如何給圖形設計器增加移動、選擇、改變大小及面板、縮略圖、框線選擇和工具箱和連接等功能,本篇是這個圖形設計器系列的最后一篇,將和大家一起來學習一下如何給圖形設計器增加分組、對齊、排序、序列化等功能。
WPF Diagram Designer - Part 4
分組:Group, Ungroup
由于WPF不允許一個對象作為多個其他元素的子對象存在,而當移動父對象時,模板也會Unload導致一些問題,所以在這個系列中對分組的實現方式是:當分組一組元素時,內部生成一個Group,這個Group內部其實也是一個DesignerItem對象,只是IsGroup=true而已,在分組時,內部的對象的ParentID都置為這個Group對象的Id
public interface IGroupable
{
Guid ID { get; }
Guid ParentID { get; set; }
bool IsGroup { get; set; }
}
{
Guid ID { get; }
Guid ParentID { get; set; }
bool IsGroup { get; set; }
}
執行分組時的代碼如下:
?
private void Group_Executed(object sender, ExecutedRoutedEventArgs e)
{
var items = from item in this.SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;
Rect rect = GetBoundingRectangle(items);
DesignerItem groupItem = new DesignerItem();
groupItem.IsGroup = true;
groupItem.Width = rect.Width;
groupItem.Height = rect.Height;
Canvas.SetLeft(groupItem, rect.Left);
Canvas.SetTop(groupItem, rect.Top);
Canvas groupCanvas = new Canvas();
groupItem.Content = groupCanvas;
Canvas.SetZIndex(groupItem, this.Children.Count);
this.Children.Add(groupItem);
foreach (DesignerItem item in items)
item.ParentID = groupItem.ID;
this.SelectionService.SelectItem(groupItem);
}
{
var items = from item in this.SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;
Rect rect = GetBoundingRectangle(items);
DesignerItem groupItem = new DesignerItem();
groupItem.IsGroup = true;
groupItem.Width = rect.Width;
groupItem.Height = rect.Height;
Canvas.SetLeft(groupItem, rect.Left);
Canvas.SetTop(groupItem, rect.Top);
Canvas groupCanvas = new Canvas();
groupItem.Content = groupCanvas;
Canvas.SetZIndex(groupItem, this.Children.Count);
this.Children.Add(groupItem);
foreach (DesignerItem item in items)
item.ParentID = groupItem.ID;
this.SelectionService.SelectItem(groupItem);
}
當我們選擇一個分組子對象時,設計器會選擇這個分組以及分組的所有子對象
?
?
internal void SelectItem(ISelectable item)
{
this.ClearSelection();
this.AddToSelection(item);
}
internal void AddToSelection(ISelectable item)
{
if (item is IGroupable)
{
List<IGroupable> groupItems = GetGroupMembers(item as IGroupable);
foreach (ISelectable groupItem in groupItems)
{
groupItem.IsSelected = true;
CurrentSelection.Add(groupItem);
}
}
else
{
item.IsSelected = true;
CurrentSelection.Add(item);
}
}
{
this.ClearSelection();
this.AddToSelection(item);
}
internal void AddToSelection(ISelectable item)
{
if (item is IGroupable)
{
List<IGroupable> groupItems = GetGroupMembers(item as IGroupable);
foreach (ISelectable groupItem in groupItems)
{
groupItem.IsSelected = true;
CurrentSelection.Add(groupItem);
}
}
else
{
item.IsSelected = true;
CurrentSelection.Add(item);
}
}
?
?
對齊:Align (Left, Right, Top, Bottom, Centered horizontal, Centered vertical)、Distribute (horizontal, vertical)
?
private void AlignLeft_Executed(object sender, ExecutedRoutedEventArgs e)
{
var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;
if (selectedItems.Count() > 1)
{
double left = Canvas.GetLeft(selectedItems.First());
foreach (DesignerItem item in selectedItems)
{
double delta = left - Canvas.GetLeft(item);
foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
{
Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
}
}
}
}
{
var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;
if (selectedItems.Count() > 1)
{
double left = Canvas.GetLeft(selectedItems.First());
foreach (DesignerItem item in selectedItems)
{
double delta = left - Canvas.GetLeft(item);
foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
{
Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
}
}
}
}
?
private void AlignHorizontalCenters_Executed(object sender, ExecutedRoutedEventArgs e)
{
var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;
if (selectedItems.Count() > 1)
{
double center = Canvas.GetLeft(selectedItems.First()) + selectedItems.First().Width / 2;
foreach (DesignerItem item in selectedItems)
{
double delta = center - (Canvas.GetLeft(item) + item.Width / 2);
foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
{
Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
}
}
}
}
{
var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
where item.ParentID == Guid.Empty
select item;
if (selectedItems.Count() > 1)
{
double center = Canvas.GetLeft(selectedItems.First()) + selectedItems.First().Width / 2;
foreach (DesignerItem item in selectedItems)
{
double delta = center - (Canvas.GetLeft(item) + item.Width / 2);
foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
{
Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
}
}
}
}
?
?
排序:Order (Bring forward, Bring to top, Send backward, Send to back)
?
private void BringForward_Executed(object sender, ExecutedRoutedEventArgs e)
{
List<UIElement> ordered = (from item in SelectionService.CurrentSelection
orderby Canvas.GetZIndex(item as UIElement) descending
select item as UIElement).ToList();
int count = this.Children.Count;
for (int i = 0; i < ordered.Count; i++)
{
int currentIndex = Canvas.GetZIndex(ordered[i]);
int newIndex = Math.Min(count - 1 - i, currentIndex + 1);
if (currentIndex != newIndex)
{
Canvas.SetZIndex(ordered[i], newIndex);
IEnumerable<UIElement> it = this.Children.OfType<UIElement>().Where(item => Canvas.GetZIndex(item) == newIndex);
foreach (UIElement elm in it)
{
if (elm != ordered[i])
{
Canvas.SetZIndex(elm, currentIndex);
break;
}
}
}
}
}
{
List<UIElement> ordered = (from item in SelectionService.CurrentSelection
orderby Canvas.GetZIndex(item as UIElement) descending
select item as UIElement).ToList();
int count = this.Children.Count;
for (int i = 0; i < ordered.Count; i++)
{
int currentIndex = Canvas.GetZIndex(ordered[i]);
int newIndex = Math.Min(count - 1 - i, currentIndex + 1);
if (currentIndex != newIndex)
{
Canvas.SetZIndex(ordered[i], newIndex);
IEnumerable<UIElement> it = this.Children.OfType<UIElement>().Where(item => Canvas.GetZIndex(item) == newIndex);
foreach (UIElement elm in it)
{
if (elm != ordered[i])
{
Canvas.SetZIndex(elm, currentIndex);
break;
}
}
}
}
}
序列化:Open, Save
使用XML保存,代碼如下:
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)
)
);
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)
)
);
讀取的時候需要建立Connection
?
private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
{
...
foreach (XElement connectionXML in connectionsXML)
{
Guid sourceID = new Guid(connectionXML.Element("SourceID").Value);
Guid sinkID = new Guid(connectionXML.Element("SinkID").Value);
String sourceConnectorName = connectionXML.Element("SourceConnectorName").Value;
String sinkConnectorName = connectionXML.Element("SinkConnectorName").Value;
Connector sourceConnector = GetConnector(sourceID, sourceConnectorName);
Connector sinkConnector = GetConnector(sinkID, sinkConnectorName);
Connection connection = new Connection(sourceConnector, sinkConnector);
Canvas.SetZIndex(connection, Int32.Parse(connectionXML.Element("zIndex").Value));
this.Children.Add(connection);
}
}
{
...
foreach (XElement connectionXML in connectionsXML)
{
Guid sourceID = new Guid(connectionXML.Element("SourceID").Value);
Guid sinkID = new Guid(connectionXML.Element("SinkID").Value);
String sourceConnectorName = connectionXML.Element("SourceConnectorName").Value;
String sinkConnectorName = connectionXML.Element("SinkConnectorName").Value;
Connector sourceConnector = GetConnector(sourceID, sourceConnectorName);
Connector sinkConnector = GetConnector(sinkID, sinkConnectorName);
Connection connection = new Connection(sourceConnector, sinkConnector);
Canvas.SetZIndex(connection, Int32.Parse(connectionXML.Element("zIndex").Value));
this.Children.Add(connection);
}
}
常用功能:Cut, Copy, Paste, Delete,Print
?
private void Cut_Executed(object sender, ExecutedRoutedEventArgs e)
{
CopyCurrentSelection();
DeleteCurrentSelection();
}
{
CopyCurrentSelection();
DeleteCurrentSelection();
}
?
private void Print_Executed(object sender, ExecutedRoutedEventArgs e)
{
SelectionService.ClearSelection();
PrintDialog printDialog = new PrintDialog();
if (true == printDialog.ShowDialog())
{
printDialog.PrintVisual(this, "WPF Diagram");
}
}
{
SelectionService.ClearSelection();
PrintDialog printDialog = new PrintDialog();
if (true == printDialog.ShowDialog())
{
printDialog.PrintVisual(this, "WPF Diagram");
}
}
?
?本文轉自 jingen_zhou 51CTO博客,原文鏈接:http://blog.51cto.com/zhoujg/517448,如需轉載請自行聯系原作者