0

To achieve this type of interactive artwork, where you can drag the boxes and draw connection lines between them, I am using the code below.

enter image description here

Basically, when the mouse is being dragged (to move a box) or a new selection is being made on the panel by clicking a new box, I clear the panel, draw all rectangles, all text and then all line connections using a timer (around 20 times a second). This seems however very slow to re draw the panel components and gives a very annoying flashing effect. Is there a way of optimising this code? / Making the drawing more efficient?

    public MainForm()
    {
        // Create surface and pen to use for all drawing
        surface = NodesPanel.CreateGraphics();
        surface.InterpolationMode = InterpolationMode.Default;
        surface.SmoothingMode = SmoothingMode.Default;

        penl = new Pen(Color.Blue, 2.0f);
        ....

        timer.Tick += new EventHandler(timer_Tick);
        timer.Interval = 60;              // Timer call
        timer.Enabled = true;                       // Enable the timer
        timer.Start();
     }





    void timer_Tick(object sender, EventArgs e)
    {
        if (refreshScreen) // when dragging a box of making a new selection
        {
            RenderScreen();
        }
    }



    public void RenderScreen()
    {
        surface.Clear(Color.White);

        drawUnselectedNodes();

        if (showSearchGate)
        {
            drawGates();
        }

        if (currentlyClickedNode != null && !(currentlyClickedNode is Gate))
        {
            currentlyClickedNode.drawMyConnections(surface, penl); // Draw in a different colour
            currentlyClickedNode.drawMe(surface, penl);

            if (drawingLine)
            {
                DrawingHelper.DrawLine(surface, penl, currentlyClickedNode.getGatePosition(), linePosition, "regular");
            }
        }
    }



    private void drawUnselectedNodes()
    {
        foreach (QuestNode q in nodes)
        {
            if (q != currentlyClickedNode)
            {
                q.drawMe(surface, penl);
            }
        }

    }



 // The node structure
public class QuestNode
{
    ...
    public void drawMe(Graphics g, Pen p)
    {
        DrawingHelper.DrawRoundRectWithText
             (g, p, pos.x, pos.y, NODE_WIDTH * currentScale, NODE_HEIGHT *
                  currentScale, GATE_RADIUS * currentScale, name, description, true,
                       (selected ? RECTANGLE_SELECTED_COLOR : ANGLE_UNSELECTED_COLOR));
    }

    public void drawMyConnections(Graphics g, Pen p) 
    {
        foreach (QuestNode qn in relatedQuests)
        {
            DrawingHelper.DrawLine(g, p, pos, qn.getGatePosition(), "regular");
        }
    }
 }




static class DrawingHelper
{
    public static SolidBrush STANDARD_BRUSH = new SolidBrush(RECTANGLE_COLOR);
    public static int PRIMARY_TEXT_FONT_SIZE = 10;
    public static Font PRIMARY_FONT_STYLE = new Font("Arial", PRIMARY_TEXT_FONT_SIZE);
    public static int SECONDARY_TEXT_FONT_SIZE = 8;
    public static Font SECONDARY_FONT_STYLE = new Font("Arial", SECONDARY_TEXT_FONT_SIZE);


     public static void DrawRoundRectWithText(Graphics g, Pen p, float x, float y,
             float width, float height, float gateRadius, string textFirstLike, 
                 string textSecondsLine, bool hasGate, Color color)
    {
        Graphics formGraphics = g;
        g.InterpolationMode = InterpolationMode.Default;
        g.SmoothingMode = SmoothingMode.Default;

        SolidBrush myBrush = STANDARD_BRUSH;
        myBrush.Color = color;

        p.DashPattern = new float[] { 1 };
        Rectangle rect = new Rectangle((int)x, (int)y, (int)width, (int)height);
        p.Width = RECTANGLE_BORDER;

        formGraphics.FillRectangle(myBrush, rect);
        p.Color = RECTANGLE_COLOR;
        formGraphics.DrawRectangle(p, rect);


        DrawString(g, textFirstLike, x + TEXT_OFFSET_X, y + height / 4, PRIMARY_TEXT_FONT_SIZE, (int)width);
        DrawString(g, textSecondsLine, x + TEXT_OFFSET_X, y + height / 2, SECONDARY_TEXT_FONT_SIZE, (int)width);

        int gateWidth = (int)gateRadius;
        int gateHeight = (int)gateRadius;

        if (hasGate)
        {

            myBrush.Color = GATE_COLOR;
            Rectangle circle = new Rectangle((int)(x + width) - gateWidth/2, (int)(y + height) - gateHeight/2, gateWidth, gateHeight);

            formGraphics.FillEllipse(myBrush, circle);
            formGraphics.DrawEllipse(p, circle);

        }

        Point[] diamondPoints = new Point[4];
        diamondPoints[0] = new Point((int)(x - gateWidth / 2), (int)y);
        diamondPoints[1] = new Point((int)x, (int)(y - gateHeight / 2));
        diamondPoints[2] = new Point((int)(x + gateWidth / 2), (int)y);
        diamondPoints[3] = new Point((int)x, (int)(y + gateHeight / 2));

        myBrush.Color = Color.LightGray;
        formGraphics.FillPolygon(myBrush, diamondPoints);
        formGraphics.DrawPolygon(p, diamondPoints);
    }
  }

    public static void DrawLine(Graphics g, Pen p, Vector2D origin, Vector2D end, string style)
    {
        Pen myPen = p;
        p.Color = Color.Black;

        Graphics formGraphics = g;

        g.InterpolationMode = InterpolationMode.Default;
        g.SmoothingMode = SmoothingMode.Default;

        myPen.Width = LINE_BORDER;
        if (style == "dotted")
        {
            myPen.DashPattern = new float[] { 1, 1, 0.5f };
        }

        formGraphics.DrawLine(myPen, origin.x, origin.y, end.x, end.y);

    }

    private static void DrawString(Graphics g, string text, float posX, float posY, int fontSize, int stringMaxWidth)
    {

        string drawString = text;
        Font drawFont = (fontSize == PRIMARY_TEXT_FONT_SIZE) ? PRIMARY_FONT_STYLE : SECONDARY_FONT_STYLE;
        SolidBrush drawBrush = STANDARD_BRUSH;
        drawBrush.Color = Color.Black;

        float x = posX;
        float y = posY;

        float charHeight = drawFont.GetHeight() / 2;
        int maxChars = (int)(stringMaxWidth / charHeight);

        if (drawString.Length > (maxChars - 3))
        {
            drawString = drawString.Substring(0, maxChars - 3) + "...";
        }

        g.DrawString(drawString, drawFont, drawBrush, x, y);
    }
Alex
  • 561
  • 2
  • 9
  • 28
  • 1
    *Never* use CreateGraphics(), that can't be double-buffered. Always paint in the Paint event handler. – Hans Passant Nov 27 '12 at 18:42
  • Thank you. I have moved all the drawing functions into the panel's Paint event and am using the event's Graphics object for all drawing. That, and making the panel Double Buffered (using the info here http://stackoverflow.com/questions/76993/how-to-double-buffer-net-controls-on-a-form), has removed all flickering. Thanks! – Alex Nov 28 '12 at 17:51

0 Answers0