using System; using System.Windows.Forms; using System.Drawing; using System.Windows.Forms.VisualStyles; using System.Diagnostics; namespace BismNormalizer.TabularCompare.UI { /// /// Summary description for TreeGridCell. /// public class TreeGridCell:DataGridViewTextBoxCell { private const int INDENT_WIDTH = 20; private const int INDENT_MARGIN = 5; private int glyphWidth; private int calculatedLeftPadding; internal bool IsSited; private Padding _previousPadding; private int _imageWidth = 0, _imageHeight = 0, _imageHeightOffset = 0; public TreeGridCell() { glyphWidth = 15; calculatedLeftPadding = 0; this.IsSited = false; } public override object Clone() { TreeGridCell c = (TreeGridCell)base.Clone(); c.glyphWidth = this.glyphWidth; c.calculatedLeftPadding = this.calculatedLeftPadding; return c; } internal protected virtual void UnSited() { // The row this cell is in is being removed from the grid. this.IsSited = false; this.Style.Padding = this._previousPadding; } internal protected virtual void Sited() { // when we are added to the DGV we can realize our style this.IsSited = true; // remember what the previous padding size is so it can be restored when unsiting this._previousPadding = this.Style.Padding; this.UpdateStyle(); } internal protected virtual void UpdateStyle(){ // styles shouldn't be modified when we are not sited. if (this.IsSited == false) return; int level = this.Level; Padding p = this._previousPadding; Size preferredSize; using (Graphics g = this.OwningNode._grid.CreateGraphics() ) { preferredSize =this.GetPreferredSize(g, this.InheritedStyle, this.RowIndex, new Size(0, 0)); } Image image = this.OwningNode.Image; if (image != null) { // calculate image size _imageWidth = image.Width+2; _imageHeight = image.Height+2; } else { _imageWidth = glyphWidth; _imageHeight = 0; } //Commented out after found KPI Lake performance issue //// TO_DO: Make this cleaner //if (preferredSize.Height < _imageHeight) //{ // this.Style.Padding = new Padding(p.Left + (level * INDENT_WIDTH) + _imageWidth + INDENT_MARGIN, // p.Top + (_imageHeight / 2), p.Right, p.Bottom + (_imageHeight / 2)); // _imageHeightOffset = 2;// (_imageHeight - preferredSize.Height) / 2; //} //else //{ // this.Style.Padding = new Padding(p.Left + (level * INDENT_WIDTH) + _imageWidth + INDENT_MARGIN, // p.Top , p.Right, p.Bottom ); //} calculatedLeftPadding = ((level - 1) * glyphWidth) + _imageWidth + INDENT_MARGIN; } public int Level { get { TreeGridNode row = this.OwningNode; if (row != null) { return row.Level; } else return -1; } } protected virtual int GlyphMargin => ((this.Level - 1) * INDENT_WIDTH) + INDENT_MARGIN; protected virtual int GlyphOffset => ((this.Level - 1) * INDENT_WIDTH); protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) { TreeGridNode node = this.OwningNode; if (node == null) return; Image image = node.Image; if (this._imageHeight == 0 && image != null) this.UpdateStyle(); // paint the cell normally base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts); float dpiFactor = Utils.GetDpiFactor(); int scaleBack = (dpiFactor > 1 ? Convert.ToInt32(dpiFactor) : 0); // TO_DO: Indent width needs to take image size into account Rectangle glyphRect = new Rectangle(cellBounds.X + this.GlyphMargin - scaleBack, cellBounds.Y - scaleBack, INDENT_WIDTH, cellBounds.Height - 1); int glyphHalf = glyphRect.Width / 2; //TO_DO: This painting code needs to be rehashed to be cleaner int level = this.Level; //TO_DO: Rehash this to take different Imagelayouts into account. This will speed up drawing // for images of the same size (ImageLayout.None) if (image != null) { Point pp; if (_imageHeight > cellBounds.Height) pp = new Point(glyphRect.X + this.glyphWidth, cellBounds.Y + _imageHeightOffset); else pp = new Point(glyphRect.X + this.glyphWidth, (cellBounds.Height / 2 - _imageHeight / 2) + cellBounds.Y); // Graphics container to push/pop changes. This enables us to set clipping when painting // the cell's image -- keeps it from bleeding outsize of cells. System.Drawing.Drawing2D.GraphicsContainer gc = graphics.BeginContainer(); { graphics.SetClip(cellBounds); graphics.DrawImageUnscaled(image, pp); } graphics.EndContainer(gc); } // Paint tree lines if (node._grid.ShowLines) { using (Pen linePen = new Pen(SystemBrushes.ControlDark, 1.0f)) { linePen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot; bool isLastSibling = node.IsLastSibling; bool isFirstSibling = node.IsFirstSibling; if (node.Level == 1) { // the Root nodes display their lines differently if (isFirstSibling && isLastSibling) { // only node, both first and last. Just draw horizontal line graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2, glyphRect.Right, cellBounds.Top + cellBounds.Height / 2); } else if (isLastSibling) { // last sibling doesn't draw the line extended below. Paint horizontal then vertical graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2, glyphRect.Right, cellBounds.Top + cellBounds.Height / 2); graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2); } else if (isFirstSibling) { // first sibling doesn't draw the line extended above. Paint horizontal then vertical graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2, glyphRect.Right, cellBounds.Top + cellBounds.Height / 2); graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2, glyphRect.X + 4, cellBounds.Bottom); } else { // normal drawing draws extended from top to bottom. Paint horizontal then vertical graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2, glyphRect.Right, cellBounds.Top + cellBounds.Height / 2); graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top, glyphRect.X + 4, cellBounds.Bottom); } } else { if (isLastSibling) { // last sibling doesn't draw the line extended below. Paint horizontal then vertical graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2, glyphRect.Right, cellBounds.Top + cellBounds.Height / 2); graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2); } else { // normal drawing draws extended from top to bottom. Paint horizontal then vertical graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top + cellBounds.Height / 2, glyphRect.Right, cellBounds.Top + cellBounds.Height / 2); graphics.DrawLine(linePen, glyphRect.X + 4, cellBounds.Top, glyphRect.X + 4, cellBounds.Bottom); } // paint lines of previous levels to the root TreeGridNode previousNode = node.Parent; int horizontalStop = (glyphRect.X + 4) - INDENT_WIDTH; while (!previousNode.IsRoot) { if (previousNode.HasChildren && !previousNode.IsLastSibling) { // paint vertical line graphics.DrawLine(linePen, horizontalStop, cellBounds.Top, horizontalStop, cellBounds.Bottom); } previousNode = previousNode.Parent; horizontalStop = horizontalStop - INDENT_WIDTH; } } } } //Commented out after found KPI Lake performance issue // if (dpiFactor > 1) dpiFactor = dpiFactor * HighDPIUtils.PrimaryFudgeFactor; // float size = 10 * dpiFactor; //if (node.HasChildren || node._grid.VirtualNodes) //{ // if (((TreeGridView)this.DataGridView).ImageList.Images.Count > 0) // { // // Paint node glyphs // if (node.IsExpanded) // { // graphics.DrawImage(((TreeGridView)this.DataGridView).ImageList.Images[10], glyphRect.X, glyphRect.Y + (glyphRect.Height / 2) - 4, size, size); // } // else // { // graphics.DrawImage(((TreeGridView)this.DataGridView).ImageList.Images[9], glyphRect.X, glyphRect.Y + (glyphRect.Height / 2) - 4, size, size); // } // } //} } protected override void OnMouseUp(DataGridViewCellMouseEventArgs e) { base.OnMouseUp(e); TreeGridNode node = this.OwningNode; if (node != null) node._grid._inExpandCollapseMouseCapture = false; } protected override void OnMouseDown(DataGridViewCellMouseEventArgs e) { if (e.Location.X > this.InheritedStyle.Padding.Left) { base.OnMouseDown(e); } else { // Expand the node //TO_DO: Calculate more precise location TreeGridNode node = this.OwningNode; if (node != null) { node._grid._inExpandCollapseMouseCapture = true; if (node.IsExpanded) node.Collapse(); else node.Expand(); } } } public TreeGridNode OwningNode => base.OwningRow as TreeGridNode; } public class TreeGridColumn : DataGridViewTextBoxColumn { internal Image _defaultNodeImage; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public TreeGridColumn() { this.CellTemplate = new TreeGridCell(); } // Need to override Clone for design-time support. public override object Clone() { TreeGridColumn c = (TreeGridColumn)base.Clone(); c._defaultNodeImage = this._defaultNodeImage; return c; } public Image DefaultNodeImage { get { return _defaultNodeImage; } set { _defaultNodeImage = value; } } } }