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

Sunday, November 3, 2013

What about a Dynamic Flow Chart Control? (Part1)

This is the idea: I would like to build a “FlowChart” control where the “Entities” are the classical controls, like buttons, panels, label, picture etc, and the graphical relations are maintained also if the position between elements is changed, or if new elements are added.

In figure Number 1 there is the initial structure, the colored rectangles are my “special objects” FCControlBase.

Each one are connected, at least, with another one. Pressing Draw the relations are showed (figure 2).

Figure1: Initial Structure

Figure2: Relations between Entities

Now image to change the structure, all relations are maintained and redraw correctly by the control like in figure 3.
Figure3: New Structure with the relations

This is only a starting point to use to improve your projects. The base classes are Open Source, so you could enjoy developing it more. But now let’s see the basic ideas.

In figure 4 I have used the control to illustrate the custom library and the main properties.

Figure 4: Classes Architecture

The base class FCControlBase has two properties Connections and Caption. The first one maintains the relations between the Entities, and obviously “Caption” is the text displayed into the block.

Connection

This class is the core, it exposes the linked Childs to the current Entity, here the Properties:       
  • Child     get or set the FCControlBase connected.
  • Type     get or set the ConjunctionType (Line, or ArrowLine …)
  • Origin   get or set the VertexPosition. Each Entity can be connected graphically to one of eight vertex.
  • Destination        get or set VertexPosition.
  • Color     get or set a Line Color.
  • Text      get or set a description.

Figure 5: Property Connections Editor


public class Connection
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        VertexPosition mOrigin { get; set; }
        VertexPosition mDestination { get; set; }
        UserControl mChild { get; set; }
        ConjunctionType mType { get; set; }
        Color mColor { get; set; }
        string mText { get; set; }

        public VertexPosition Origin { get { return mOrigin; } set { mOrigin = value; OnPropertyChanged("Origin"); } }
        public VertexPosition Destination { get { return mDestination; } set { mDestination = value; OnPropertyChanged("Destination"); } }
        public UserControl Child { get { return mChild; } set { mChild = value; OnPropertyChanged("Child"); } }
        public ConjunctionType Type { get { return mType; } set { mType = value; OnPropertyChanged("Type"); } }
        public Color Color { get { return mColor; } set { mColor = value; OnPropertyChanged("Color"); } }
        public string Text { get { return mText; } set { mText = value; OnPropertyChanged("Text"); } }

        public Connection()
        {
            Origin = VertexPosition.BottomMiddle;
            Destination = VertexPosition.TopMiddle;
            Child = null;
            Type = ConjunctionType.Arrow;
            Color = Color.Black;
            Text = "";
        }
    }



Part 2