Skip to content

Commit 00124c6

Browse files
committed
Grid is now rendered in background
ShowElementMenu event can now be used to show context sensitive menus for nodes, node elements etc. or to show a menu when nothing is clicked on
1 parent e265097 commit 00124c6

File tree

5 files changed

+227
-15
lines changed

5 files changed

+227
-15
lines changed

Graph/GraphControl.cs

Lines changed: 153 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
namespace Graph
3636
{
37+
public delegate bool AcceptElement(IElement element);
38+
3739
public partial class GraphControl : Control
3840
{
3941
#region Constructor
@@ -49,11 +51,30 @@ public GraphControl()
4951
public event EventHandler<AcceptNodeEventArgs> NodeAdded;
5052
public event EventHandler<AcceptNodeEventArgs> NodeRemoving;
5153
public event EventHandler<NodeEventArgs> NodeRemoved;
54+
public event EventHandler<AcceptElementLocationEventArgs> ShowElementMenu;
5255
public event EventHandler<AcceptNodeConnectionEventArgs> ConnectionAdding;
5356
public event EventHandler<AcceptNodeConnectionEventArgs> ConnectionAdded;
5457
public event EventHandler<AcceptNodeConnectionEventArgs> ConnectionRemoving;
5558
public event EventHandler<NodeConnectionEventArgs> ConnectionRemoved;
5659

60+
#region Grid
61+
public bool ShowGrid = true;
62+
public float GridStep = 16.0f;
63+
private Color internalGridColor = Color.LightGray;
64+
private Pen GridPen = new Pen(Color.LightGray);
65+
public Color GridColor
66+
{
67+
get { return internalGridColor; }
68+
set
69+
{
70+
if (internalGridColor == value)
71+
return;
72+
73+
internalGridColor = value;
74+
GridPen = new Pen(internalGridColor);
75+
}
76+
}
77+
#endregion
5778

5879
#region DragElement
5980
IElement internalDragElement;
@@ -762,6 +783,62 @@ IElement FindElementAt(PointF location)
762783
}
763784
#endregion
764785

786+
#region FindElementAt
787+
IElement FindElementAt(PointF location, AcceptElement acceptElement)
788+
{
789+
foreach (var node in graphNodes)
790+
{
791+
var inputConnector = FindInputConnectorAt(node, location);
792+
if (inputConnector != null && acceptElement(inputConnector))
793+
return inputConnector;
794+
795+
var outputConnector = FindOutputConnectorAt(node, location);
796+
if (outputConnector != null && acceptElement(outputConnector))
797+
return outputConnector;
798+
799+
if (node.bounds.Contains(location))
800+
{
801+
var item = FindNodeItemAt(node, location);
802+
if (item != null && acceptElement(item))
803+
return item;
804+
if (acceptElement(node))
805+
return node;
806+
else
807+
return null;
808+
}
809+
}
810+
811+
var skipConnections = new HashSet<NodeConnection>();
812+
var foundConnections = new List<NodeConnection>();
813+
foreach (var node in graphNodes)
814+
{
815+
foreach (var connection in node.connections)
816+
{
817+
if (skipConnections.Add(connection)) // if we can add it, we haven't checked it yet
818+
{
819+
if (connection.bounds.Contains(location))
820+
foundConnections.Insert(0, connection);
821+
}
822+
}
823+
}
824+
foreach (var connection in foundConnections)
825+
{
826+
if (connection.textBounds.Contains(location) && acceptElement(connection))
827+
return connection;
828+
}
829+
foreach (var connection in foundConnections)
830+
{
831+
using (var region = GraphRenderer.GetConnectionRegion(connection))
832+
{
833+
if (region.IsVisible(location) && acceptElement(connection))
834+
return connection;
835+
}
836+
}
837+
838+
return null;
839+
}
840+
#endregion
841+
765842
#region GetTransformedLocation
766843
PointF GetTransformedLocation()
767844
{
@@ -800,23 +877,24 @@ protected override void OnPaint(PaintEventArgs e)
800877

801878
if (e.Graphics == null)
802879
return;
880+
803881

804-
e.Graphics.Clear(Color.White);
805-
806-
if (this.graphNodes.Count == 0)
807-
return;
808-
809-
UpdateMatrices();
810-
e.Graphics.PageUnit = GraphicsUnit.Pixel;
882+
e.Graphics.PageUnit = GraphicsUnit.Pixel;
811883
e.Graphics.CompositingQuality = CompositingQuality.GammaCorrected;
812-
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
813-
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
814884
e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
815885
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
816-
817-
886+
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
887+
888+
UpdateMatrices();
818889
e.Graphics.Transform = transformation;
819890

891+
OnDrawBackground(e);
892+
893+
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
894+
895+
if (this.graphNodes.Count == 0)
896+
return;
897+
820898

821899
var transformed_location = GetTransformedLocation();
822900
if (command == CommandMode.MarqueSelection)
@@ -857,6 +935,41 @@ protected override void OnPaint(PaintEventArgs e)
857935
}
858936
#endregion
859937

938+
#region OnDrawBackground
939+
virtual protected void OnDrawBackground(PaintEventArgs e)
940+
{
941+
e.Graphics.Clear(Color.White);
942+
943+
if (!ShowGrid)
944+
return;
945+
946+
var points = new PointF[]{
947+
new PointF(e.ClipRectangle.Left , e.ClipRectangle.Top),
948+
new PointF(e.ClipRectangle.Right, e.ClipRectangle.Bottom)
949+
};
950+
951+
inverse_transformation.TransformPoints(points);
952+
953+
var left = points[0].X;
954+
var right = points[1].X;
955+
var top = points[0].Y;
956+
var bottom = points[1].Y;
957+
var stepScaled = GridStep;
958+
959+
var xOffset = ((float)Math.Round(left / stepScaled) * stepScaled);
960+
var yOffset = ((float)Math.Round(top / stepScaled) * stepScaled);
961+
962+
if (stepScaled > 3)
963+
{
964+
for (float x = xOffset; x < right; x += stepScaled)
965+
e.Graphics.DrawLine(GridPen, x, top, x, bottom);
966+
967+
for (float y = yOffset; y < bottom; y += stepScaled)
968+
e.Graphics.DrawLine(GridPen, left, y, right, y);
969+
}
970+
}
971+
#endregion
972+
860973

861974

862975
#region OnMouseWheel
@@ -1436,7 +1549,7 @@ private bool ConnectionIsAllowed(NodeConnector from, NodeConnector to)
14361549
if (!CompatibilityStrategy.CanConnect(from, to))
14371550
return false;
14381551
}
1439-
1552+
14401553
// If someone has subscribed to the ConnectionAdding event,
14411554
// give them a chance to interrupt this connection attempt.
14421555
if (null != ConnectionAdding)
@@ -1663,8 +1776,8 @@ protected override void OnDoubleClick(EventArgs e)
16631776
}
16641777
#endregion
16651778

1666-
#region OnClick
1667-
protected override void OnClick(EventArgs e)
1779+
#region OnMouseClick
1780+
protected override void OnMouseClick(MouseEventArgs e)
16681781
{
16691782
try
16701783
{
@@ -1676,6 +1789,31 @@ protected override void OnClick(EventArgs e)
16761789
inverse_transformation.TransformPoints(points);
16771790
var transformed_location = points[0];
16781791

1792+
if (e.Button == MouseButtons.Right)
1793+
{
1794+
if (null != ShowElementMenu)
1795+
{
1796+
// See if we clicked on an element and give our owner the chance to show a menu
1797+
var result = FindElementAt(transformed_location, delegate(IElement el)
1798+
{
1799+
// Fire the event and see if someone cancels it.
1800+
var eventArgs = new AcceptElementLocationEventArgs(el, this.PointToScreen(lastLocation));
1801+
// Give our owner the chance to show a menu for this element ...
1802+
ShowElementMenu(this, eventArgs);
1803+
// If the owner declines (cancel == true) then we'll continue looking up the hierarchy ..
1804+
return !eventArgs.Cancel;
1805+
});
1806+
// If we haven't found anything to click on we'll just return the event with a null pointer ..
1807+
// allowing our owner to show a generic menu
1808+
if (result == null)
1809+
{
1810+
var eventArgs = new AcceptElementLocationEventArgs(null, this.PointToScreen(lastLocation));
1811+
ShowElementMenu(this, eventArgs);
1812+
}
1813+
return;
1814+
}
1815+
}
1816+
16791817
var element = FindElementAt(transformed_location);
16801818
if (element == null)
16811819
{
@@ -1705,7 +1843,7 @@ protected override void OnClick(EventArgs e)
17051843
}
17061844
finally
17071845
{
1708-
base.OnClick(e);
1846+
base.OnMouseClick(e);
17091847
}
17101848
}
17111849
#endregion

Graph/Node.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ public sealed class AcceptNodeEventArgs : CancelEventArgs
4949
public Node Node { get; private set; }
5050
}
5151

52+
public sealed class AcceptElementLocationEventArgs : CancelEventArgs
53+
{
54+
public AcceptElementLocationEventArgs(IElement element, Point position) { Element = element; Position = position; }
55+
public AcceptElementLocationEventArgs(IElement element, Point position, bool cancel) : base(cancel) { Element = element; Position = position; }
56+
public IElement Element { get; private set; }
57+
public Point Position { get; private set; }
58+
}
59+
5260
public class Node : IElement
5361
{
5462
public string Title { get { return titleItem.Title; } set { titleItem.Title = value; } }

GraphExample/ExampleForm.Designer.cs

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

GraphExample/ExampleForm.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public ExampleForm()
6666
graphControl.ConnectionAdded += new EventHandler<AcceptNodeConnectionEventArgs>(OnConnectionAdded);
6767
graphControl.ConnectionAdding += new EventHandler<AcceptNodeConnectionEventArgs>(OnConnectionAdding);
6868
graphControl.ConnectionRemoving += new EventHandler<AcceptNodeConnectionEventArgs>(OnConnectionRemoved);
69+
graphControl.ShowElementMenu += new EventHandler<AcceptElementLocationEventArgs>(OnShowElementMenu);
6970

7071
graphControl.Connect(colorItem, check1Item);
7172
}
@@ -85,6 +86,36 @@ void OnConnectionRemoved(object sender, AcceptNodeConnectionEventArgs e)
8586
//e.Cancel = true;
8687
}
8788

89+
void OnShowElementMenu(object sender, AcceptElementLocationEventArgs e)
90+
{
91+
if (e.Element == null)
92+
{
93+
// Show a test menu for when you click on nothing
94+
testMenuItem.Text = "(clicked on nothing)";
95+
nodeMenu.Show(e.Position);
96+
e.Cancel = false;
97+
} else
98+
if (e.Element is Node)
99+
{
100+
// Show a test menu for a node
101+
testMenuItem.Text = ((Node)e.Element).Title;
102+
nodeMenu.Show(e.Position);
103+
e.Cancel = false;
104+
} else
105+
if (e.Element is NodeItem)
106+
{
107+
// Show a test menu for a nodeItem
108+
testMenuItem.Text = e.Element.GetType().Name;
109+
nodeMenu.Show(e.Position);
110+
e.Cancel = false;
111+
} else
112+
{
113+
// if you don't want to show a menu for this item (but perhaps show a menu for something more higher up)
114+
// then you can cancel the event
115+
e.Cancel = true;
116+
}
117+
}
118+
88119
void OnConnectionAdding(object sender, AcceptNodeConnectionEventArgs e)
89120
{
90121
//e.Cancel = true;

GraphExample/ExampleForm.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,10 @@
117117
<resheader name="writer">
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120+
<metadata name="nodeMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
121+
<value>17, 17</value>
122+
</metadata>
123+
<metadata name="nodeToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
124+
<value>128, 17</value>
125+
</metadata>
120126
</root>

0 commit comments

Comments
 (0)