TableLayout.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. //
  2. // TableLayout.cs
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining
  5. // a copy of this software and associated documentation files (the
  6. // "Software"), to deal in the Software without restriction, including
  7. // without limitation the rights to use, copy, modify, merge, publish,
  8. // distribute, sublicense, and/or sell copies of the Software, and to
  9. // permit persons to whom the Software is furnished to do so, subject to
  10. // the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be
  13. // included in all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. //
  23. // Copyright (c) 2006 Jonathan Pobst
  24. //
  25. // Authors:
  26. // Jonathan Pobst ([email protected])
  27. //
  28. #if NET_2_0
  29. using System;
  30. using System.Drawing;
  31. namespace System.Windows.Forms.Layout
  32. {
  33. internal class TableLayout : LayoutEngine
  34. {
  35. private static Control dummy_control = new Control ("Dummy"); // Used as a placeholder for row/col spans
  36. public TableLayout () : base ()
  37. {
  38. }
  39. public override void InitLayout (object child, BoundsSpecified specified)
  40. {
  41. base.InitLayout (child, specified);
  42. }
  43. // There are 3 steps to doing a table layout:
  44. // 1) Figure out which row/column each control goes into
  45. // 2) Figure out the sizes of each row/column
  46. // 3) Size and position each control
  47. public override bool Layout (object container, LayoutEventArgs args)
  48. {
  49. TableLayoutPanel panel = container as TableLayoutPanel;
  50. TableLayoutSettings settings = panel.LayoutSettings;
  51. // Nothing to layout, don't waste time
  52. if (panel.Controls.Count == 0)
  53. return false;
  54. // STEP 1:
  55. // - Figure out which row/column each control goes into
  56. // - Store data in the TableLayoutPanel.actual_positions
  57. panel.actual_positions = CalculateControlPositions (panel, Math.Max (settings.ColumnCount, 1), Math.Max (settings.RowCount, 1));
  58. // STEP 2:
  59. // - Figure out the sizes of each row/column
  60. // - Store data in the TableLayoutPanel.widths/heights
  61. CalculateColumnRowSizes (panel, panel.actual_positions.GetLength (0), panel.actual_positions.GetLength (1));
  62. // STEP 3:
  63. // - Size and position each control
  64. LayoutControls(panel);
  65. return false;
  66. }
  67. private Control[,] CalculateControlPositions (TableLayoutPanel panel, int columns, int rows)
  68. {
  69. Control[,] grid = new Control[columns, rows];
  70. TableLayoutSettings settings = panel.LayoutSettings;
  71. // First place all controls that have an explicit col/row
  72. foreach (Control c in panel.Controls) {
  73. int col = settings.GetColumn (c);
  74. int row = settings.GetRow (c);
  75. if ((col >= 0 && col < columns) && (row >= 0 && row < rows)) {
  76. if (grid[col, row] == null) {
  77. int col_span = Math.Min (settings.GetColumnSpan (c), columns);
  78. int row_span = Math.Min (settings.GetRowSpan (c), rows);
  79. if (col + col_span > columns) {
  80. if (row + 1 < rows) {
  81. grid[col, row] = dummy_control;
  82. row++;
  83. col = 0;
  84. }
  85. else if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddColumns)
  86. return CalculateControlPositions (panel, columns + 1, rows);
  87. else
  88. throw new ArgumentException ();
  89. }
  90. if (row + row_span > rows) {
  91. if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddRows)
  92. return CalculateControlPositions (panel, columns, rows + 1);
  93. else
  94. throw new ArgumentException ();
  95. }
  96. grid[col, row] = c;
  97. for (int i = 1; i < col_span; i++)
  98. grid[col + i, row] = dummy_control;
  99. for (int i = 1; i < row_span; i++)
  100. grid[col, row + i] = dummy_control;
  101. }
  102. }
  103. }
  104. int x_pointer = 0;
  105. int y_pointer = 0;
  106. // Fill in gaps with controls that do not have an explicit col/row
  107. foreach (Control c in panel.Controls) {
  108. int col = settings.GetColumn (c);
  109. int row = settings.GetRow (c);
  110. if ((col >= 0 && col < columns) && (row >= 0 && row < rows))
  111. continue;
  112. for (int y = y_pointer; y < rows; y++) {
  113. y_pointer = y;
  114. x_pointer = 0;
  115. for (int x = x_pointer; x < columns; x++) {
  116. x_pointer = x;
  117. if (grid[x, y] == null) {
  118. int col_span = Math.Min (settings.GetColumnSpan (c), columns);
  119. int row_span = Math.Min (settings.GetRowSpan (c), rows);
  120. if (x + col_span > columns) {
  121. if (y + 1 < rows)
  122. break;
  123. else if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddColumns)
  124. return CalculateControlPositions (panel, columns + 1, rows);
  125. else
  126. throw new ArgumentException ();
  127. }
  128. if (y + row_span > rows) {
  129. if (x + 1 < columns)
  130. break;
  131. else if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddRows)
  132. return CalculateControlPositions (panel, columns, rows + 1);
  133. else
  134. throw new ArgumentException ();
  135. }
  136. grid[x, y] = c;
  137. for (int i = 1; i < col_span; i++)
  138. grid[x + i, y] = dummy_control;
  139. for (int i = 1; i < row_span; i++)
  140. grid[x, y + i] = dummy_control;
  141. // I know someone will kill me for using a goto, but
  142. // sometimes they really are the easiest way...
  143. goto Found;
  144. }
  145. }
  146. }
  147. // We ran out of room in the grid, and have more controls, what is our GrowStyle?
  148. switch (settings.GrowStyle) {
  149. case TableLayoutPanelGrowStyle.AddColumns:
  150. return CalculateControlPositions (panel, columns + 1, rows);
  151. case TableLayoutPanelGrowStyle.AddRows:
  152. default:
  153. return CalculateControlPositions (panel, columns, rows + 1);
  154. case TableLayoutPanelGrowStyle.FixedSize:
  155. throw new ArgumentException ();
  156. }
  157. Found: ;
  158. }
  159. return grid;
  160. }
  161. private void CalculateColumnRowSizes (TableLayoutPanel panel, int columns, int rows)
  162. {
  163. TableLayoutSettings settings = panel.LayoutSettings;
  164. panel.column_widths = new int[panel.actual_positions.GetLength (0)];
  165. panel.row_heights = new int[panel.actual_positions.GetLength (1)];
  166. Rectangle parentDisplayRectangle = panel.DisplayRectangle;
  167. TableLayoutColumnStyleCollection col_styles = new TableLayoutColumnStyleCollection (panel);
  168. foreach (ColumnStyle cs in settings.ColumnStyles)
  169. col_styles.Add( new ColumnStyle(cs.SizeType, cs.Width));
  170. TableLayoutRowStyleCollection row_styles = new TableLayoutRowStyleCollection (panel);
  171. foreach (RowStyle rs in settings.RowStyles)
  172. row_styles.Add (new RowStyle (rs.SizeType, rs.Height));
  173. // If we have more columns than columnstyles, temporarily add enough columnstyles
  174. if (columns > col_styles.Count)
  175. {
  176. for (int i = col_styles.Count; i < columns; i++)
  177. col_styles.Add(new ColumnStyle());
  178. }
  179. // Same for rows..
  180. if (rows > row_styles.Count)
  181. {
  182. for (int i = row_styles.Count; i < rows; i++)
  183. row_styles.Add (new RowStyle ());
  184. }
  185. while (row_styles.Count > rows)
  186. row_styles.RemoveAt (row_styles.Count - 1);
  187. while (col_styles.Count > columns)
  188. col_styles.RemoveAt (col_styles.Count - 1);
  189. // Figure up all the column widths
  190. int total_width = parentDisplayRectangle.Width;
  191. int index = 0;
  192. // First assign all the Absolute sized columns..
  193. foreach (ColumnStyle cs in col_styles) {
  194. if (cs.SizeType == SizeType.Absolute) {
  195. panel.column_widths[index] = (int)cs.Width;
  196. total_width -= (int)cs.Width;
  197. }
  198. index++;
  199. }
  200. index = 0;
  201. // Next, assign all the AutoSize columns..
  202. foreach (ColumnStyle cs in col_styles)
  203. {
  204. if (cs.SizeType == SizeType.AutoSize)
  205. {
  206. int max_width = 0;
  207. // Find the widest control in the column
  208. for (int i = 0; i < rows; i ++)
  209. {
  210. Control c = panel.actual_positions[index, i];
  211. if (c != null && c != dummy_control)
  212. {
  213. if (c.Dock == DockStyle.Fill || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left && (c.Anchor & AnchorStyles.Right) == AnchorStyles.Right))
  214. continue;
  215. else if (settings.GetColumnSpan(c) > 1)
  216. continue;
  217. if (c.Width + c.Margin.Left + c.Margin.Right > max_width)
  218. max_width = c.Width + c.Margin.Left + c.Margin.Right;
  219. }
  220. }
  221. panel.column_widths[index] = max_width;
  222. total_width -= max_width;
  223. }
  224. index++;
  225. }
  226. index = 0;
  227. float total_percent = 0;
  228. // Finally, assign the remaining space to Percent columns..
  229. if (total_width > 0)
  230. {
  231. int percent_width = total_width;
  232. // Find the total percent (not always 100%)
  233. foreach (ColumnStyle cs in col_styles)
  234. {
  235. if (cs.SizeType == SizeType.Percent)
  236. total_percent += cs.Width;
  237. }
  238. // Divy up the space..
  239. foreach (ColumnStyle cs in col_styles)
  240. {
  241. if (cs.SizeType == SizeType.Percent)
  242. {
  243. panel.column_widths[index] = (int)((cs.Width / total_percent) * percent_width);
  244. total_width -= panel.column_widths[index];
  245. }
  246. index++;
  247. }
  248. }
  249. if (total_width > 0)
  250. panel.column_widths[col_styles.Count - 1] += total_width;
  251. // Figure up all the row heights
  252. int total_height = parentDisplayRectangle.Height;
  253. index = 0;
  254. // First assign all the Absolute sized rows..
  255. foreach (RowStyle rs in row_styles) {
  256. if (rs.SizeType == SizeType.Absolute) {
  257. panel.row_heights[index] = (int)rs.Height;
  258. total_height -= (int)rs.Height;
  259. }
  260. index++;
  261. }
  262. index = 0;
  263. // Next, assign all the AutoSize rows..
  264. foreach (RowStyle rs in row_styles) {
  265. if (rs.SizeType == SizeType.AutoSize) {
  266. int max_height = 0;
  267. // Find the tallest control in the row
  268. for (int i = 0; i < columns; i++) {
  269. Control c = panel.actual_positions[i, index];
  270. if (c != null && c != dummy_control) {
  271. if (c.Dock == DockStyle.Fill || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top && (c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom))
  272. continue;
  273. else if (settings.GetRowSpan (c) > 1)
  274. continue;
  275. if (c.Height + c.Margin.Top + c.Margin.Bottom > max_height)
  276. max_height = c.Height + c.Margin.Top + c.Margin.Bottom;
  277. }
  278. }
  279. panel.row_heights[index] = max_height;
  280. total_height -= max_height;
  281. }
  282. index++;
  283. }
  284. index = 0;
  285. total_percent = 0;
  286. // Finally, assign the remaining space to Percent columns..
  287. if (total_height > 0) {
  288. int percent_height = total_height;
  289. // Find the total percent (not always 100%)
  290. foreach (RowStyle rs in row_styles) {
  291. if (rs.SizeType == SizeType.Percent)
  292. total_percent += rs.Height;
  293. }
  294. // Divy up the space..
  295. foreach (RowStyle rs in row_styles) {
  296. if (rs.SizeType == SizeType.Percent) {
  297. panel.row_heights[index] = (int)((rs.Height / total_percent) * percent_height);
  298. total_height -= panel.row_heights[index];
  299. }
  300. index++;
  301. }
  302. }
  303. if (total_height > 0)
  304. panel.row_heights[row_styles.Count - 1] += total_height;
  305. }
  306. private void LayoutControls (TableLayoutPanel panel)
  307. {
  308. TableLayoutSettings settings = panel.LayoutSettings;
  309. int columns = panel.actual_positions.GetLength(0);
  310. int rows = panel.actual_positions.GetLength(1);
  311. Point current_pos = new Point(0,0);
  312. for (int y = 0; y < rows; y++)
  313. {
  314. for (int x = 0; x < columns; x ++)
  315. {
  316. Control c = panel.actual_positions[x,y];
  317. if(c != null && c != dummy_control)
  318. {
  319. int new_x = 0;
  320. int new_y = 0;
  321. int new_width = 0;
  322. int new_height = 0;
  323. // Figure out the width of the control
  324. int column_width = panel.column_widths[x];
  325. for (int i = 1; i < settings.GetColumnSpan(c); i++)
  326. column_width += panel.column_widths[x + i];
  327. if (c.Dock == DockStyle.Fill || c.Dock == DockStyle.Top || c.Dock == DockStyle.Bottom || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left && (c.Anchor & AnchorStyles.Right) == AnchorStyles.Right))
  328. new_width = column_width - c.Margin.Left - c.Margin.Right;
  329. else
  330. new_width = Math.Min (c.Width, column_width - c.Margin.Left - c.Margin.Right);
  331. // Figure out the height of the control
  332. int column_height = panel.row_heights[y];
  333. for (int i = 1; i < settings.GetRowSpan (c); i++)
  334. column_height += panel.row_heights[y + i];
  335. if (c.Dock == DockStyle.Fill || c.Dock == DockStyle.Left || c.Dock == DockStyle.Right || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top && (c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom))
  336. new_height = column_height - c.Margin.Top - c.Margin.Bottom;
  337. else
  338. new_height = Math.Min (c.Height, column_height - c.Margin.Top - c.Margin.Bottom);
  339. // Figure out the left location of the control
  340. if (c.Dock == DockStyle.Left || c.Dock == DockStyle.Fill || (c.Anchor & AnchorStyles.Left) == AnchorStyles.Left)
  341. new_x = current_pos.X + c.Margin.Left;
  342. else if (c.Dock == DockStyle.Right || (c.Anchor & AnchorStyles.Right) == AnchorStyles.Right)
  343. new_x = (current_pos.X + column_width) - new_width - c.Margin.Right;
  344. else // (center control)
  345. new_x = ((current_pos.X + column_width - c.Margin.Left - c.Margin.Right) / 2) + c.Margin.Left - (new_width / 2);
  346. // Figure out the top location of the control
  347. if (c.Dock == DockStyle.Top || c.Dock == DockStyle.Fill || (c.Anchor & AnchorStyles.Top) == AnchorStyles.Top)
  348. new_y = current_pos.Y + c.Margin.Top;
  349. else if (c.Dock == DockStyle.Bottom || (c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom)
  350. new_y = (current_pos.Y + column_height) - new_height - c.Margin.Bottom;
  351. else // (center control)
  352. new_y = ((current_pos.Y + column_height - c.Margin.Top - c.Margin.Bottom) / 2) + c.Margin.Top - (new_height / 2);
  353. c.Bounds = new Rectangle(new_x, new_y, new_width, new_height);
  354. }
  355. current_pos.Offset (panel.column_widths[x], 0);
  356. }
  357. current_pos.Offset (-1 * current_pos.X, panel.row_heights[y]);
  358. }
  359. }
  360. }
  361. }
  362. #endif