I have a problem with the drawing and or the filling speed of my FlowLayoutPanel.
.
Can I speedup the process my cashing my images somehow?
Or is there a better alternative to the FlowLayoutPanel?

private void FillPanel()
{
	flpServices.Controls.Clear();
	
	prevStatus = 0;
	prevRows = 0;
	foreach (Jims.Services s in serviceList)
	{
		PictureBox pt = new PictureBox();
		
		pt.ImageLocation = @""+ imgloc + (int)s.Status + ".png";
		pt.SizeMode = PictureBoxSizeMode.StretchImage;
		pt.Width = 32;
		pt.Height = 32;
		pt.Tag = s.App_Name;
		pt.Click += new EventHandler(PictureBoxClick);

		Label lbl = new Label();
		lbl.Text = s.App_Name + ": " + s.Project_Name;
		lbl.Width = flpServices.Width - pt.Width - 30;
		lbl.Height = 32;
		lbl.TextAlign = ContentAlignment.MiddleLeft;

		flpServices.Controls.Add(pt);
		flpServices.Controls.Add(lbl);
		flpServices.SetFlowBreak(lbl, true);

		prevRows++;
		prevStatus += (int)s.Status;
	}
	IconChanger();
}

Dani AI

Generated

Good move reusing controls — that avoids repeated control construction/layout. Combine that with a small set of practices and you should see a big improvement.

Preload and cache the images once (off the UI thread) and assign cached Bitmaps when updating rows. Example preload pattern (keeps files unlocked by cloning into a Bitmap):

var cache = new Dictionary<int, Image>();
foreach (var status in statuses)
{
    using (var fs = File.OpenRead(Path.Combine(imgloc, status + ".png")))
    {
        cache[status] = new Bitmap(Image.FromStream(fs));
    }
}

Batch updates so the panel does not repaint for every change. Either call SuspendLayout()/ResumeLayout(false) while you update, or disable redraw with WM_SETREDRAW, update controls, then re-enable and Refresh():

// P/Invoke WM_SETREDRAW approach
SendMessage(flpServices.Handle, WM_SETREDRAW, (IntPtr)0, IntPtr.Zero);
// update controls...
SendMessage(flpServices.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
flpServices.Refresh();

Reduce flicker by enabling double buffering on the FlowLayoutPanel (subclass and set DoubleBuffered = true and appropriate ControlStyles). For very large lists, switch to a virtualized control (ListView in VirtualMode + ImageList or a DataGridView with owner-draw) — those avoid hundreds of child controls and paint much faster.

Tie these together: preload images (cache), stop redraw, update/reuse controls, then resume and let the double-buffered panel paint once. This combines ’s reuse approach, the image-caching idea from , and the compositing/double-buffer advice from while avoiding per-item disk IO and constant relayout. Be sure to Dispose cached images on shutdown.

Recommended Answers

All 4 Replies

Image.FromFile() is faster than ImageLocation. Use pt.Image=Image.FromFile("file").

Image.FromFile() is faster than ImageLocation. Use pt.Image=Image.FromFile("file").

Thanks that's better but still not perfect, I still see the drawing of the items...
What other ways of handling dynamic content is there?

I've come up with one solutions and that's to reuse the controls already in the panel. So instead of clearing the panel I change the label text and the image.

private void FillPanel()
{
	prevStatus = 0;
	prevRows = 0;

	//number of rows to be adden or removed
	int dif = (flpServices.Controls.Count / 2) - serviceList.Count; 
	
	if (dif < 0) //More rows needed
	{
		for (int i = 0; i > dif; i--)
		{
			//Create empty Picturebox
			PictureBox pt = new PictureBox();
			pt.SizeMode = PictureBoxSizeMode.StretchImage;
			pt.Width = 32;
			pt.Height = 32;
			pt.Click += new EventHandler(PictureBoxClick);

			//Create empty Label
			Label lbl = new Label();
			lbl.Width = flpServices.Width - pt.Width - 30;
			lbl.Height = 32;
			lbl.TextAlign = ContentAlignment.MiddleLeft;

			//Add the PictureBox and the Label to the panel
			flpServices.Controls.Add(pt);
			flpServices.Controls.Add(lbl);
			flpServices.SetFlowBreak(lbl, true);

		}
	}
	else if (dif > 0) //Fewer rows neded
	{
		while (dif > 0)
		{
			//Remove the PictureBox and the Label to the panel
			flpServices.Controls.RemoveAt(flpServices.Controls.Count-1);
			flpServices.Controls.RemoveAt(flpServices.Controls.Count-1);
			dif--;
		}
	}

	int row = 0;
	//Update the controls in the panel.
	foreach (Jims.Services s in serviceList)
	{
		UpdateFLPRow(s, row);

		row += 2;

		prevRows++;
		prevStatus += (int)s.Status;
	}	
	IconChanger();
}

private void UpdateFLPRow(Jims.Services s,int row)
{
	((PictureBox)flpServices.Controls[row]).Image = Image.FromFile(@"" + imgloc + (int)s.Status + ".png");
	((PictureBox)flpServices.Controls[row]).Tag = s.App_Name;
	((Label)flpServices.Controls[row + 1]).Text = s.App_Name + ": " + s.Project_Name;
}

Try this; drop this method in your code.

protected override CreateParams CreateParams
        {
            get
             {
               CreateParams cp = base.CreateParams;
               cp.ExStyle |= 0x02000000;
               return cp;
             }
        }

Hope this helps,
Jay Stratemeyer

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.