using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Design; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; using System.ComponentModel; using System.ComponentModel.Design; using System.ComponentModel.Design.Serialization; using System.Diagnostics; namespace DataGridTreeView ...{ /**////<summary> /// Summary description for DataGridTreeView ///</summary> [System.ComponentModel.DesignerCategory("code"), Designer(typeof(System.Windows.Forms.Design.ControlDesigner)), ComplexBindingProperties(), Docking(DockingBehavior.Ask)] publicclass TreeGridView : DataGridView ...{ privateint _indentWidth; private TreeGridNode _root; private TreeGridColumn _expandableColumn; privatebool _disposing =false; private ImageList _imageList; privatebool _inExpandCollapse =false; internalbool _inExpandCollapseMouseCapture =false; private Control hideScrollBarControl; privatebool _showLines =true; privatebool _virtualNodes =false; internal VisualStyleRenderer rOpen =new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened); internal VisualStyleRenderer rClose =new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed); public TreeGridView() ...{ this.EditMode = DataGridViewEditMode.EditProgrammatically; this.RowTemplate =new TreeGridNode() as DataGridViewRow; this.AllowUserToAddRows =false; this.AllowUserToDeleteRows =false; this._root =new TreeGridNode(this); this._root.IsRoot =true; this.Rows.CollectionChanged +=delegate(object sender, System.ComponentModel.CollectionChangeEventArgs e) ...{ }; } protectedoverridevoid OnKeyDown(KeyEventArgs e) ...{ base.OnKeyDown(e); if (!e.Handled) ...{ if (e.KeyCode == Keys.F2 &&this.CurrentCellAddress.X >-1&&this.CurrentCellAddress.Y >-1) ...{ if (!this.CurrentCell.Displayed) ...{ this.FirstDisplayedScrollingRowIndex =this.CurrentCellAddress.Y; } else ...{ //TODO } this.SelectionMode = DataGridViewSelectionMode.CellSelect; this.BeginEdit(true); } elseif (e.KeyCode == Keys.Enter &&!this.IsCurrentCellInEditMode) ...{ this.SelectionMode = DataGridViewSelectionMode.FullRowSelect; this.CurrentCell.OwningRow.Selected =true; } } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] publicnewobject DataSource ...{ get...{ returnnull; } set ...{ thrownew NotSupportedException("The TreeGridView doesn't support databinding!"); } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] publicnewobject DataMember ...{ get...{ returnnull; } set...{ thrownew NotSupportedException("The TreeGridView does not support databinding"); } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] publicnew DataGridViewRowCollection Rows ...{ get...{ returnbase.Rows; } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] publicnewbool VirtualMode ...{ get...{ returnfalse; } set...{ thrownew NotSupportedException("The TreeGridView does not support virtual mode"); } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] publicnew DataGridViewRow RowTemplate ...{ get...{ returnbase.RowTemplate; } set...{ base.RowTemplate = value; } } [Description("Returns the TreeGridNode for the given DataGridViewRow")] public TreeGridNode GetNodeForRow(DataGridViewRow row) ...{ return row as TreeGridNode; } [Description("Returns the TreeGridNode for the given DataGridViewRow")] public TreeGridNode GetNodeForRow(int index) ...{ return GetNodeForRow(base.Rows[index]); } [Category("Data"), Description("The collection of root nodes in the treelist."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(CollectionEditor), typeof(UITypeEditor))] public TreeGridNodeCollection Nodes ...{ get ...{ returnthis._root.Nodes; } } publicnew TreeGridNode CurrentRow ...{ get ...{ returnbase.CurrentRow as TreeGridNode; } } [DefaultValue(false), Description("Causes nodes to always show as expandable. Use the NodeExpanding event to add nodes.")] publicbool VirtualNodes ...{ get...{ return _virtualNodes; } set...{ _virtualNodes = value; } } public TreeGridNode CurrentNode ...{ get ...{ returnthis.CurrentRow; } } [DefaultValue(true)] publicbool ShowLines ...{ get...{ returnthis._showLines; } set...{ if (value !=this._showLines) ...{ this._showLines = value; this.Invalidate(); } } } public ImageList ImageList ...{ get...{ returnthis._imageList; } set...{ this._imageList = value; } } publicnewint RowCount ...{ get...{ returnthis.Nodes.Count; } set ...{ for (int i =0; i < value; i++) this.Nodes.Add(new TreeGridNode()); } } Site nodes and collapse/expand support#region Site nodes and collapse/expand support protectedoverridevoid OnRowsAdded(DataGridViewRowsAddedEventArgs e) ...{ base.OnRowsAdded(e); // Notify the row when it is added to the base grid int count = e.RowCount -1; TreeGridNode row; while (count >=0) ...{ row =base.Rows[e.RowIndex + count] as TreeGridNode; if (row !=null) ...{ row.Sited(); } count--; } } internalprotectedvoid UnSiteAll() ...{ this.UnSiteNode(this._root); } internalprotectedvirtualvoid UnSiteNode(TreeGridNode node) ...{ if (node.IsSited || node.IsRoot) ...{ // remove child rows first foreach (TreeGridNode childNode in node.Nodes) ...{ this.UnSiteNode(childNode); } // now remove this row except for the root if (!node.IsRoot) ...{ base.Rows.Remove(node); // Row isn't sited in the grid anymore after remove. Note that we cannot // Use the RowRemoved event since we cannot map from the row index to // the index of the expandable row/node. node.UnSited(); } } } internalprotectedvirtualbool CollapseNode(TreeGridNode node) ...{ if (node.IsExpanded) ...{ CollapsingEventArgs exp =new CollapsingEventArgs(node); this.OnNodeCollapsing(exp); if (!exp.Cancel) ...{ this.LockVerticalScrollBarUpdate(true); this.SuspendLayout(); _inExpandCollapse =true; node.IsExpanded =false; foreach (TreeGridNode childNode in node.Nodes) ...{ Debug.Assert(childNode.RowIndex !=-1, "Row is NOT in the grid."); this.UnSiteNode(childNode); } CollapsedEventArgs exped =new CollapsedEventArgs(node); this.OnNodeCollapsed(exped); //TODO: Convert this to a specific NodeCell property _inExpandCollapse =false; this.LockVerticalScrollBarUpdate(false); this.ResumeLayout(true); this.InvalidateCell(node.Cells[0]); } return!exp.Cancel; } else ...{ // row isn't expanded, so we didn't do anything. returnfalse; } } internalprotectedvirtualvoid SiteNode(TreeGridNode node) ...{ //TODO: Raise exception if parent node is not the root or is not sited. int rowIndex =-1; TreeGridNode currentRow; node._grid =this; if (node.Parent !=null&& node.Parent.IsRoot ==false) ...{ // row is a child Debug.Assert(node.Parent !=null&& node.Parent.IsExpanded ==true); if (node.Index >0) ...{ currentRow = node.Parent.Nodes[node.Index -1]; } else ...{ currentRow = node.Parent; } } else ...{ // row is being added to the root if (node.Index >0) ...{ currentRow = node.Parent.Nodes[node.Index -1]; } else ...{ currentRow =null; } } if (currentRow !=null) ...{ while (currentRow.Level >= node.Level) ...{ if (currentRow.RowIndex <base.Rows.Count -1) ...{ currentRow =base.Rows[currentRow.RowIndex +1] as TreeGridNode; Debug.Assert(currentRow !=null); } else // no more rows, site this node at the end. break; } if (currentRow == node.Parent) rowIndex = currentRow.RowIndex +1; elseif (currentRow.Level < node.Level) rowIndex = currentRow.RowIndex; else rowIndex = currentRow.RowIndex +1; } else rowIndex =0; Debug.Assert(rowIndex !=-1); this.SiteNode(node, rowIndex); Debug.Assert(node.IsSited); if (node.IsExpanded) ...{ // add all child rows to display foreach (TreeGridNode childNode in node.Nodes) ...{ //TODO: could use the more efficient SiteRow with index. this.SiteNode(childNode); } } } internalprotectedvirtualvoid SiteNode(TreeGridNode node, int index) ...{ if (index <base.Rows.Count) ...{ base.Rows.Insert(index, node); } else ...{ // for the last item. base.Rows.Add(node); } } internalprotectedvirtualbool ExpandNode(TreeGridNode node) ...{ if (!node.IsExpanded ||this._virtualNodes) ...{ ExpandingEventArgs exp =new ExpandingEventArgs(node); this.OnNodeExpanding(exp); if (!exp.Cancel) ...{ this.LockVerticalScrollBarUpdate(true); this.SuspendLayout(); _inExpandCollapse =true; node.IsExpanded =true; //TODO Convert this to a InsertRange foreach (TreeGridNode childNode in node.Nodes) ...{ Debug.Assert(childNode.RowIndex ==-1, "Row is already in the grid."); this.SiteNode(childNode); //this.BaseRows.Insert(rowIndex + 1, childRow); //TODO : remove -- just a test. //childNode.Cells[0].Value = "child"; } ExpandedEventArgs exped =new ExpandedEventArgs(node); this.OnNodeExpanded(exped); //TODO: Convert this to a specific NodeCell property _inExpandCollapse =false; this.LockVerticalScrollBarUpdate(false); this.ResumeLayout(true); this.InvalidateCell(node.Cells[0]); } return!exp.Cancel; } else ...{ // row is already expanded, so we didn't do anything. returnfalse; } } protectedoverridevoid OnMouseUp(MouseEventArgs e) ...{ // used to keep extra mouse moves from selecting more rows when collapsing base.OnMouseUp(e); this._inExpandCollapseMouseCapture =false; } protectedoverridevoid OnMouseMove(MouseEventArgs e) ...{ // while we are expanding and collapsing a node mouse moves are // supressed to keep selections from being messed up. if (!this._inExpandCollapseMouseCapture) base.OnMouseMove(e); } #endregion Collapse/Expand events#region Collapse/Expand events publicevent ExpandingEventHandler NodeExpanding; publicevent ExpandedEventHandler NodeExpanded; publicevent CollapsingEventHandler NodeCollapsing; publicevent CollapsedEventHandler NodeCollapsed; protectedvirtualvoid OnNodeExpanding(ExpandingEventArgs e) ...{ if (this.NodeExpanding !=null) ...{ NodeExpanding(this, e); } } protectedvirtualvoid OnNodeExpanded(ExpandedEventArgs e) ...{ if (this.NodeExpanded !=null) ...{ NodeExpanded(this, e); } } protectedvirtualvoid OnNodeCollapsing(CollapsingEventArgs e) ...{ if (this.NodeCollapsing !=null) ...{ NodeCollapsing(this, e); } } protectedvirtualvoid OnNodeCollapsed(CollapsedEventArgs e) ...{ if (this.NodeCollapsed !=null) ...{ NodeCollapsed(this, e); } } #endregion Helper methods#region Helper methods protectedoverridevoid Dispose(bool disposing) ...{ this._disposing =true; base.Dispose(Disposing); this.UnSiteAll(); } protectedoverridevoid OnHandleCreated(EventArgs e) ...{ base.OnHandleCreated(e); // this control is used to temporarly hide the vertical scroll bar hideScrollBarControl =new Control(); hideScrollBarControl.Visible =false; hideScrollBarControl.Enabled =false; hideScrollBarControl.TabStop =false; // control is disposed automatically when the grid is disposed this.Controls.Add(hideScrollBarControl); } protectedoverridevoid OnRowEnter(DataGridViewCellEventArgs e) ...{ // ensure full row select base.OnRowEnter(e); if (this.SelectionMode == DataGridViewSelectionMode.CellSelect || (this.SelectionMode == DataGridViewSelectionMode.FullRowSelect && base.Rows[e.RowIndex].Selected ==false)) ...{ this.SelectionMode = DataGridViewSelectionMode.FullRowSelect; base.Rows[e.RowIndex].Selected =true; } } privatevoid LockVerticalScrollBarUpdate(bool lockUpdate/**//*, bool delayed*/) ...{ // Temporarly hide/show the vertical scroll bar by changing its parent if (!this._inExpandCollapse) ...{ if (lockUpdate) ...{ this.VerticalScrollBar.Parent = hideScrollBarControl; } else ...{ this.VerticalScrollBar.Parent =this; } } } protectedoverridevoid OnColumnAdded(DataGridViewColumnEventArgs e) ...{ if (typeof(TreeGridColumn).IsAssignableFrom(e.Column.GetType())) ...{ if (_expandableColumn ==null) ...{ // identify the expanding column. _expandableColumn = (TreeGridColumn)e.Column; } else ...{ // this.Columns.Remove(e.Column); //throw new InvalidOperationException("Only one TreeGridColumn per TreeGridView is supported."); } } // Expandable Grid doesn't support sorting. This is just a limitation of the sample. e.Column.SortMode = DataGridViewColumnSortMode.NotSortable; base.OnColumnAdded(e); } privatestaticclass Win32Helper ...{ publicconstint WM_SYSKEYDOWN =0x0104, WM_KEYDOWN =0x0100, WM_SETREDRAW =0x000B; [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)] publicstaticextern IntPtr SendMessage(System.Runtime.InteropServices.HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam); [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)] publicstaticextern IntPtr SendMessage(System.Runtime.InteropServices.HandleRef hWnd, int msg, int wParam, int lParam); [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)] publicstaticexternbool PostMessage(System.Runtime.InteropServices.HandleRef hwnd, int msg, IntPtr wparam, IntPtr lparam); } #endregion } }
TreeGridCell.cs
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; using System.Drawing; using System.Diagnostics; namespace DataGridTreeView ...{ publicclass TreeGridCell : DataGridViewTextBoxCell ...{ privateconstint INDENT_WIDTH =20; privateconstint INDENT_MARGIN =5; privateint glyphWidth; privateint calculatedLeftPadding; internalbool IsSited; private Padding _previousPadding; privateint _imageWidth =0; privateint _imageHeight =0; privateint _imageHeightOffset =0; private Rectangle _lastKnownGlyphRect; public TreeGridCell() ...{ glyphWidth =15; calculatedLeftPadding =0; this.IsSited =false; } publicoverrideobject Clone() ...{ TreeGridCell cell = (TreeGridCell)base.Clone(); cell.glyphWidth =this.glyphWidth; cell.calculatedLeftPadding =this.calculatedLeftPadding; return cell; } internalprotectedvirtualvoid UnSited() ...{ this.IsSited =false; this.Style.Padding =this._previousPadding; } internalprotectedvirtualvoid Sited() ...{ this.IsSited =true; this._previousPadding =this.Style.Padding; this.UpdateStyle(); } internalprotectedvirtualvoid UpdateStyle() ...{ 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) ...{ _imageWidth = image.Width +2; _imageHeight = image.Height +2; } else ...{ _imageWidth = glyphWidth; _imageHeight =0; } 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; } 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; } publicint Level ...{ get ...{ TreeGridNode row =this.OwningNode; if (row !=null) ...{ return row.Level; } else ...{ return-1; } } } public TreeGridNode OwningNode ...{ get ...{ returnbase.OwningRow as TreeGridNode; } } protectedvirtualint GlyphMargin ...{ get ...{ return ((this.Level -1) * INDENT_WIDTH) + INDENT_MARGIN; } } protectedvirtualint GlyphOffset ...{ get ...{ return ((this.Level -1) * INDENT_WIDTH); } } protectedoverridevoid 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); // TODO: Indent width needs to take image size into account Rectangle glyphRect =new Rectangle(cellBounds.X +this.GlyphMargin, cellBounds.Y, INDENT_WIDTH, cellBounds.Height -1); int glyphHalf = glyphRect.Width /2; //TODO: This painting code needs to be rehashed to be cleaner int level =this.Level; //TODO: 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); } elseif (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); } elseif (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; } } } } if (node.HasChildren || node._grid.VirtualNodes) ...{ if (node.IsExpanded) node._grid.rOpen.DrawBackground(graphics, new Rectangle(glyphRect.X, glyphRect.Y + (glyphRect.Height /2) -4, 10, 10)); else node._grid.rClose.DrawBackground(graphics, new Rectangle(glyphRect.X, glyphRect.Y + (glyphRect.Height /2) -4, 10, 10)); } } protectedoverridevoid OnMouseUp(DataGridViewCellMouseEventArgs e) ...{ base.OnMouseUp(e); TreeGridNode node =this.OwningNode; if (node !=null) node._grid._inExpandCollapseMouseCapture =false; } protectedoverridevoid OnMouseDown(DataGridViewCellMouseEventArgs e) ...{ if (e.Location.X >this.InheritedStyle.Padding.Left) ...{ base.OnMouseDown(e); } else ...{ TreeGridNode node =this.OwningNode; if (node !=null) ...{ node._grid._inExpandCollapseMouseCapture =true; if (node.IsExpanded) node.Collapse(); else node.Expand(); } } } } }
TreeGridColumn.cs
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Windows.Forms.Design; using System.Diagnostics; using System.Drawing; using System.Drawing.Design; using System.ComponentModel; using System.ComponentModel.Design; using System.ComponentModel.Design.Serialization; namespace DataGridTreeView ...{ publicclass TreeGridColumn : DataGridViewTextBoxColumn ...{ internal Image _defaultNodeImage; public TreeGridColumn() ...{ this.CellTemplate =new TreeGridCell(); } publicoverrideobject Clone() ...{ TreeGridColumn column = (TreeGridColumn)base.Clone(); column._defaultNodeImage =this._defaultNodeImage; return column; } public Image DefaultNodeImage ...{ get ...{ return _defaultNodeImage; } set ...{ _defaultNodeImage = value; } } } }
TreeGridNode.cs
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using System.Drawing.Design; using System.Diagnostics; using System.ComponentModel; using System.ComponentModel.Design; using System.ComponentModel.Design.Serialization; namespace DataGridTreeView ...{ [ToolboxItem(false),DesignTimeVisible(false)] publicclass TreeGridNode : DataGridViewRow ...{ internal TreeGridView _grid; internal TreeGridNode _parent; internal TreeGridNodeCollection _owner; internalbool IsExpanded; internalbool IsRoot; internalbool _isSited; internalbool _isFirstSibling; internalbool _isLastSibling; internal Image _image; internalint _imageIndex; private Random rndSeed =new Random(); publicint UniqueValue =-1; TreeGridCell _treeCell; TreeGridNodeCollection childrenNodes; privateint _index; privateint _level; privatebool childCellsCreated =false; private ISite site =null; private EventHandler disposed =null; internal TreeGridNode(TreeGridView owner) : this() ...{ this._grid = owner; this.IsExpanded =true; } public TreeGridNode() ...{ _index =-1; _level =-1; IsExpanded =false; UniqueValue =this.rndSeed.Next(); _isSited =false; _isFirstSibling =false; _isLastSibling =false; _imageIndex =-1; } publicoverrideobject Clone() ...{ TreeGridNode row = (TreeGridNode)base.Clone(); row.UniqueValue =-1; row._level =this._level; row._grid =this._grid; row._parent =this._parent; row._imageIndex =this._imageIndex; if (row._imageIndex ==-1) ...{ row.Image =this.Image; } row.IsExpanded =this.IsExpanded; return row; } internalprotectedvirtualvoid UnSited() ...{ TreeGridCell cell; foreach (DataGridViewCell dCell inthis.Cells) ...{ cell = dCell as TreeGridCell; if (cell !=null) ...{ cell.UnSited(); } } this._isSited =false; } internalprotectedvirtualvoid Sited() ...{ this._isSited =true; this.childCellsCreated =true; Debug.Assert(this._grid !=null); TreeGridCell cell; foreach (DataGridViewCell dCell inthis.Cells) ...{ cell = dCell as TreeGridCell; if (cell !=null) ...{ cell.Sited(); } } } public Image Image ...{ get ...{ if (_image ==null&& _imageIndex !=-1) ...{ if (this.ImageList !=null&&this._imageIndex <this.ImageList.Images.Count) ...{ returnthis.ImageList.Images[this._imageIndex]; } else ...{ returnnull; } } else ...{ returnthis._image; } } set ...{ _image = value; if (_image !=null) ...{ this._imageIndex =-1; } if (this._isSited) ...{ this._treeCell.UpdateStyle(); if (this.Displayed) ...{ this._grid.InvalidateRow(this.RowIndex); } } } } [System.ComponentModel.Description("Represents the index of this row in the Grid. Advanced usage."), System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] publicint RowIndex ...{ get ...{ returnbase.Index; } } [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ImageList ImageList ...{ get ...{ if (this._grid !=null) ...{ returnthis._grid.ImageList; } else ...{ returnnull; } } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] publicnewint Index ...{ get ...{ if (_index ==-1) ...{ _index =this._owner.IndexOf(this); } return _index; } internalset ...{ _index = value; } } privatebool ShouldSerializeImageIndex() ...{ return (this._imageIndex !=-1&&this._image ==null); } [Category("Appearance"), Description("..."), DefaultValue(-1), TypeConverter(typeof(ImageIndexConverter)), Editor("System.Windows.Forms.Design.ImageIndexEditor", typeof(UITypeEditor))] publicint ImageIndex ...{ get...{ return _imageIndex; } set ...{ _imageIndex = value; if (_imageIndex !=-1) ...{ this._image =null; } if (this._isSited) ...{ this._treeCell.UpdateStyle(); if (this.Displayed) this._grid.InvalidateRow(this.RowIndex); } } } privatebool ShouldSerializeImage() ...{ return (this._imageIndex ==-1&&this._image !=null); } protectedoverride DataGridViewCellCollection CreateCellsInstance() ...{ DataGridViewCellCollection cells =base.CreateCellsInstance(); cells.CollectionChanged += cells_CollectionChanged; return cells; } void cells_CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e) ...{ if (_treeCell !=null) return; if (e.Action == System.ComponentModel.CollectionChangeAction.Add || e.Action == System.ComponentModel.CollectionChangeAction.Refresh) ...{ TreeGridCell treeCell =null; if (e.Element ==null) ...{ foreach (DataGridViewCell cell inbase.Cells) ...{ if (cell.GetType().IsAssignableFrom(typeof(TreeGridCell))) ...{ treeCell = (TreeGridCell)cell; break; } } } else ...{ treeCell = e.Element as TreeGridCell; } if (treeCell !=null) _treeCell = treeCell; } } [Category("Data"), Description("The collection of root nodes in the treelist."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Editor(typeof(CollectionEditor), typeof(UITypeEditor))] public TreeGridNodeCollection Nodes ...{ get ...{ if (childrenNodes ==null) ...{ childrenNodes =new TreeGridNodeCollection(this); } return childrenNodes; } set...{ ;} } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] publicnew DataGridViewCellCollection Cells ...{ get ...{ if (!childCellsCreated &&this.DataGridView ==null) ...{ if (this._grid ==null) returnnull; this.CreateCells(this._grid); childCellsCreated =true; } returnbase.Cells; } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] publicint Level ...{ get ...{ if (this._level ==-1) ...{ // calculate level int walk =0; TreeGridNode walkRow =this.Parent; while (walkRow !=null) ...{ walk++; walkRow = walkRow.Parent; } this._level = walk; } returnthis._level; } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public TreeGridNode Parent ...{ get ...{ returnthis._parent; } } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] publicvirtualbool HasChildren ...{ get ...{ return (this.childrenNodes !=null&&this.Nodes.Count !=0); } } [Browsable(false)] publicbool IsSited ...{ get ...{ returnthis._isSited; } } [Browsable(false)] publicbool IsFirstSibling ...{ get ...{ return (this.Index ==0); } } [Browsable(false)] publicbool IsLastSibling ...{ get ...{ TreeGridNode parent =this.Parent; if (parent !=null&& parent.HasChildren) ...{ return (this.Index == parent.Nodes.Count -1); } else returntrue; } } publicvirtualbool Collapse() ...{ returnthis._grid.CollapseNode(this); } publicvirtualbool Expand() ...{ if (this._grid !=null) returnthis._grid.ExpandNode(this); else ...{ this.IsExpanded =true; returntrue; } } internalprotectedvirtualbool InsertChildNode(int index, TreeGridNode node) ...{ node._parent =this; node._grid =this._grid; // ensure that all children of this node has their grid set if (this._grid !=null) UpdateChildNodes(node); //TODO: do we need to use index parameter? if ((this._isSited ||this.IsRoot) &&this.IsExpanded) this._grid.SiteNode(node); returntrue; } internalprotectedvirtualbool InsertChildNodes(int index, params TreeGridNode[] nodes) ...{ foreach (TreeGridNode node in nodes) ...{ this.InsertChildNode(index, node); } returntrue; } internalprotectedvirtualbool AddChildNode(TreeGridNode node) ...{ node._parent =this; node._grid =this._grid; // ensure that all children of this node has their grid set if (this._grid !=null) UpdateChildNodes(node); if ((this._isSited ||this.IsRoot) &&this.IsExpanded &&!node._isSited) this._grid.SiteNode(node); returntrue; } internalprotectedvirtualbool AddChildNodes(params TreeGridNode[] nodes) ...{ //TODO: Convert the final call into an SiteNodes?? foreach (TreeGridNode node in nodes) ...{ this.AddChildNode(node); } returntrue; } internalprotectedvirtualbool RemoveChildNode(TreeGridNode node) ...{ if ((this.IsRoot ||this._isSited) &&this.IsExpanded) ...{ //We only unsite out child node if we are sited and expanded. this._grid.UnSiteNode(node); } node._grid =null; node._parent =null; returntrue; } internalprotectedvirtualbool ClearNodes() ...{ if (this.HasChildren) ...{ for (int i =this.Nodes.Count -1; i >=0; i--) ...{ this.Nodes.RemoveAt(i); } } returntrue; } [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) ] publicevent EventHandler Disposed ...{ add ...{ this.disposed += value; } remove ...{ this.disposed -= value; } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public ISite Site ...{ get ...{ returnthis.site; } set ...{ this.site = value; } } privatevoid UpdateChildNodes(TreeGridNode node) ...{ if (node.HasChildren) ...{ foreach (TreeGridNode childNode in node.Nodes) ...{ childNode._grid = node._grid; this.UpdateChildNodes(childNode); } } } publicoverridestring ToString() ...{ StringBuilder sb =new StringBuilder(36); sb.Append("TreeGridNode { Index="); sb.Append(this.RowIndex.ToString(System.Globalization.CultureInfo.CurrentCulture)); sb.Append(" }"); return sb.ToString(); } //protected override void Dispose(bool disposing) { // if (disposing) // { // lock(this) // { // if (this.site != null && this.site.Container != null) // { // this.site.Container.Remove(this); // } // if (this.disposed != null) // { // this.disposed(this, EventArgs.Empty); // } // } // } // base.Dispose(disposing); //} } }
TreeGridNodeCollection.cs
using System; using System.Collections.Generic; using System.Text; namespace DataGridTreeView ...{ publicclass TreeGridNodeCollection : System.Collections.Generic.IList<TreeGridNode>, System.Collections.IList ...{ internal System.Collections.Generic.List<TreeGridNode> _list; internal TreeGridNode _owner; internal TreeGridNodeCollection(TreeGridNode owner) ...{ this._owner = owner; this._list =new List<TreeGridNode>(); } Public Members#region Public Members publicvoid Add(TreeGridNode item) ...{ // The row needs to exist in the child collection before the parent is notified. item._grid =this._owner._grid; bool hadChildren =this._owner.HasChildren; item._owner =this; this._list.Add(item); this._owner.AddChildNode(item); // if the owner didn't have children but now does (asserted) and it is sited update it if (!hadChildren &&this._owner.IsSited) ...{ this._owner._grid.InvalidateRow(this._owner.RowIndex); } } public TreeGridNode Add(string text) ...{ TreeGridNode node =new TreeGridNode(); this.Add(node); node.Cells[0].Value = text; return node; } public TreeGridNode Add(paramsobject[] values) ...{ TreeGridNode node =new TreeGridNode(); this.Add(node); int cell =0; if (values.Length > node.Cells.Count) thrownew ArgumentOutOfRangeException("values"); foreach (object o in values) ...{ node.Cells[cell].Value = o; cell++; } return node; } publicvoid Insert(int index, TreeGridNode item) ...{ // The row needs to exist in the child collection before the parent is notified. item._grid =this._owner._grid; item._owner =this; this._list.Insert(index, item); this._owner.InsertChildNode(index, item); } publicbool Remove(TreeGridNode item) ...{ // The parent is notified first then the row is removed from the child collection. this._owner.RemoveChildNode(item); item._grid =null; returnthis._list.Remove(item); } publicvoid RemoveAt(int index) ...{ TreeGridNode row =this._list[index]; // The parent is notified first then the row is removed from the child collection. this._owner.RemoveChildNode(row); row._grid =null; this._list.RemoveAt(index); } publicvoid Clear() ...{ // The parent is notified first then the row is removed from the child collection. this._owner.ClearNodes(); this._list.Clear(); } publicint IndexOf(TreeGridNode item) ...{ returnthis._list.IndexOf(item); } public TreeGridNode this[int index] ...{ get ...{ returnthis._list[index]; } set ...{ thrownew Exception("The method or operation is not implemented."); } } publicbool Contains(TreeGridNode item) ...{ returnthis._list.Contains(item); } publicvoid CopyTo(TreeGridNode[] array, int arrayIndex) ...{ thrownew Exception("The method or operation is not implemented."); } publicint Count ...{ get...{ returnthis._list.Count; } } publicbool IsReadOnly ...{ get...{ returnfalse; } } #endregion IList Interface#region IList Interface void System.Collections.IList.Remove(object value) ...{ this.Remove(value as TreeGridNode); } int System.Collections.IList.Add(object value) ...{ TreeGridNode item = value as TreeGridNode; this.Add(item); return item.Index; } void System.Collections.IList.RemoveAt(int index) ...{ this.RemoveAt(index); } void System.Collections.IList.Clear() ...{ this.Clear(); } bool System.Collections.IList.IsReadOnly ...{ get...{ returnthis.IsReadOnly; } } bool System.Collections.IList.IsFixedSize ...{ get...{ returnfalse; } } int System.Collections.IList.IndexOf(object item) ...{ returnthis.IndexOf(item as TreeGridNode); } void System.Collections.IList.Insert(int index, object value) ...{ this.Insert(index, value as TreeGridNode); } int System.Collections.ICollection.Count ...{ get...{ returnthis.Count; } } bool System.Collections.IList.Contains(object value) ...{ returnthis.Contains(value as TreeGridNode); } void System.Collections.ICollection.CopyTo(Array array, int index) ...{ thrownew Exception("The method or operation is not implemented."); } object System.Collections.IList.this[int index] ...{ get ...{ returnthis[index]; } set ...{ thrownew Exception("The method or operation is not implemented."); } } IEnumerable Members#region IEnumerable<ExpandableRow> Members public IEnumerator<TreeGridNode> GetEnumerator() ...{ returnthis._list.GetEnumerator(); } #endregion IEnumerable Members#region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() ...{ returnthis.GetEnumerator(); } #endregion #endregion ICollection Members#region ICollection Members bool System.Collections.ICollection.IsSynchronized ...{ get...{ thrownew Exception("The method or operation is not implemented."); } } object System.Collections.ICollection.SyncRoot ...{ get...{ thrownew Exception("The method or operation is not implemented."); } } #endregion } }