TreeGridView.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. //---------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. //
  5. //THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
  6. //KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  7. //IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  8. //PARTICULAR PURPOSE.
  9. //---------------------------------------------------------------------
  10. using System;
  11. using System.Windows.Forms;
  12. using System.Drawing;
  13. using System.Diagnostics;
  14. using System.Windows.Forms.VisualStyles;
  15. using System.ComponentModel;
  16. using System.ComponentModel.Design;
  17. using System.ComponentModel.Design.Serialization;
  18. using System.Drawing.Design;
  19. namespace AdvancedDataGridView
  20. {
  21. /// <summary>
  22. /// Summary description for TreeGridView.
  23. /// </summary>
  24. [System.ComponentModel.DesignerCategory("code"),
  25. Designer(typeof(System.Windows.Forms.Design.ControlDesigner)),
  26. ComplexBindingProperties(),
  27. Docking(DockingBehavior.Ask)]
  28. public class TreeGridView : DataGridView
  29. {
  30. private int _indentWidth;
  31. private TreeGridNode _root;
  32. private TreeGridColumn _expandableColumn;
  33. private bool _disposing = false;
  34. internal ImageList _imageList;
  35. private bool _inExpandCollapse = false;
  36. internal bool _inExpandCollapseMouseCapture = false;
  37. private Control hideScrollBarControl;
  38. private bool _showLines = true;
  39. private bool _virtualNodes = false;
  40. internal VisualStyleRenderer rOpen = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened);
  41. internal VisualStyleRenderer rClosed = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed);
  42. #region Constructor
  43. public TreeGridView()
  44. {
  45. // Control when edit occurs because edit mode shouldn't start when expanding/collapsing
  46. this.EditMode = DataGridViewEditMode.EditProgrammatically;
  47. this.RowTemplate = new TreeGridNode() as DataGridViewRow;
  48. // This sample does not support adding or deleting rows by the user.
  49. this.AllowUserToAddRows = false;
  50. this.AllowUserToDeleteRows = false;
  51. this._root = new TreeGridNode(this);
  52. this._root.IsRoot = true;
  53. // Ensures that all rows are added unshared by listening to the CollectionChanged event.
  54. base.Rows.CollectionChanged += delegate(object sender, System.ComponentModel.CollectionChangeEventArgs e) { };
  55. }
  56. #endregion
  57. #region Keyboard F2 to begin edit support
  58. protected override void OnKeyDown(KeyEventArgs e)
  59. {
  60. // Cause edit mode to begin since edit mode is disabled to support
  61. // expanding/collapsing
  62. base.OnKeyDown(e);
  63. if (!e.Handled)
  64. {
  65. if (e.KeyCode == Keys.F2 && this.CurrentCellAddress.X > -1 && this.CurrentCellAddress.Y > -1)
  66. {
  67. if (!this.CurrentCell.Displayed)
  68. {
  69. this.FirstDisplayedScrollingRowIndex = this.CurrentCellAddress.Y;
  70. }
  71. else
  72. {
  73. // TODO:calculate if the cell is partially offscreen and if so scroll into view
  74. }
  75. this.SelectionMode = DataGridViewSelectionMode.CellSelect;
  76. this.BeginEdit(true);
  77. }
  78. else if (e.KeyCode == Keys.Enter && !this.IsCurrentCellInEditMode)
  79. {
  80. this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
  81. this.CurrentCell.OwningRow.Selected = true;
  82. }
  83. }
  84. }
  85. #endregion
  86. #region Shadow and hide DGV properties
  87. // This sample does not support databinding
  88. [Browsable(false),
  89. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  90. EditorBrowsable(EditorBrowsableState.Never)]
  91. public new object DataSource
  92. {
  93. get { return null; }
  94. set { throw new NotSupportedException("The TreeGridView does not support databinding"); }
  95. }
  96. [Browsable(false),
  97. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  98. EditorBrowsable(EditorBrowsableState.Never)]
  99. public new object DataMember
  100. {
  101. get { return null; }
  102. set { throw new NotSupportedException("The TreeGridView does not support databinding"); }
  103. }
  104. [Browsable(false),
  105. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  106. EditorBrowsable(EditorBrowsableState.Never)]
  107. public new DataGridViewRowCollection Rows
  108. {
  109. get { return base.Rows; }
  110. }
  111. [Browsable(false),
  112. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  113. EditorBrowsable(EditorBrowsableState.Never)]
  114. public new bool VirtualMode
  115. {
  116. get { return false; }
  117. set { throw new NotSupportedException("The TreeGridView does not support virtual mode"); }
  118. }
  119. // none of the rows/nodes created use the row template, so it is hidden.
  120. [Browsable(false),
  121. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  122. EditorBrowsable(EditorBrowsableState.Never)]
  123. public new DataGridViewRow RowTemplate
  124. {
  125. get { return base.RowTemplate; }
  126. set { base.RowTemplate = value; }
  127. }
  128. #endregion
  129. #region Public methods
  130. [Description("Returns the TreeGridNode for the given DataGridViewRow")]
  131. public TreeGridNode GetNodeForRow(DataGridViewRow row)
  132. {
  133. return row as TreeGridNode;
  134. }
  135. [Description("Returns the TreeGridNode for the given DataGridViewRow")]
  136. public TreeGridNode GetNodeForRow(int index)
  137. {
  138. return GetNodeForRow(base.Rows[index]);
  139. }
  140. #endregion
  141. #region Public properties
  142. [Category("Data"),
  143. Description("The collection of root nodes in the treelist."),
  144. DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
  145. Editor(typeof(CollectionEditor), typeof(UITypeEditor))]
  146. public TreeGridNodeCollection Nodes
  147. {
  148. get
  149. {
  150. return this._root.Nodes;
  151. }
  152. }
  153. public new TreeGridNode CurrentRow
  154. {
  155. get
  156. {
  157. return base.CurrentRow as TreeGridNode;
  158. }
  159. }
  160. [DefaultValue(false),
  161. Description("Causes nodes to always show as expandable. Use the NodeExpanding event to add nodes.")]
  162. public bool VirtualNodes
  163. {
  164. get { return _virtualNodes; }
  165. set { _virtualNodes = value; }
  166. }
  167. public TreeGridNode CurrentNode
  168. {
  169. get
  170. {
  171. return this.CurrentRow;
  172. }
  173. }
  174. [DefaultValue(true)]
  175. public bool ShowLines
  176. {
  177. get { return this._showLines; }
  178. set
  179. {
  180. if (value != this._showLines)
  181. {
  182. this._showLines = value;
  183. this.Invalidate();
  184. }
  185. }
  186. }
  187. public ImageList ImageList
  188. {
  189. get { return this._imageList; }
  190. set
  191. {
  192. this._imageList = value;
  193. //TODO: should we invalidate cell styles when setting the image list?
  194. }
  195. }
  196. public new int RowCount
  197. {
  198. get { return this.Nodes.Count; }
  199. set
  200. {
  201. for (int i = 0; i < value; i++)
  202. this.Nodes.Add(new TreeGridNode());
  203. }
  204. }
  205. #endregion
  206. #region Site nodes and collapse/expand support
  207. protected override void OnRowsAdded(DataGridViewRowsAddedEventArgs e)
  208. {
  209. base.OnRowsAdded(e);
  210. // Notify the row when it is added to the base grid
  211. int count = e.RowCount - 1;
  212. TreeGridNode row;
  213. while (count >= 0)
  214. {
  215. row = base.Rows[e.RowIndex + count] as TreeGridNode;
  216. if (row != null)
  217. {
  218. row.Sited();
  219. }
  220. count--;
  221. }
  222. }
  223. internal protected void UnSiteAll()
  224. {
  225. this.UnSiteNode(this._root);
  226. }
  227. internal protected virtual void UnSiteNode(TreeGridNode node)
  228. {
  229. if (node.IsSited || node.IsRoot)
  230. {
  231. // remove child rows first
  232. foreach (TreeGridNode childNode in node.Nodes)
  233. {
  234. this.UnSiteNode(childNode);
  235. }
  236. // now remove this row except for the root
  237. if (!node.IsRoot)
  238. {
  239. base.Rows.Remove(node);
  240. // Row isn't sited in the grid anymore after remove. Note that we cannot
  241. // Use the RowRemoved event since we cannot map from the row index to
  242. // the index of the expandable row/node.
  243. node.UnSited();
  244. }
  245. }
  246. }
  247. internal protected virtual bool CollapseNode(TreeGridNode node)
  248. {
  249. if (node.IsExpanded)
  250. {
  251. CollapsingEventArgs exp = new CollapsingEventArgs(node);
  252. this.OnNodeCollapsing(exp);
  253. if (!exp.Cancel)
  254. {
  255. this.LockVerticalScrollBarUpdate(true);
  256. this.SuspendLayout();
  257. _inExpandCollapse = true;
  258. node.IsExpanded = false;
  259. foreach (TreeGridNode childNode in node.Nodes)
  260. {
  261. Debug.Assert(childNode.RowIndex != -1, "Row is NOT in the grid.");
  262. this.UnSiteNode(childNode);
  263. }
  264. CollapsedEventArgs exped = new CollapsedEventArgs(node);
  265. this.OnNodeCollapsed(exped);
  266. //TODO: Convert this to a specific NodeCell property
  267. _inExpandCollapse = false;
  268. this.LockVerticalScrollBarUpdate(false);
  269. this.ResumeLayout(true);
  270. this.InvalidateCell(node.Cells[0]);
  271. }
  272. return !exp.Cancel;
  273. }
  274. else
  275. {
  276. // row isn't expanded, so we didn't do anything.
  277. return false;
  278. }
  279. }
  280. internal protected virtual void SiteNode(TreeGridNode node)
  281. {
  282. //TODO: Raise exception if parent node is not the root or is not sited.
  283. int rowIndex = -1;
  284. TreeGridNode currentRow;
  285. node._grid = this;
  286. if (node.Parent != null && node.Parent.IsRoot == false)
  287. {
  288. // row is a child
  289. Debug.Assert(node.Parent != null && node.Parent.IsExpanded == true);
  290. if (node.Index > 0)
  291. {
  292. currentRow = node.Parent.Nodes[node.Index - 1];
  293. }
  294. else
  295. {
  296. currentRow = node.Parent;
  297. }
  298. }
  299. else
  300. {
  301. // row is being added to the root
  302. if (node.Index > 0)
  303. {
  304. currentRow = node.Parent.Nodes[node.Index - 1];
  305. }
  306. else
  307. {
  308. currentRow = null;
  309. }
  310. }
  311. if (currentRow != null)
  312. {
  313. while (currentRow.Level >= node.Level)
  314. {
  315. if (currentRow.RowIndex < base.Rows.Count - 1)
  316. {
  317. currentRow = base.Rows[currentRow.RowIndex + 1] as TreeGridNode;
  318. Debug.Assert(currentRow != null);
  319. }
  320. else
  321. // no more rows, site this node at the end.
  322. break;
  323. }
  324. if (currentRow == node.Parent)
  325. rowIndex = currentRow.RowIndex + 1;
  326. else if (currentRow.Level < node.Level)
  327. rowIndex = currentRow.RowIndex;
  328. else
  329. rowIndex = currentRow.RowIndex + 1;
  330. }
  331. else
  332. rowIndex = 0;
  333. Debug.Assert(rowIndex != -1);
  334. this.SiteNode(node, rowIndex);
  335. Debug.Assert(node.IsSited);
  336. if (node.IsExpanded)
  337. {
  338. // add all child rows to display
  339. foreach (TreeGridNode childNode in node.Nodes)
  340. {
  341. //TODO: could use the more efficient SiteRow with index.
  342. this.SiteNode(childNode);
  343. }
  344. }
  345. }
  346. internal protected virtual void SiteNode(TreeGridNode node, int index)
  347. {
  348. if (index < base.Rows.Count)
  349. {
  350. base.Rows.Insert(index, node);
  351. }
  352. else
  353. {
  354. // for the last item.
  355. base.Rows.Add(node);
  356. }
  357. }
  358. internal protected virtual bool ExpandNode(TreeGridNode node)
  359. {
  360. if (!node.IsExpanded || this._virtualNodes)
  361. {
  362. ExpandingEventArgs exp = new ExpandingEventArgs(node);
  363. this.OnNodeExpanding(exp);
  364. if (!exp.Cancel)
  365. {
  366. this.LockVerticalScrollBarUpdate(true);
  367. this.SuspendLayout();
  368. _inExpandCollapse = true;
  369. node.IsExpanded = true;
  370. //TODO Convert this to a InsertRange
  371. foreach (TreeGridNode childNode in node.Nodes)
  372. {
  373. Debug.Assert(childNode.RowIndex == -1, "Row is already in the grid.");
  374. this.SiteNode(childNode);
  375. //this.BaseRows.Insert(rowIndex + 1, childRow);
  376. //TODO : remove -- just a test.
  377. //childNode.Cells[0].Value = "child";
  378. }
  379. ExpandedEventArgs exped = new ExpandedEventArgs(node);
  380. this.OnNodeExpanded(exped);
  381. //TODO: Convert this to a specific NodeCell property
  382. _inExpandCollapse = false;
  383. this.LockVerticalScrollBarUpdate(false);
  384. this.ResumeLayout(true);
  385. this.InvalidateCell(node.Cells[0]);
  386. }
  387. return !exp.Cancel;
  388. }
  389. else
  390. {
  391. // row is already expanded, so we didn't do anything.
  392. return false;
  393. }
  394. }
  395. protected override void OnMouseUp(MouseEventArgs e)
  396. {
  397. // used to keep extra mouse moves from selecting more rows when collapsing
  398. base.OnMouseUp(e);
  399. this._inExpandCollapseMouseCapture = false;
  400. }
  401. protected override void OnMouseMove(MouseEventArgs e)
  402. {
  403. // while we are expanding and collapsing a node mouse moves are
  404. // supressed to keep selections from being messed up.
  405. if (!this._inExpandCollapseMouseCapture)
  406. base.OnMouseMove(e);
  407. }
  408. #endregion
  409. #region Collapse/Expand events
  410. public event ExpandingEventHandler NodeExpanding;
  411. public event ExpandedEventHandler NodeExpanded;
  412. public event CollapsingEventHandler NodeCollapsing;
  413. public event CollapsedEventHandler NodeCollapsed;
  414. protected virtual void OnNodeExpanding(ExpandingEventArgs e)
  415. {
  416. if (this.NodeExpanding != null)
  417. {
  418. NodeExpanding(this, e);
  419. }
  420. }
  421. protected virtual void OnNodeExpanded(ExpandedEventArgs e)
  422. {
  423. if (this.NodeExpanded != null)
  424. {
  425. NodeExpanded(this, e);
  426. }
  427. }
  428. protected virtual void OnNodeCollapsing(CollapsingEventArgs e)
  429. {
  430. if (this.NodeCollapsing != null)
  431. {
  432. NodeCollapsing(this, e);
  433. }
  434. }
  435. protected virtual void OnNodeCollapsed(CollapsedEventArgs e)
  436. {
  437. if (this.NodeCollapsed != null)
  438. {
  439. NodeCollapsed(this, e);
  440. }
  441. }
  442. #endregion
  443. #region Helper methods
  444. protected override void Dispose(bool disposing)
  445. {
  446. this._disposing = true;
  447. base.Dispose(Disposing);
  448. this.UnSiteAll();
  449. }
  450. protected override void OnHandleCreated(EventArgs e)
  451. {
  452. base.OnHandleCreated(e);
  453. // this control is used to temporarly hide the vertical scroll bar
  454. hideScrollBarControl = new Control();
  455. hideScrollBarControl.Visible = false;
  456. hideScrollBarControl.Enabled = false;
  457. hideScrollBarControl.TabStop = false;
  458. // control is disposed automatically when the grid is disposed
  459. this.Controls.Add(hideScrollBarControl);
  460. }
  461. protected override void OnRowEnter(DataGridViewCellEventArgs e)
  462. {
  463. // ensure full row select
  464. base.OnRowEnter(e);
  465. if (this.SelectionMode == DataGridViewSelectionMode.CellSelect ||
  466. (this.SelectionMode == DataGridViewSelectionMode.FullRowSelect &&
  467. base.Rows[e.RowIndex].Selected == false))
  468. {
  469. this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
  470. base.Rows[e.RowIndex].Selected = true;
  471. }
  472. }
  473. private void LockVerticalScrollBarUpdate(bool lockUpdate/*, bool delayed*/)
  474. {
  475. // Temporarly hide/show the vertical scroll bar by changing its parent
  476. if (!this._inExpandCollapse)
  477. {
  478. if (lockUpdate)
  479. {
  480. this.VerticalScrollBar.Parent = hideScrollBarControl;
  481. }
  482. else
  483. {
  484. this.VerticalScrollBar.Parent = this;
  485. }
  486. }
  487. }
  488. protected override void OnColumnAdded(DataGridViewColumnEventArgs e)
  489. {
  490. if (typeof(TreeGridColumn).IsAssignableFrom(e.Column.GetType()))
  491. {
  492. if (_expandableColumn == null)
  493. {
  494. // identify the expanding column.
  495. _expandableColumn = (TreeGridColumn)e.Column;
  496. }
  497. else
  498. {
  499. // this.Columns.Remove(e.Column);
  500. //throw new InvalidOperationException("Only one TreeGridColumn per TreeGridView is supported.");
  501. }
  502. }
  503. // Expandable Grid doesn't support sorting. This is just a limitation of the sample.
  504. e.Column.SortMode = DataGridViewColumnSortMode.NotSortable;
  505. base.OnColumnAdded(e);
  506. }
  507. private static class Win32Helper
  508. {
  509. public const int WM_SYSKEYDOWN = 0x0104,
  510. WM_KEYDOWN = 0x0100,
  511. WM_SETREDRAW = 0x000B;
  512. [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
  513. public static extern IntPtr SendMessage(System.Runtime.InteropServices.HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam);
  514. [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
  515. public static extern IntPtr SendMessage(System.Runtime.InteropServices.HandleRef hWnd, int msg, int wParam, int lParam);
  516. [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
  517. public static extern bool PostMessage(System.Runtime.InteropServices.HandleRef hwnd, int msg, IntPtr wparam, IntPtr lparam);
  518. }
  519. #endregion
  520. }
  521. }