📄 lithiumcontrol.cs
字号:
using System;
using System.Diagnostics;
using System.Xml;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.IO;
using System.Collections;
namespace Netron.Lithium
{
/// <summary>
/// Netron's 'Lithium' tree control
/// </summary>
public class LithiumControl : ScrollableControl
{
#region Events
/// <summary>
/// notifies the host to show the properties usually in the property grid
/// </summary>
public event ShowProps OnShowProps;
/// <summary>
/// occurs when a new node is added to the diagram
/// </summary>
public event ShapeData OnNewNode;
/// <summary>
/// occurs when certain objects send info out
/// </summary>
public event Messager OnInfo;
/// <summary>
/// occurs when a shape is deleted
/// </summary>
public event ShapeData OnDeleteNode;
#endregion
#region Fields
/// <summary>
/// the current layout direction
/// </summary>
protected TreeDirection layoutDirection = TreeDirection.Vertical;
/// <summary>
/// the space between the nodes
/// </summary>
protected int wordSpacing = 20;
/// <summary>
/// the height between branches
/// </summary>
protected int branchHeight = 60;
/// <summary>
/// whether the tree layout algorithm will do its work by default
/// </summary>
protected bool layoutEnabled = true;
/// <summary>
/// the default name when the root is added
/// </summary>
protected readonly string defaultRootName = "Root";
/// <summary>
/// the abstract representation of the graph
/// </summary>
protected internal GraphAbstract graphAbstract;
/// <summary>
/// the entity hovered by the mouse
/// </summary>
protected Entity hoveredEntity;
/// <summary>
/// the unique entity currently selected
/// </summary>
protected Entity selectedEntity;
/// <summary>
/// whether we are tracking, i.e. moving something around
/// </summary>
protected bool tracking = false;
/// <summary>
/// just a reference point for the OnMouseDown event
/// </summary>
protected Point refp;
/// <summary>
/// the context menu of the control
/// </summary>
protected ContextMenu menu;
/// <summary>
/// A simple, general purpose random generator
/// </summary>
protected Random rnd;
/// <summary>
/// simple proxy for the propsgrid of the control
/// </summary>
protected Proxy proxy;
/// <summary>
/// whether the diagram is moved globally
/// </summary>
protected bool globalMove = false;
/// <summary>
/// just the default gridsize used in the paint-background method
/// </summary>
protected Size gridSize = new Size(10,10);
/// <summary>
/// the new but volatile connection
/// </summary>
protected internal Connection neoCon = null;
/// <summary>
/// memory of a connection if the volatile does not end up to a solid connection
/// </summary>
protected ShapeBase memChild = null, memParent = null;
/// <summary>
/// the type of connection
/// </summary>
protected ConnectionType connectionType = ConnectionType.Default;
#endregion
#region Properties
/// <summary>
/// Gets or sets the type of connection drawn
/// </summary>
public ConnectionType ConnectionType
{
get{return connectionType;}
set{connectionType = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the direction the tree-layout expands the tree
/// </summary>
public TreeDirection LayoutDirection
{
get{return layoutDirection;}
set{layoutDirection = value;
//update in case it has changed
switch(value)
{
case TreeDirection.Horizontal:
branchHeight = 120;
wordSpacing = 20;
break;
case TreeDirection.Vertical:
branchHeight = 70;
wordSpacing = 20;
break;
}
DrawTree();
}
}
/// <summary>
/// Gets or sets whether the tree layout algorithm will do its work by default
/// </summary>
public bool LayoutEnabled
{
get{return layoutEnabled ;}
set{
layoutEnabled = value;
if(value) //if set to true, let's rectify what eventually has been distorted by the user
DrawTree();
}
}
/// <summary>
/// Gets or sets the shape collection
/// </summary>
[Browsable(false)]
public ShapeCollection Shapes
{
get{return graphAbstract.Shapes;}
set{graphAbstract.Shapes = value;}
}
/// <summary>
/// Gets or sets the connection collection
/// </summary>
[Browsable(false)]
public ConnectionCollection Connections
{
get{return graphAbstract.Connections;}
set{graphAbstract.Connections = value;}
}
/// <summary>
/// Gets the root of the diagram
/// </summary>
[Browsable(false)]
public ShapeBase Root
{
get{return graphAbstract.Root;}
}
/// <summary>
/// Gets or sets the height between the tree branches
/// </summary>
[Browsable(true)]
public int BranchHeight
{
get{return branchHeight;}
set{
branchHeight = value;
DrawTree();
}
}
/// <summary>
/// Gets or sets the spacing between the nodes
/// </summary>
[Browsable(true)]
public int WordSpacing
{
get{return wordSpacing;}
set
{
wordSpacing = value;
DrawTree();
}
}
#endregion
#region Constructor
/// <summary>
/// Default ctor
/// </summary>
public LithiumControl()
{
//double-buffering
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
//init the abstract
graphAbstract = new GraphAbstract();
graphAbstract.Shapes.OnShapeAdded+=new ShapeData(OnShapeAdded);
AddRoot(defaultRootName);
//default menu, can be overwritten in the design of the application
menu = new ContextMenu();
BuildMenu();
this.ContextMenu = menu;
//init the randomizer
rnd = new Random();
//init the proxy
proxy = new Proxy(this);
//allow scrolling
this.AutoScroll=true; this.HScroll=true; this.VScroll=true;
}
#endregion
#region Methods
private void visitor_OnDelete(ShapeBase shape)
{
if(OnDeleteNode!=null)
OnDeleteNode(shape);
}
/// <summary>
/// Passes the event from the abstracts shape collection to the outside.
/// Having the event in the GraphAbstract being raised centralizes it,
/// otherwise the event should be raise in various places
/// </summary>
/// <param name="shape"></param>
private void OnShapeAdded(ShapeBase shape)
{
if(this.OnNewNode!=null)
OnNewNode(shape);
}
/// <summary>
/// Builds the context menu
/// </summary>
private void BuildMenu()
{
MenuItem mnuDelete = new MenuItem("Delete",new EventHandler(OnDelete));
menu.MenuItems.Add(mnuDelete);
MenuItem mnuProps = new MenuItem("Properties", new EventHandler(OnProps));
menu.MenuItems.Add(mnuProps);
MenuItem mnuDash = new MenuItem("-");
menu.MenuItems.Add(mnuDash);
MenuItem mnuShapes = new MenuItem("Change to");
menu.MenuItems.Add(mnuShapes);
MenuItem mnuRecShape = new MenuItem("Rectangular shape", new EventHandler(OnRecShape));
mnuShapes.MenuItems.Add(mnuRecShape);
MenuItem mnuOvalShape = new MenuItem("Oval shape", new EventHandler(OnOvalShape));
mnuShapes.MenuItems.Add(mnuOvalShape);
MenuItem mnuDash2 = new MenuItem("-");
menu.MenuItems.Add(mnuDash2);
MenuItem mnuTLShape = new MenuItem("Add Text label", new EventHandler(OnTextLabelShape));
menu.MenuItems.Add(mnuTLShape);
}
#region Menu handlers
/// <summary>
/// Deletes the currently selected object from the canvas
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnDelete(object sender, EventArgs e)
{
if(selectedEntity!=null)
{
if(typeof(ShapeBase).IsInstanceOfType(selectedEntity))
{
this.Shapes.Remove(selectedEntity as ShapeBase);
this.Invalidate();
}
else
if(typeof(Connection).IsInstanceOfType(selectedEntity))
{
this.Connections.Remove(selectedEntity as Connection);
this.Invalidate();
}
}
}
/// <summary>
/// Asks the host to show the props
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnProps(object sender, EventArgs e)
{
object thing;
if(this.selectedEntity==null)
thing = this.proxy;
else
thing =selectedEntity;
if(this.OnShowProps!=null)
OnShowProps(thing);
}
private void OnRecShape(object sender, EventArgs e)
{
throw new NotImplementedException("Sorry, not implemented yet");
}
private void OnOvalShape(object sender, EventArgs e)
{
throw new NotImplementedException("Sorry, not implemented yet");
}
private void OnTextLabelShape(object sender, EventArgs e)
{
AddShape(ShapeTypes.TextLabel,refp);
}
#endregion
/// <summary>
/// Paints the control
/// </summary>
/// <remarks>
/// If you switch the painting order of Connections and shapes the connection line
/// will be underneath/above the shape
/// </remarks>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
//use the best quality, with a performance penalty
g.SmoothingMode= System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
int scrollPositionX = this.AutoScrollPosition.X;
int scrollPositionY = this.AutoScrollPosition.Y;
g.TranslateTransform(scrollPositionX, scrollPositionY);
//zoom
//g.ScaleTransform(0.1f,0.1f);
//draw the Connections
for(int k=0; k<Connections.Count; k++)
{
Connections[k].Paint(g);
}
if(neoCon!=null) neoCon.Paint(g);
//loop over the shapes and draw
for(int k=0; k<Shapes.Count; k++)
{
if(Shapes[k].visible)
Shapes[k].Paint(g);
}
}
/// <summary>
/// Paints the background
/// </summary>
/// <param name="e"></param>
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground (e);
// Graphics g = e.Graphics;
//
// g.DrawString("Lithium Tree Layout Control [version " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + "]",Font,Brushes.SlateGray,new Point(20,10));
// g.DrawString("<Shift>: move the diagram",Font,Brushes.SlateGray,new Point(20,30));
// g.DrawString("<Ctrl>: move a node to a new parent",Font,Brushes.SlateGray,new Point(20,40));
// g.DrawString("<Alt>: add a new child node",Font,Brushes.SlateGray,new Point(20,50));
}
#region Add random node
/// <summary>
/// Adds a random node
/// </summary>
public void AddRandomNode()
{
AddRandomNode("Random");
}
/// <summary>
/// Adds a random node to the diagram
/// </summary>
/// <param name="newName">the text of the newly added shape</param>
public void AddRandomNode(string newName)
{
//Random rnd = new Random();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -