📄 lithiumcontrol.cs
字号:
Pickup(shape.childNodes[k]);
}
}
#endregion
/// <summary>
/// Resets the hovering status of the control, i.e. the hoverEntity is set to null.
/// </summary>
private void HoverNone()
{
if(hoveredEntity!=null)
{
hoveredEntity.hovered = false;
hoveredEntity.Invalidate();
}
hoveredEntity = null;
}
/// <summary>
/// Collapses the whole diagram
/// </summary>
public void CollapseAll()
{
this.Root.Collapse(true);
}
/// <summary>
/// Deletes the currently selected shape
/// </summary>
public void Delete()
{
if(selectedEntity==null) return;
ShapeBase sh = selectedEntity as ShapeBase;
if(sh!=null)
{
if(sh.IsRoot) return; //cannot delete the root
//delete the node from the parent's children
sh.parentNode.childNodes.Remove(sh);
//delete everything underneath the node
DeleteVisitor visitor = new DeleteVisitor(this.graphAbstract);
visitor.OnDelete+=new ShapeData(visitor_OnDelete);
graphAbstract.DepthFirstTraversal(visitor,sh);
DrawTree();
}
//Invalidate();
}
/// <summary>
/// Expands the whole diagram
/// </summary>
public void ExpandAll()
{
IVisitor expander = new ExpanderVisitor();
this.graphAbstract.DepthFirstTraversal(expander);
DrawTree();
}
/// <summary>
/// Saves the diagram to file in XML format (XML serialization)
/// </summary>
/// <param name="filePath"></param>
public void SaveGraphAs(string filePath) { XmlTextWriter tw = new XmlTextWriter(filePath,System.Text.Encoding.Unicode);
GraphSerializer g = new GraphSerializer(this);
g.Serialize(tw);
tw.Close(); }
/// <summary>
/// Opens a diagram which was saved to XML previously (XML deserialization)
/// </summary>
/// <param name="filePath"></param>
public void OpenGraph(string filePath)
{
XmlTextReader reader = new XmlTextReader(filePath);
GraphSerializer ser = new GraphSerializer(this);
graphAbstract = ser.Deserialize(reader) as GraphAbstract;
reader.Close();
DrawTree();
Invalidate();
}
#region Layout algorithm
int marginLeft = 10;
/// <summary>
/// Generic entry point to layout the diagram on the canvas.
/// The default LayoutDirection is vertical. If you wish to layout the tree in a certain
/// direction you need to specify this property first. Also, the direction is global, you cannot have
/// different parts being drawn in different ways though it can be implemented.
///
/// </summary>
public void DrawTree()
{
if(!layoutEnabled) return;
Point p = Point.Empty; //the shift vector difference between the original and the moved root
try
{
//start the recursion
//the layout will move the root but it's reset to its original position
switch(layoutDirection)
{
case TreeDirection.Vertical:
p = new Point(graphAbstract.Root.X, graphAbstract.Root.Y);
VerticalDrawTree(graphAbstract.Root,false,marginLeft,this.graphAbstract.Root.Y);
p = new Point(-graphAbstract.Root.X+p.X, -graphAbstract.Root.Y+ p.Y);
MoveDiagram(p);
break;
case TreeDirection.Horizontal:
p = new Point(graphAbstract.Root.X, graphAbstract.Root.Y);
HorizontalDrawTree(graphAbstract.Root,false,marginLeft,10);
p = new Point(-graphAbstract.Root.X+p.X, -graphAbstract.Root.Y+ p.Y);
MoveDiagram(p);
break;
}
int maxY = 0;
foreach (ShapeBase shape in Shapes)
{
if (shape.ShapeColor == Color.Ivory)
{
if (shape.Visible)
{
if (shape.Y > maxY)
{
maxY = shape.Y;
}
}
}
}
foreach (ShapeBase shape in Shapes)
{
if (shape.ShapeColor == Color.Ivory)
{
if (shape.Visible)
{
shape.Move(new Point(0, maxY - shape.Y));
}
}
}
CalculateScrollBars();
Invalidate();
}
catch(Exception exc)
{
Trace.WriteLine(exc.Message);
}
}
private void CalculateScrollBars()
{
Point minPoint = new Point(int.MaxValue,int.MaxValue);
Size maxSize = new Size(0,0);
foreach(ShapeBase shape in Shapes)
{
if (shape.Visible)
{
if (shape.X + shape.Width > maxSize.Width)
{
maxSize.Width = shape.X + shape.Width;
}
if (shape.Y + shape.Height > maxSize.Height)
{
maxSize.Height = shape.Y + shape.Height;
}
if (shape.X < minPoint.X)
{
minPoint.X = shape.X;
}
if (shape.Y < minPoint.Y)
{
minPoint.Y = shape.Y;
}
}
}
MoveDiagram(new Point(50 - minPoint.X, 50 - minPoint.Y));
maxSize.Width = maxSize.Width - minPoint.X + 100;
maxSize.Height = maxSize.Height - minPoint.Y + 100;
this.AutoScrollMinSize = maxSize;
}
/// <summary>
/// Positions everything underneath the node and returns the total width of the kids
/// </summary>
/// <param name="containerNode"></param>
/// <param name="first"></param>
/// <param name="shiftLeft"></param>
/// <param name="shiftTop"></param>
/// <returns></returns>
private int VerticalDrawTree(ShapeBase containerNode, bool first, int shiftLeft, int shiftTop)
{
bool isFirst = false;
bool isParent = containerNode.childNodes.Count>0? true: false;
int childrenWidth = 0;
int thisX, thisY;
int returned = 0;
int verticalDelta = branchHeight ; //the applied vertical shift of the child depends on the Height of the containerNode
#region Children width
for(int i =0; i<containerNode.childNodes.Count; i++)
{
//determine the width of the label
if(i==0)
isFirst = true;
else
isFirst = false;
if(containerNode.childNodes[i].visible)
{
if((branchHeight - containerNode.Height) < 30) //if too close to the child, shift it with 40 units
verticalDelta = containerNode.Height + 40;
returned = VerticalDrawTree(containerNode.childNodes[i], isFirst, shiftLeft + childrenWidth, shiftTop + verticalDelta );
childrenWidth += returned;
}
}
if(childrenWidth>0 && containerNode.expanded)
childrenWidth=Math.Max(Convert.ToInt32(childrenWidth + (containerNode.Width-childrenWidth)/2), childrenWidth); //in case the length of the containerNode is bigger than the total length of the children
#endregion
if(childrenWidth==0) //there are no children; this is the branch end
childrenWidth = containerNode.Width+wordSpacing;
#region Positioning
thisY = shiftTop;
if(containerNode.childNodes.Count>0 && containerNode.expanded)
{
if(containerNode.childNodes.Count==1)
{
thisX = Convert.ToInt32(containerNode.childNodes[0].X+containerNode.childNodes[0].Width/2 - containerNode.Width/2);
}
else
{
float firstChild = containerNode.childNodes[0].Left+ containerNode.childNodes[0].Width/2;
float lastChild = containerNode.childNodes[containerNode.childNodes.Count-1].Left + containerNode.childNodes[containerNode.childNodes.Count-1].Width/2;
//the following max in case the containerNode is larger than the childrenWidth
thisX = Convert.ToInt32(Math.Max(firstChild + (lastChild -firstChild - containerNode.Width)/2, firstChild));
}
}
else
{
thisX = shiftLeft;
}
containerNode.rectangle.X = thisX;
containerNode.rectangle.Y = thisY;
#endregion
return childrenWidth;
}
/// <summary>
/// Horizontal layout algorithm
/// </summary>
/// <param name="containerNode"></param>
/// <param name="first"></param>
/// <param name="shiftLeft"></param>
/// <param name="shiftTop"></param>
/// <returns></returns>
private int HorizontalDrawTree(ShapeBase containerNode, bool first, int shiftLeft, int shiftTop)
{
bool isFirst = false;
bool isParent = containerNode.childNodes.Count>0? true: false;
int childrenHeight = 0;
int thisX, thisY;
int returned = 0;
int horizontalDelta = branchHeight;
#region Children width
for(int i =0; i<containerNode.childNodes.Count; i++)
{
//determine the width of the label
if(i==0)
isFirst = true;
else
isFirst = false;
if(containerNode.childNodes[i].visible)
{
if((branchHeight - containerNode.Width) < 30) //if too close to the child, shift it with 40 units
horizontalDelta = containerNode.Width + 40;
returned = HorizontalDrawTree(containerNode.childNodes[i], isFirst, shiftLeft + horizontalDelta , shiftTop + childrenHeight );
childrenHeight += returned;
}
}
#endregion
if(childrenHeight==0) //there are no children; this is the branch end
childrenHeight = containerNode.Height+wordSpacing;
#region Positioning
thisX = shiftLeft;
if(containerNode.childNodes.Count>0 && containerNode.expanded)
{
int firstChild = containerNode.childNodes[0].Y;
int lastChild = containerNode.childNodes[containerNode.childNodes.Count-1].Y;
thisY = Convert.ToInt32(firstChild + (lastChild - firstChild)/2);
}
else
{
thisY = Convert.ToInt32(shiftTop);
}
containerNode.rectangle.X = thisX;
containerNode.rectangle.Y = thisY;
#endregion
return childrenHeight;
}
private int Measure(string text)
{
Graphics g = Graphics.FromHwnd(this.Handle);
return Size.Round(g.MeasureString(text,Font)).Width +37;
}
/// <summary>
/// Resizes the shape to fit the text
/// </summary>
/// <param name="shape"></param>
public void Fit(ShapeBase shape)
{
Graphics g = Graphics.FromHwnd(this.Handle);
Size s = Size.Round(g.MeasureString(shape.Text,Font));
shape.Width =s.Width +20;
shape.Height = s.Height+8;
}
private void FitAll()
{
foreach(ShapeBase shape in graphAbstract.Shapes)
Fit(shape);
Invalidate();
}
/// <summary>
/// When the font of the control is changed all shapes get
/// the new font assigned
/// </summary>
/// <param name="e"></param>
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged (e);
foreach(ShapeBase shape in graphAbstract.Shapes)
shape.Font = Font;
}
/// <summary>
/// Adds a child node to the currently selected one
/// </summary>
public void AddChild()
{
if(selectedEntity==null) return;
ShapeBase sh = selectedEntity as ShapeBase;
if(sh!=null)
{
sh.AddChild("New node");
DrawTree();
}
}
/// <summary>
/// DFT of the diagram with the given visitor, starting from the given shape
/// </summary>
/// <param name="visitor"></param>
/// <param name="shape"></param>
public void DepthFirstTraversal(IVisitor visitor, ShapeBase shape)
{
graphAbstract.DepthFirstTraversal(visitor, shape);
}
/// <summary>
/// DFT of the diagram with the given visitor, starting from the root
/// </summary>
/// <param name="visitor"></param>
public void DepthFirstTraversal(IVisitor visitor)
{
graphAbstract.DepthFirstTraversal(visitor);
}
/// <summary>
/// BFT of the diagram with the given visitor, starting from the root
/// </summary>
/// <param name="visitor"></param>
public void BreadthFirstTraversal(IVisitor visitor)
{
graphAbstract.BreadthFirstTraversal(visitor);
}
/// <summary>
/// BFT of the diagram with the given visitor, starting from the given shape
/// </summary>
/// <param name="visitor"></param>
/// <param name="shape"></param>
public void BreadthFirstTraversal(IVisitor visitor, ShapeBase shape)
{
graphAbstract.BreadthFirstTraversal(visitor, shape);
}
#endregion
#endregion
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -