在前面三篇文章中我们介绍了如何给图形设计器增加移动、选择、改变大小及面板、缩略图、框线选择和工具箱和连接等功能,本篇是这个图形设计器系列的最后一篇,将和大家一起来学习一下如何给图形设计器增加分组、对齐、排序、序列化等功能。
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,如需转载请自行联系原作者