2

I'm creating an application to schedule different tasks. These are displayed by drawing rectangles on a panel. This has to be responsive. So I need to draw and invalidate on every size change. When I reach a maximum height of my planning panel it autoscrolls.

The problem is when I grab the scrollbar and start scrolling away for a bit, when I release the scrollbar my whole application and computer freezes.

Most likely this is due to the onpaint event being called on every little scroll and stacking up, leaving the application to hang until they are all done.

Now my question is: How would I be able to fix this? Possibly by keeping the paint event from being called multiple times, but how?

The method called by the paint event:

private void panelPlanning_Paint(object sender, PaintEventArgs e)
    {
        for (int i = 0; i < userList.Count; i++)
        {
            Label temp = new Label();
            temp.Text = userList[i].Text;
            temp.Width = panelUsers.Width;
            temp.Height = 50;
            temp.BorderStyle = BorderStyle.FixedSingle;
            temp.Location = new Point(0, i * 50);
            temp.TextAlign = ContentAlignment.MiddleCenter;
            panelUsers.Controls.Add(temp);

            foreach (FullTask task in taskList)
            {
                if (task.AssignedTo == userList[i].Text && task.StartDate != "" && task.DueDate != "")
                {
                    DateTime start = DateTime.ParseExact(task.StartDate, "dd/MM/yyyy", CultureInfo.InvariantCulture);
                    DateTime end = DateTime.ParseExact(task.DueDate, "dd/MM/yyyy", CultureInfo.InvariantCulture);
                    Brush brush;
                    if (task.Priority == Enums.priorities[2])
                    {
                        brush = Brushes.Yellow;
                    }
                    else if (task.Priority == Enums.priorities[1])
                    {
                        brush = new SolidBrush(Color.FromArgb(255, 0, 80, 123));
                    }
                    else
                    {
                        brush = Brushes.Red;
                    }

                    panelPlanning.CreateGraphics().FillRectangle(brush, new Rectangle((start.Subtract(dtPickerStart.Value).Days + 1) * labelDaysWidth, i * 50, (end.Subtract(start).Days + 1) * labelDaysWidth, 25));
                }
            }
        }
    }
Toon Casteele
  • 2,479
  • 15
  • 25
  • Make sure the OnPaint events doesn't get called like dozens of time every you scroll a bit. – Max Apr 09 '13 at 14:24
  • Well that's my question, isn't it? How do I stop the onscroll from calling the onpaint (without having to write a new panel class to override the onscroll)? – Toon Casteele Apr 09 '13 at 14:27

4 Answers4

5

You are adding new Label controls on every single paint event. Don't do this:

      // Label temp = new Label();
      // temp.Text = userList[i].Text;
      // temp.Width = panelUsers.Width;
      // temp.Height = 50;
      // temp.BorderStyle = BorderStyle.FixedSingle;
      // temp.Location = new Point(0, i * 50);
      // temp.TextAlign = ContentAlignment.MiddleCenter;
      // panelUsers.Controls.Add(temp);

Also, use the e.Graphics object supplied by the argument, not CreateGraphics,

LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • 1
    Moving the labels outside the paint method did the trick. I can't believe it takes like a 100 times longer to add 10 labels than to add 60 rectangles, if not more, while also iterating a couple of lists. – Toon Casteele Apr 09 '13 at 14:42
0

You have to trace which FullTask actually has to be displayed on the screen. This can be achieved by looking on Control.ClientRectangle and keeping in mind current scroll position.

In this way from the all set of the FullTasks to draw you will get only that subset of tasks that actually visible on the screen, and draw only those ones.

This is a correct way of dealing with drawable artifacts which is implemented in more or less all drawing frameworks 2D or even 3D.

There is also concept of tracing if some element is covered (or part of it is covered) by another element, so it will be "culed", but in your case this doesn't seem to matter.

Pay attention also on fact that you adding controls. Do not do that. If the amount of FullTasks is big, just draw them with Graphics object.

Tigran
  • 61,654
  • 8
  • 86
  • 123
0

The following line of code should only be executed once:

panelUsers.Controls.Add(temp);

You are constantly adding a new instance of temp to the panelUsers control.

Max
  • 12,622
  • 16
  • 73
  • 101
0

as already Described by @LarsTech, don't add controls in the paint event, and use the supplied Graphics object instead of your own ...

If you still face performance problems, consider painting on a bitmap whenever you change something, and only draw that bitmap onto the screen in onPaint

DarkSquirrel42
  • 10,167
  • 3
  • 20
  • 31