Why is this so slow, Is it GDI+

Posted on 2004-09-13
Last Modified: 2012-08-13
Hello I am wondering why this code is so slow, I think its gots something to do with GDI+

All I'm doing here is owner drawing list box items. Each Item is basically a rounded corner rectangle with some text inside. It all went well for me until I added the code marked with ***********. I added because when items were selected I wanted the item rectangles to be colored LightSteelBlue. The problem was that when another item was selected the previously selected item would remain a LightSteelBlue color. Hence I added code marked with ******* to resolve the problem, which it does. But things have gotten unbarably slow now. and I can't see why. Maybe the code isn;t the most efficient in the world but I can't see why it would take so long to compute (I'm talking about seconds here before the item color changes when selected and the previous selected item color changes to white (the unselected item color)). Can anyone tell me why things went so slow?? and a possible resolve?

private Rectangle oldSelectedRect = new Rectangle(-1,-1,0,0);
            private int oldSelectedIndex = -1;
            private GraphicsPath oldLabelPath = new GraphicsPath();
            private void listBoxLabels_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
                  //DrawMode property set to draw fixed sized items from resource editor
                  // Set antialias effect on                    
                  gOffScreen.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;

                  //Get Graphics DC
                  Graphics g = e.Graphics;
                  // Create a new Brush and initialize to a Black colored brush by default.
                  Brush myBrush = Brushes.Black;

                  Pen penLabelOutline = new Pen(Color.Black, 1);
                  //Draw a Rounded Rectangle Label
                  int nRadius = 8;
                  //Calculate Label Bounds
                  Rectangle rectLabel = e.Bounds;
                  rectLabel.X += 4;
                  rectLabel.Width -= 8;
                  rectLabel.Y += 3;
                  rectLabel.Height -= 6;

                  GraphicsPath pathLabel = DrawRoundRect(rectLabel.X, rectLabel.Y, rectLabel.Width, rectLabel.Height, nRadius);
                  //Outline and fill Label

/***************************This if block slows the whole thing down by seconds, Why?******************/
                  if( oldSelectedRect != e.Bounds && oldSelectedIndex > -1 && oldSelectedIndex < listBoxLabels.Items.Count)
                        if(oldSelectedRect.Y != 3)//!=3 is hack to handle a drawing anomally
                              gOffScreen.FillPath(new SolidBrush(Color.White), oldLabelPath);
                              gOffScreen.DrawPath(penLabelOutline, oldLabelPath);

                              //Get previously selected Label Info
                              TagLabel PreviousLabel = (TagLabel)this.listBoxLabels.Items[oldSelectedIndex];
                                    this.DrawLableProduct(PreviousLabel, gOffScreen, oldSelectedRect);
                  //Chek if item is selected
                  if(e.Index == this.listBoxLabels.SelectedIndex)
                        gOffScreen.FillPath(new SolidBrush(Color.LightSteelBlue), pathLabel);
                        oldSelectedRect = e.Bounds;
                        oldSelectedIndex = e.Index;
                        oldLabelPath = (GraphicsPath)pathLabel.Clone();
                        gOffScreen.FillPath(new SolidBrush(Color.White), pathLabel);
                  gOffScreen.DrawPath(penLabelOutline, pathLabel);

                  //Calculate Label Bounds (i.e. the rectangle inside the rounded rectangle - radius on all sides)
                  rectLabel.X += nRadius;
                  rectLabel.Width -= 2 * nRadius;
                  rectLabel.Y += nRadius;
                  rectLabel.Height-= 2 * nRadius;
                  //Get Label Info
                  TagLabel Label = (TagLabel)this.listBoxLabels.Items[e.Index];
                        this.DrawLableProduct(Label, gOffScreen, rectLabel);
                        if(e.Index == this.listBoxLabels.SelectedIndex)
                              oldSelectedRect = rectLabel;
                  // Drawimage on screen                    
                  g.DrawImage(OffScreenImage, 0, 0);


3 routines called by the paint handler (if it matters)

private void DrawLableProduct(TagLabel Label, Graphics g, Rectangle rectLabelBounds)
                  //Specify the format of the text
                  StringFormat sf = new StringFormat();
                  sf.FormatFlags = StringFormatFlags.NoWrap;
                  sf.Trimming = StringTrimming.EllipsisCharacter;
                  sf.Alignment = StringAlignment.Center;
                  string strText;

                  //1) Draw the logo
                  //Get bitmap from file
                  Bitmap bmpLogo = new Bitmap("Graphics\\LabelLogo.bmp");

                  //Default Logo Width & height = 120 x 40
                  //Calculate logo size; Size = 0.6 x 0.2 label bounds
                  RectangleF rectLogo = new RectangleF(new Point(rectLabelBounds.X, rectLabelBounds.Y), new SizeF((float)(rectLabelBounds.Width * 0.6), (float)(rectLabelBounds.Height * 0.2)));            
            //Position the log at the top of the label
                  rectLogo.X += (rectLabelBounds.Width - rectLogo.Width)/2;
                  g.DrawImage(bmpLogo, rectLogo);
                  //2)Drawbarcode Rectangle height 40%
                  RectangleF rectBarcode = rectLabelBounds;
                  rectBarcode.Height = (float)(rectLabelBounds.Height * 0.4);
                  strText = "*" + Label.BarCode + "*";
                  Font fntItem = new Font("Free 3 of 9", 10);
                  fntItem = new Font(fntItem.FontFamily.Name, this.CalculateFontHeight(g, strText, rectBarcode, fntItem));

                  //Get the exact width and height of the barcode
                  rectBarcode.Width = (int)((SizeF)g.MeasureString(strText, fntItem)).Width + 1;//MeasureDisplayStringWidth(g, strText, fntItem);
                  rectBarcode.Height = (int)((SizeF)g.MeasureString(strText, fntItem)).Height;
                  //Centre the barcode in the middle of the label
                  rectBarcode.X += (rectLabelBounds.Width - rectBarcode.Width)/2;
                  rectBarcode.Y += ((rectLabelBounds.Height - rectBarcode.Height)/2) + 4;
                  //g.DrawRectangle(new Pen(Color.Black), rectBarcode.X, rectBarcode.Y, rectBarcode.Width, rectBarcode.Height);
                  g.DrawString(strText, fntItem, Brushes.Black, rectBarcode, sf);

                  //3) Draw the Price
                  RectangleF rectPrice = rectLabelBounds;
                  rectPrice.Height = (float)(rectLabelBounds.Height * 0.25);
                  rectPrice.Y += (float)(rectLabelBounds.Height * 0.75);
                        strText = " " + Label.SalePrice.ToString("C");
                        strText = " " + Label.PriceSell.ToString("C");
                  fntItem = new Font(this.listBoxLabels.Font.FontFamily.Name, 10);
                  fntItem = new Font(fntItem.FontFamily.Name, this.CalculateFontHeight(g, strText, rectPrice, fntItem) - 1);
                  fntItem = new Font(fntItem, FontStyle.Bold);

                  RectangleF rectExactPrice = rectPrice;
                  rectExactPrice.Height = g.MeasureString(strText, fntItem).Height;
                  rectExactPrice.Width = g.MeasureString(strText, fntItem).Width;//MeasureDisplayStringWidth(g, strText, fntItem);
                  rectExactPrice.X = rectLabelBounds.Right - rectExactPrice.Width;
                  rectExactPrice.Y = rectPrice.Bottom - rectExactPrice.Height + 4;
                  //Draw the Price in the bottom right corner
                  sf.Alignment = StringAlignment.Far;
                  //g.DrawRectangle(new Pen(Color.Black), rectExactPrice.X, rectExactPrice.Y, rectExactPrice.Width, rectExactPrice.Height);
                  g.DrawString(strText, fntItem, Brushes.Black, rectExactPrice, sf);
                  //4) Draw Barcode Text
                  RectangleF rectBarcodeText = rectLabelBounds;
                  rectBarcodeText.Height = (float)(rectLabelBounds.Height * 0.15);
                  rectBarcodeText.Y = rectBarcode.Bottom - 4;
                  rectBarcodeText.X = rectBarcode.Left + 4;
                  strText = Label.BarCode;

                  fntItem = new Font(fntItem, FontStyle.Regular);
                  fntItem = new Font(fntItem.FontFamily.Name, this.CalculateFontHeight(g, strText, rectBarcodeText, fntItem));
                  sf.Alignment = StringAlignment.Near;
                  g.DrawString(strText, fntItem, Brushes.Black, rectBarcodeText, sf);
                  //5) Draw description string on top of barcode
                  RectangleF rectDescription = rectLabelBounds;
                  rectDescription.Height = (float)(rectLabelBounds.Height * 0.15);
                  strText = Label.Description;
                  //Left align the descripion string on top of the barcode
                  rectDescription.X = rectBarcode.Left;
                  rectDescription.Y = rectBarcode.Top - (rectDescription.Height + 2);
                  sf.Alignment = StringAlignment.Center;
                  g.DrawString(strText, fntItem, Brushes.Black, rectDescription, sf);

                  //6) Draw was price
                        //Draw the was Price
                        RectangleF rectSalePrice = rectExactPrice;
                        rectSalePrice.Height = (float)(float)(rectLabelBounds.Height * 0.15);
                        rectSalePrice.Y = rectExactPrice.Bottom - rectSalePrice.Height - 4;
                        rectSalePrice.Width = rectLabelBounds.Width - rectExactPrice.Width;
                        strText = "was: " + Label.PriceSell.ToString("C") + " now:";
                        rectSalePrice.Width = g.MeasureString(strText, fntItem).Width;
                        rectSalePrice.X = rectExactPrice.Right - rectExactPrice.Width - rectSalePrice.Width;
                        g.DrawString(strText, fntItem, Brushes.Black, rectSalePrice, sf);

            static private int MeasureDisplayStringWidth(Graphics graphics, string text, Font font)
                   * Description: Calculates the exact width in pixels of a string
                   * Parameters:
                   *      graphics = Device context being used to draw
                   *      text = string to measure
                   * Return = width of string
                  StringFormat format = new StringFormat();
                  RectangleF rect = new RectangleF(0, 0,1000, 1000);
                  CharacterRange[] ranges = {new CharacterRange(0, text.Length) };
                  Region[] regions = new Region[1];


                  regions = graphics.MeasureCharacterRanges (text, font, rect, format);
                  rect    = regions[0].GetBounds (graphics);

                  return (int)(rect.Right + 1.0f);

            /// <summary>
            /// Returns the max allowable font size for a specified text string to fit in a rectangle
            /// </summary>
            /// <param name="g"></param>
            /// <param name="rectText"></param>
            /// <param name="fntText"></param>
            /// <returns></returns>
            private float CalculateFontHeight(Graphics g, string strText, RectangleF rectText, Font fntText)
                  float nFontSize = 1.0f;

                  //Initially the Font Size = 1;
                  fntText = new Font(fntText.FontFamily.Name, 1);

                  while(MeasureDisplayStringWidth(g, strText, fntText) < rectText.Width)
                        nFontSize += 0.1f;
                        fntText = new Font(fntText.FontFamily.Name, nFontSize);

                  while(rectText.Height < ((SizeF)g.MeasureString(strText, fntText)).Height)
                        nFontSize -= 0.1f;
                        fntText = new Font(fntText.FontFamily.Name, nFontSize);
                  return nFontSize;

Question by:auk_ie
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
1 Comment
LVL 14

Accepted Solution

AvonWyss earned 500 total points
ID: 12042981
Try not to create a new Font() instance everytime, but rather store it in a variable and just assign it where you need it. Creating font objects is relatively expensive.

Featured Post

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes a simple method to resize a control at runtime.  It includes ready-to-use source code and a complete sample demonstration application.  We'll also talk about C# Extension Methods. Introduction In one of my applications…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
There are cases when e.g. an IT administrator wants to have full access and view into selected mailboxes on Exchange server, directly from his own email account in Outlook or Outlook Web Access. This proves useful when for example administrator want…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…

691 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question