[C# Helper]
Index Books FAQ Contact About Rod
[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

[C# 24-Hour Trainer]

[C# 5.0 Programmer's Reference]

[MCSD Certification Toolkit (Exam 70-483): Programming in C#]

Title: Draw a tree with nodes containing pictures in C#

[Draw a tree with nodes containing pictures in C#]

(Sorry, this tree is somewhat out of date.)

This example uses the generic TreeNode class described in the following posts to draw a tree with nodes containing pictures.

The program is basically the same because the generic TreeNode class can draw just about anything. The node PictureNode class implements the IDrawable interface, and the TreeNode class uses its methods to arrange the nodes.

The following code shows most of the PictureNode class.

class PictureNode : IDrawable { // Constructor. public Image Picture = null; public string Description; public bool Selected = false; public PictureNode(string description, Image picture) { Description = description; Picture = picture; } // The size of the drawn rectangles. public static SizeF NodeSize = new SizeF(100, 100); // Return the size needed by this node. public SizeF GetSize(Graphics gr, Font font) { return NodeSize; } // Return a RectangleF giving the node's location. private RectangleF Location(PointF center) { return new RectangleF( center.X - NodeSize.Width / 2, center.Y - NodeSize.Height / 2, NodeSize.Width, NodeSize.Height); } // Return True if the target is under this node. public bool IsAtPoint(Graphics gr, Font font, PointF center_pt, PointF target_pt) { RectangleF rect = Location(center_pt); return rect.Contains(target_pt); } ... }

The class starts by declaring variables to hold a node's picture and description. It also provides a Selected value so it can draw the node differently when the node is selected. The constructor simply initializes the picture and description.

The NodeSize variable gives the amount of room the nodes will occupy. For simplicity, this example makes all of the nodes the same size, but you should be able to give them different sizes if you like.

The GetSize method simply returns the node's size.

The Location method returns a rectangle indicating where the node will be when it's centered at a given point. That method is used in the next method, IsAtPoint, to figure out where the node is.

The IsAtPoint method returns true if a given point lies within the node's Location rectangle.

The last two methods, Draw and PositionImage, are the most interesting. The following code shows the Draw method.

// Draw the person. public void Draw(float x, float y, Graphics gr, Pen pen, Brush bg_brush, Brush text_brush, Font font) { // Draw a border. RectangleF rectf = Location(new PointF(x, y)); Rectangle rect = Rectangle.Round(rectf); if (Selected) { gr.FillRectangle(Brushes.White, rect); ControlPaint.DrawBorder3D(gr, rect, Border3DStyle.Sunken); } else { gr.FillRectangle(Brushes.LightGray, rect); ControlPaint.DrawBorder3D(gr, rect, Border3DStyle.Raised); } // Draw the picture. rectf.Inflate(-5, -5); rectf = PositionImage(Picture, rectf); gr.DrawImage(Picture, rectf); }

The Draw method creates a rectangle to represent the node's location. If the node is selected, it fills the rectangle with white and then calls ControlPaint.DrawBorder to outline the node in a sunken border. If the node is not selected, it fills the rectangle light gray white and then calls ControlPaint.DrawBorder to outline the node in a raised border.

Next the method uses the rectangle object's Inflate method to increase its size by -5. What Inflate does is add the indicated amount to every edge of the rectangle. In this case, the amount is -5, so the method shaves off 5 pixels from each of the rectangle's edges.

The method then calls PositionImage method to figure out where it should draw the node's picture. It finishes by drawing the image. The following code shows the PositionImage method.

// Find a rectangle to draw the image centered in the // rectangle as large as possible without stretching. private RectangleF PositionImage(Image picture, RectangleF rect) { // Get the X and Y scales. float pic_wid = picture.Width; float pic_hgt = picture.Height; float pic_aspect = pic_wid / pic_hgt; float rect_aspect = rect.Width / rect.Height; float scale = 1; if (pic_aspect > rect_aspect) { scale = rect.Width / pic_wid; } else { scale = rect.Height / pic_hgt; } // See where we need to draw. pic_wid *= scale; pic_hgt *= scale; RectangleF drawing_rect = new RectangleF( rect.X + (rect.Width - pic_wid) / 2, rect.Y + (rect.Height - pic_hgt) / 2, pic_wid, pic_hgt); return drawing_rect; }

This method finds a rectangle within a target rectangle that can make the image as large as possible without stretching it out of shape.

That's all there is to the PictureNode class. The code that arranges the nodes is contained in the TreeNode class. See the earlier posts for information about how that works.

Download the example to experiment with it and to see additional details.

© 2009-2023 Rocky Mountain Computer Consulting, Inc. All rights reserved.