Solved

Why is this so slow, Is it GDI+

Posted on 2004-09-13
1
1,581 Views
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];
                              if(!PreviousLabel.Seperator)
                              {
                                    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();
            }
                  else
                  {
                        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];
                  if(!Label.Seperator)
                  {
                        this.DrawLableProduct(Label, gOffScreen, rectLabel);
                        if(e.Index == this.listBoxLabels.SelectedIndex)
                        {
                              oldSelectedRect = rectLabel;
                        }
                  }
                  
                  // Drawimage on screen                    
                  g.DrawImage(OffScreenImage, 0, 0);

                  pathLabel.Dispose();
                  g.Dispose();
            }


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);
                  if(Label.SaleItem)
                        strText = " " + Label.SalePrice.ToString("C");
                  else
                        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
                  if(Label.SaleItem)
                  {
                        //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];

                  format.SetMeasurableCharacterRanges(ranges);

                  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;
            }



0
Comment
Question by:auk_ie
1 Comment
 
LVL 14

Accepted Solution

by:
AvonWyss earned 500 total points
Comment Utility
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.
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Introduction This article series is supposed to shed some light on the use of IDisposable and objects that inherit from it. In essence, a more apt title for this article would be: using (IDisposable) {}. I’m just not sure how many people would ge…
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…
This video discusses moving either the default database or any database to a new volume.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

743 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now