using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; using System.Drawing.Drawing2D; namespace BrightIdeasSoftware { public partial class TreeListView { /// /// This class handles drawing the tree structure of the primary column. /// public class TreeRenderer : HighlightTextRenderer { /// /// Create a TreeRenderer /// public TreeRenderer() { this.LinePen = new Pen(Color.Blue, 1.0f); this.LinePen.DashStyle = DashStyle.Dot; } /// /// Return the branch that the renderer is currently drawing. /// private Branch Branch { get { return this.TreeListView.TreeModel.GetBranch(this.RowObject); } } /// /// Return the pen that will be used to draw the lines between branches /// public Pen LinePen { get { return linePen; } set { linePen = value; } } private Pen linePen; /// /// Return the TreeListView for which the renderer is being used. /// public TreeListView TreeListView { get { return (TreeListView)this.ListView; } } /// /// Should the renderer draw lines connecting siblings? /// public bool IsShowLines { get { return isShowLines; } set { isShowLines = value; } } private bool isShowLines = true; /// /// How many pixels will be reserved for each level of indentation? /// public static int PIXELS_PER_LEVEL = 16 + 1; /// /// The real work of drawing the tree is done in this method /// /// /// public override void Render(System.Drawing.Graphics g, System.Drawing.Rectangle r) { this.DrawBackground(g, r); Branch br = this.Branch; Rectangle paddedRectangle = this.ApplyCellPadding(r); Rectangle expandGlyphRectangle = paddedRectangle; expandGlyphRectangle.Offset((br.Level - 1) * PIXELS_PER_LEVEL, 0); expandGlyphRectangle.Width = PIXELS_PER_LEVEL; expandGlyphRectangle.Height = PIXELS_PER_LEVEL; expandGlyphRectangle.Y = this.AlignVertically(paddedRectangle, expandGlyphRectangle); int expandGlyphRectangleMidVertical = expandGlyphRectangle.Y + (expandGlyphRectangle.Height/2); if (this.IsShowLines) this.DrawLines(g, r, this.LinePen, br, expandGlyphRectangleMidVertical); if (br.CanExpand) this.DrawExpansionGlyph(g, expandGlyphRectangle, br.IsExpanded); int indent = br.Level * PIXELS_PER_LEVEL; paddedRectangle.Offset(indent, 0); paddedRectangle.Width -= indent; this.DrawImageAndText(g, paddedRectangle); } /// /// Draw the expansion indicator /// /// /// /// protected virtual void DrawExpansionGlyph(Graphics g, Rectangle r, bool isExpanded) { if (this.UseStyles) { this.DrawExpansionGlyphStyled(g, r, isExpanded); } else { this.DrawExpansionGlyphManual(g, r, isExpanded); } } /// /// Gets whether or not we should render using styles /// protected virtual bool UseStyles { get { return !this.IsPrinting && Application.RenderWithVisualStyles; } } /// /// Draw the expansion indicator using styles /// /// /// /// protected virtual void DrawExpansionGlyphStyled(Graphics g, Rectangle r, bool isExpanded) { VisualStyleElement element = VisualStyleElement.TreeView.Glyph.Closed; if (isExpanded) element = VisualStyleElement.TreeView.Glyph.Opened; VisualStyleRenderer renderer = new VisualStyleRenderer(element); renderer.DrawBackground(g, r); } /// /// Draw the expansion indicator without using styles /// /// /// /// protected virtual void DrawExpansionGlyphManual(Graphics g, Rectangle r, bool isExpanded) { int h = 8; int w = 8; int x = r.X + 4; int y = r.Y + (r.Height / 2) - 4; g.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h); g.FillRectangle(Brushes.White, x + 1, y + 1, w - 1, h - 1); g.DrawLine(Pens.Black, x + 2, y + 4, x + w - 2, y + 4); if (!isExpanded) g.DrawLine(Pens.Black, x + 4, y + 2, x + 4, y + h - 2); } /// /// Draw the lines of the tree /// /// /// /// /// /// protected virtual void DrawLines(Graphics g, Rectangle r, Pen p, Branch br, int glyphMidVertical) { Rectangle r2 = r; r2.Width = PIXELS_PER_LEVEL; // Vertical lines have to start on even points, otherwise the dotted line looks wrong. // This is only needed if pen is dotted. int top = r2.Top; //if (p.DashStyle == DashStyle.Dot && (top & 1) == 0) // top += 1; // Draw lines for ancestors int midX; IList ancestors = br.Ancestors; foreach (Branch ancestor in ancestors) { if (!ancestor.IsLastChild && !ancestor.IsOnlyBranch) { midX = r2.Left + r2.Width / 2; g.DrawLine(p, midX, top, midX, r2.Bottom); } r2.Offset(PIXELS_PER_LEVEL, 0); } // Draw lines for this branch midX = r2.Left + r2.Width / 2; // Horizontal line first g.DrawLine(p, midX, glyphMidVertical, r2.Right, glyphMidVertical); // Vertical line second if (br.IsFirstBranch) { if (!br.IsLastChild && !br.IsOnlyBranch) g.DrawLine(p, midX, glyphMidVertical, midX, r2.Bottom); } else { if (br.IsLastChild) g.DrawLine(p, midX, top, midX, glyphMidVertical); else g.DrawLine(p, midX, top, midX, r2.Bottom); } } /// /// Do the hit test /// /// /// /// /// protected override void HandleHitTest(Graphics g, OlvListViewHitTestInfo hti, int x, int y) { Branch br = this.Branch; Rectangle r = this.Bounds; if (br.CanExpand) { r.Offset((br.Level - 1) * PIXELS_PER_LEVEL, 0); r.Width = PIXELS_PER_LEVEL; if (r.Contains(x, y)) { hti.HitTestLocation = HitTestLocation.ExpandButton; return; } } r = this.Bounds; int indent = br.Level * PIXELS_PER_LEVEL; r.X += indent; r.Width -= indent; // Ignore events in the indent zone if (x < r.Left) { hti.HitTestLocation = HitTestLocation.Nothing; } else { this.StandardHitTest(g, hti, r, x, y); } } /// /// Calculate the edit rect /// /// /// /// /// /// /// protected override Rectangle HandleGetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize) { return this.StandardGetEditRectangle(g, cellBounds, preferredSize); } } } }