Trouble with TreeView.DrawNode - OwnerDrawText

Emmanuel F picture Emmanuel F · Jan 12, 2010 · Viewed 8k times · Source

I have an app that is connected to a remote server and polling data when needed. It has a TreeView where the Nodes represent the objects that are available and the color of the text indicate whether the data has been loaded or not; gray-italicized indicates not loaded, black, regular text is loaded.

Currently I have set the TreeView to be OwnderDrawText and have the TreeView.DrawNode function simply draw the text as so:

private void TreeViewDrawNode(object sender, DrawTreeNodeEventArgs e)
{
    if (!e.Node.IsVisible)
    {
        return;
    }

    bool bLoaded = false;

    if (e.Bounds.Location.X >= 0 && e.Bounds.Location.Y >= 0)
    {
       if(e.Node.Tag != null)
       {
           //...
           // code determining whether data has been loaded is done here
           // setting bLoaded true or false
           //...
       }
       else
       {
           e.DrawDefault = true;
           return;
       }

       Font useFont = null;
       Brush useBrush = null;

       if (bLoaded)
       {
           useFont = e.Node.TreeView.Font;
           useBrush = SystemBrushes.WindowText;
        }
        else
        {
            useFont = m_grayItallicFont;
            useBrush = SystemBrushes.GrayText;
        }
        e.Graphics.DrawString(e.Node.Text, useFont, useBrush, e.Bounds.Location);
    }
}

I figured that would be enough, however, this has been causing some issues;

  1. When a node is selected, focused or not, it doesn't envelop all of the text, example (I hope imgur is ok).
  2. When the node is focused, the dotted outline doesn't show either. If you compare it with this example. The nodes with the "log" in the text are using the e.DefaultDraw = true

I tried following the example given in this question. It looked something like this:

private void TreeViewDrawNode(object sender, DrawTreeNodeEventArgs e)
 {
  if (!e.Node.IsVisible)
  {
   return;
  }

  bool bLoaded = false;

  if (e.Bounds.Location.X >= 0 && e.Bounds.Location.Y >= 0)
  {
     if(e.Node.Tag != null)
     {
      //...
      // code determining whether data has been loaded is done here
      // setting bLoaded true or false
      //...
     }
     else
     {
      e.DrawDefault = true;
      return;
     }

   //Select the font and brush depending on whether the property has been loaded
   Font useFont = null;
   Brush useBrush = null;

   if (bLoaded)
   {
    useFont = e.Node.TreeView.Font;
    useBrush = SystemBrushes.WindowText;
   }
   else
   {
    //member variable defined elsewhere
    useFont = m_grayItallicFont;
    useBrush = SystemBrushes.GrayText;
   }

   //Begin drawing of the text

   //Get the rectangle that will be used to draw
   Rectangle itemRect = e.Bounds;
   //Move the rectangle over by 1 so it isn't on top of the check box
   itemRect.X += 1;

   //Figure out the text position
   Point textStartPos = new Point(itemRect.Left, itemRect.Top);
   Point textPos = new Point(textStartPos.X, textStartPos.Y);

   //generate the text rectangle
   Rectangle textRect = new Rectangle(textPos.X, textPos.Y, itemRect.Right - textPos.X, itemRect.Bottom - textPos.Y);

   int textHeight = (int)e.Graphics.MeasureString(e.Node.Text, useFont).Height;
   int textWidth = (int)e.Graphics.MeasureString(e.Node.Text, useFont).Width;

   textRect.Height = textHeight;

   //Draw the highlighted box
   if ((e.State & TreeNodeStates.Selected) != 0)
   {
    //e.Graphics.FillRectangle(SystemBrushes.Highlight, textRect);
    //use pink to see the difference
    e.Graphics.FillRectangle(Brushes.Pink, textRect);
   }
   //widen the rectangle by 3 pixels, otherwise all of the text     won't fit
   textRect.Width = textWidth + 3;

   //actually draw the text
   e.Graphics.DrawString(e.Node.Text, useFont, useBrush, e.Bounds.Location);

   //Draw the box around the focused node
   if ((e.State & TreeNodeStates.Focused) != 0)
   {
    textRect.Width = textWidth;
    Pen focusPen = new Pen(Color.Black);
    focusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
    e.Graphics.DrawRectangle(focusPen, textRect);
   }
  }
 }

However, the results were this. (Note, used pink to differentiate the colors). As you can see, the highlighted background doesn't extend all the way to where the focused dotted line is at. And there's also another box that is drawn as well.

I'm slightly stumped on how to fix this. All I want is to have gray italicized text when something is loaded. The first and simplest approach doesn't quite work and the second method feels like I'm doing way too much.

After all that, does anyone have any suggestions on how to do this properly, because there's got to be a simpler way.

Thank you in advance.

Answer

Hans Passant picture Hans Passant · Jan 13, 2010

You'll need to use TextRenderer.DrawText(). That's what TreeView uses, it renders text slightly different from Graphics.DrawString().