Sunday, November 17, 2013

What about a Dynamic Flow Chart Control? (Part2)



Every Entity will have potentially 8 vertex each one could have also more than one connection.

public enum VertexPosition { None, TopLeft, TopRight, TopMiddle, BottomLeft, BottomMiddle, BottomRight, MiddleLeft, MiddleRight }

So I imagined a class called FCBaseControl where to put a List of Connections and a Text property. Simple but efficient.

Connections get or set Connection items to define relations both graphics and logics.
Caption get or set a Text description that appear into the Entity area.

The FCBaseControl expose also two Method:

public Point GetVertex(VertexPosition ePosition, int iDistance = 5)
public void GetAutomaticPositions(FCControlBase c1, FCControlBase c2, out VertexPosition or, out VertexPosition dest)

GetVertex() return the coordinates (Point) of a given Vertex, while GetAutomaticPositions() get the best Vertex to connect c1 to c2 without overlapping. Connection object represent a logical relation and an hypothetic graphical connection, because the graphical connection can be changed run-time as I will show you in this article.

One event it is also published, this is for redrawing run-time the Entity object.

public event LocationDraggedEvent LocationDragged;
The next step will be build a control that will provide all redrawing activities without any operator iterations. A canvas where to paint our “pictures”. This object is called FCCanvasPanel and it is a simple UserControl derived class.

[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
    public partial class FCCanvasPanel : UserControl

The ParentControlDesigner reference extends the design mode behavior of a Control that supports nested controls, otherwise it should be impossible to manage other controls inside it. As I mentioned before this control should be able to redraw the connections with also the ability of a clever redrawing. Property AutoConnect enable the clever redrawing.




This very simple object will do the dirty job on the backstage.
This is the private Method that do the job:

private void DrawConnections()
{
            
    PaintElement draw = new PaintElement(this.CreateGraphics());

    draw.Color = Color.Black;

    foreach (var item in this.Controls)
    {
        if (item is FCControlBase)
        {
            FCControlBase c1 = (FCControlBase)item;

            foreach (Connection con in c1.Connections)
            {
                Point p1 = new Point();
                Point p2 = new Point();
                if (con.Child != null)
                {
                    if (AutoConnect)
                    {
                        FCControlBase c2 = (FCControlBase)con.Child;

                        VertexPosition or, dest;
                        FCControlBase.GetAutomaticPositions(c1, c2, out or, out dest);

                        if (or == VertexPosition.None || dest == VertexPosition.None)
                            continue;

                        p1 = c1.GetVertex(or, 10);//c1.GetVertex(con.Origin, 10);
                        p2 = c2.GetVertex(dest, 5); //c2.GetVertex(con.Destination, 5);
                    }
                    else
                    {
                        p1 = c1.GetVertex(con.Origin, 10);
                        FCControlBase c2 = (FCControlBase)con.Child;
                        p2 = c2.GetVertex(con.Destination, 5);
                    }

                    draw.Color = con.Color;
                    if (con.Type == ConjunctionType.Arrow)
                        draw.Arrow(p1, p2);
                    else if (con.Type == ConjunctionType.Line)
                        draw.Line(p1, p2);

                    if (con.Text != "")
                    {
                        Point pMid = draw.GetMiddle(p1, p2);
                        double dAngle = draw.GetAngle(p1, p2);
                        RecalcTextPointByAngle(dAngle, ref pMid);
                        draw.Text(pMid, con.Text, new Font("Arial", 8), Color.Black);
                    }
                }
            }

        }
    }
}


NOTE: I add this note in order to say sorry to the readers, because for commercial reason I cannot divulge more informations. I hope these two post can inspire you to develop a more complex library.


Part 1

No comments :

Post a Comment