1 // 2 // TableLayoutPanel.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 (monkey@jpobst.com) 27 // 28 29 using System; 30 using System.Drawing; 31 using System.Collections.Generic; 32 using System.ComponentModel; 33 using System.Runtime.InteropServices; 34 using System.Windows.Forms.Layout; 35 using System.ComponentModel.Design.Serialization; 36 37 namespace System.Windows.Forms 38 { 39 [ComVisible (true)] 40 [ClassInterface (ClassInterfaceType.AutoDispatch)] 41 [ProvideProperty ("CellPosition", typeof (Control))] 42 [ProvideProperty ("Column", typeof (Control))] 43 [ProvideProperty ("ColumnSpan", typeof (Control))] 44 [ProvideProperty ("Row", typeof (Control))] 45 [ProvideProperty ("RowSpan", typeof (Control))] 46 [DefaultProperty ("ColumnCount")] 47 [Docking (DockingBehavior.Never)] 48 [Designer ("System.Windows.Forms.Design.TableLayoutPanelDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")] 49 [DesignerSerializer ("System.Windows.Forms.Design.TableLayoutPanelCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)] 50 public class TableLayoutPanel : Panel, IExtenderProvider 51 { 52 private TableLayoutSettings settings; 53 private static TableLayout layout_engine = new TableLayout (); 54 private TableLayoutPanelCellBorderStyle cell_border_style; 55 56 // This is the row/column the Control actually got placed 57 internal Control[,] actual_positions; 58 59 // Widths and heights of each column/row 60 internal int[] column_widths; 61 internal int[] row_heights; 62 63 #region Public Constructor TableLayoutPanel()64 public TableLayoutPanel () 65 { 66 settings = new TableLayoutSettings(this); 67 cell_border_style = TableLayoutPanelCellBorderStyle.None; 68 column_widths = new int[0]; 69 row_heights = new int[0]; 70 CreateDockPadding (); 71 } 72 #endregion 73 74 #region Public Properties 75 [Localizable (true)] 76 [Browsable (false)] 77 [EditorBrowsable (EditorBrowsableState.Never)] 78 new public BorderStyle BorderStyle { 79 get { return base.BorderStyle; } 80 set { base.BorderStyle = value; } 81 } 82 83 [Localizable (true)] 84 [DefaultValue (TableLayoutPanelCellBorderStyle.None)] 85 public TableLayoutPanelCellBorderStyle CellBorderStyle { 86 get { return this.cell_border_style; } 87 set { 88 if (this.cell_border_style != value) { 89 this.cell_border_style = value; 90 this.PerformLayout (this, "CellBorderStyle"); 91 this.Invalidate (); 92 } 93 } 94 } 95 96 [Localizable (true)] 97 [DefaultValue (0)] 98 public int ColumnCount { 99 get { return settings.ColumnCount; } 100 set { settings.ColumnCount = value; } 101 } 102 103 [Browsable (false)] 104 [DisplayName ("Columns")] 105 [MergableProperty (false)] 106 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 107 public TableLayoutColumnStyleCollection ColumnStyles { 108 get { return settings.ColumnStyles; } 109 } 110 111 [Browsable (false)] 112 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] 113 new public TableLayoutControlCollection Controls { 114 get { return (TableLayoutControlCollection) base.Controls; } 115 } 116 117 [DefaultValue (TableLayoutPanelGrowStyle.AddRows)] 118 public TableLayoutPanelGrowStyle GrowStyle { 119 get { return settings.GrowStyle; } 120 set { settings.GrowStyle = value; } 121 } 122 123 public override System.Windows.Forms.Layout.LayoutEngine LayoutEngine { 124 get { return TableLayoutPanel.layout_engine; } 125 } 126 127 [Browsable (false)] 128 [EditorBrowsable (EditorBrowsableState.Never)] 129 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 130 public TableLayoutSettings LayoutSettings { 131 get { return this.settings; } 132 set { 133 if (value.isSerialized) { 134 // Serialized version doesn't calculate these. 135 value.ColumnCount = value.ColumnStyles.Count; 136 value.RowCount = value.RowStyles.Count; 137 value.panel = this; 138 139 this.settings = value; 140 } else 141 throw new NotSupportedException ("LayoutSettings value cannot be set directly."); 142 } 143 } 144 145 [Localizable (true)] 146 [DefaultValue (0)] 147 public int RowCount { 148 get { return settings.RowCount; } 149 set { settings.RowCount = value; } 150 } 151 152 [Browsable (false)] 153 [DisplayName ("Rows")] 154 [MergableProperty (false)] 155 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] 156 public TableLayoutRowStyleCollection RowStyles { 157 get { return settings.RowStyles; } 158 } 159 #endregion 160 161 #region Public Methods 162 [DefaultValue (-1)] 163 [DisplayName ("Cell")] 164 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] GetCellPosition(Control control)165 public TableLayoutPanelCellPosition GetCellPosition (Control control) 166 { 167 return settings.GetCellPosition (control); 168 } 169 170 [DisplayName ("Column")] 171 [DefaultValue (-1)] 172 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] GetColumn(Control control)173 public int GetColumn (Control control) 174 { 175 return settings.GetColumn (control); 176 } 177 178 [DisplayName ("ColumnSpan")] 179 [DefaultValue (1)] GetColumnSpan(Control control)180 public int GetColumnSpan (Control control) 181 { 182 return settings.GetColumnSpan (control); 183 } 184 185 [Browsable (false)] 186 [EditorBrowsable (EditorBrowsableState.Never)] GetColumnWidths()187 public int[] GetColumnWidths () 188 { 189 return this.column_widths; 190 } 191 GetControlFromPosition(int column, int row)192 public Control GetControlFromPosition (int column, int row) 193 { 194 if (column < 0 || row < 0) 195 throw new ArgumentException (); 196 197 TableLayoutPanelCellPosition pos = new TableLayoutPanelCellPosition (column, row); 198 199 foreach (Control c in this.Controls) 200 if (settings.GetCellPosition (c) == pos) 201 return c; 202 203 return null; 204 } 205 GetPositionFromControl(Control control)206 public TableLayoutPanelCellPosition GetPositionFromControl (Control control) 207 { 208 for (int x = 0; x < this.actual_positions.GetLength (0); x++) 209 for (int y = 0; y < this.actual_positions.GetLength (1); y++) 210 if (this.actual_positions[x, y] == control) 211 return new TableLayoutPanelCellPosition (x, y); 212 213 return new TableLayoutPanelCellPosition (-1, -1); 214 } 215 216 [DisplayName ("Row")] 217 [DefaultValue ("-1")] 218 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] GetRow(Control control)219 public int GetRow (Control control) 220 { 221 return settings.GetRow (control); 222 } 223 224 [Browsable (false)] 225 [EditorBrowsable (EditorBrowsableState.Never)] GetRowHeights()226 public int[] GetRowHeights () 227 { 228 return this.row_heights; 229 } 230 231 [DisplayName ("RowSpan")] 232 [DefaultValue (1)] GetRowSpan(Control control)233 public int GetRowSpan (Control control) 234 { 235 return settings.GetRowSpan (control); 236 } 237 SetCellPosition(Control control, TableLayoutPanelCellPosition position)238 public void SetCellPosition (Control control, TableLayoutPanelCellPosition position) 239 { 240 settings.SetCellPosition (control, position); 241 } 242 SetColumn(Control control, int column)243 public void SetColumn (Control control, int column) 244 { 245 settings.SetColumn (control, column); 246 } 247 SetColumnSpan(Control control, int value)248 public void SetColumnSpan (Control control, int value) 249 { 250 settings.SetColumnSpan (control, value); 251 } 252 SetRow(Control control, int row)253 public void SetRow (Control control, int row) 254 { 255 settings.SetRow (control, row); 256 } 257 SetRowSpan(Control control, int value)258 public void SetRowSpan (Control control, int value) 259 { 260 settings.SetRowSpan (control, value); 261 } 262 #endregion 263 264 #region Protected Methods 265 [EditorBrowsable (EditorBrowsableState.Advanced)] CreateControlsInstance()266 protected override ControlCollection CreateControlsInstance () 267 { 268 return new TableLayoutControlCollection (this); 269 } 270 OnCellPaint(TableLayoutCellPaintEventArgs e)271 protected virtual void OnCellPaint (TableLayoutCellPaintEventArgs e) 272 { 273 TableLayoutCellPaintEventHandler eh = (TableLayoutCellPaintEventHandler)(Events [CellPaintEvent]); 274 if (eh != null) 275 eh (this, e); 276 } 277 278 [EditorBrowsable (EditorBrowsableState.Advanced)] OnLayout(LayoutEventArgs levent)279 protected override void OnLayout (LayoutEventArgs levent) 280 { 281 base.OnLayout (levent); 282 Invalidate (); 283 } 284 OnPaintBackground(PaintEventArgs e)285 protected override void OnPaintBackground (PaintEventArgs e) 286 { 287 base.OnPaintBackground (e); 288 289 DrawCellBorders (e); 290 291 int border_width = GetCellBorderWidth (CellBorderStyle); 292 293 int x = border_width; 294 int y = border_width; 295 296 for (int i = 0; i < column_widths.Length; i++) { 297 for (int j = 0; j < row_heights.Length; j++) { 298 this.OnCellPaint (new TableLayoutCellPaintEventArgs (e.Graphics, e.ClipRectangle, new Rectangle (x, y, column_widths[i] + border_width, row_heights[j] + border_width), i, j)); 299 y += row_heights[j] + border_width; 300 } 301 302 x += column_widths[i] + border_width; 303 y = border_width; 304 } 305 } 306 ScaleControl(SizeF factor, BoundsSpecified specified)307 protected override void ScaleControl (SizeF factor, BoundsSpecified specified) 308 { 309 base.ScaleControl (factor, specified); 310 } 311 312 [EditorBrowsable (EditorBrowsableState.Never)] ScaleCore(float dx, float dy)313 protected override void ScaleCore (float dx, float dy) 314 { 315 base.ScaleCore (dx, dy); 316 } 317 #endregion 318 319 #region Internal Methods GetCellBorderWidth(TableLayoutPanelCellBorderStyle style)320 internal static int GetCellBorderWidth (TableLayoutPanelCellBorderStyle style) 321 { 322 switch (style) { 323 case TableLayoutPanelCellBorderStyle.Single: 324 return 1; 325 case TableLayoutPanelCellBorderStyle.Inset: 326 case TableLayoutPanelCellBorderStyle.Outset: 327 return 2; 328 case TableLayoutPanelCellBorderStyle.InsetDouble: 329 case TableLayoutPanelCellBorderStyle.OutsetPartial: 330 case TableLayoutPanelCellBorderStyle.OutsetDouble: 331 return 3; 332 } 333 334 return 0; 335 } 336 DrawCellBorders(PaintEventArgs e)337 private void DrawCellBorders (PaintEventArgs e) 338 { 339 Rectangle paint_here = new Rectangle (Point.Empty, this.Size); 340 341 switch (CellBorderStyle) { 342 case TableLayoutPanelCellBorderStyle.Single: 343 DrawSingleBorder (e.Graphics, paint_here); 344 break; 345 case TableLayoutPanelCellBorderStyle.Inset: 346 DrawInsetBorder (e.Graphics, paint_here); 347 break; 348 case TableLayoutPanelCellBorderStyle.InsetDouble: 349 DrawInsetDoubleBorder (e.Graphics, paint_here); 350 break; 351 case TableLayoutPanelCellBorderStyle.Outset: 352 DrawOutsetBorder (e.Graphics, paint_here); 353 break; 354 case TableLayoutPanelCellBorderStyle.OutsetDouble: 355 case TableLayoutPanelCellBorderStyle.OutsetPartial: 356 DrawOutsetDoubleBorder (e.Graphics, paint_here); 357 break; 358 } 359 } 360 DrawSingleBorder(Graphics g, Rectangle rect)361 private void DrawSingleBorder (Graphics g, Rectangle rect) 362 { 363 ControlPaint.DrawBorder (g, rect, SystemColors.ControlDark, ButtonBorderStyle.Solid); 364 365 int x = DisplayRectangle.X; 366 int y = DisplayRectangle.Y; 367 368 for (int i = 0; i < column_widths.Length - 1; i++) { 369 x += column_widths[i] + 1; 370 371 g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 2)); 372 } 373 374 for (int j = 0; j < row_heights.Length - 1; j++) { 375 y += row_heights[j] + 1; 376 377 g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 2, y)); 378 } 379 } 380 DrawInsetBorder(Graphics g, Rectangle rect)381 private void DrawInsetBorder (Graphics g, Rectangle rect) 382 { 383 ControlPaint.DrawBorder3D (g, rect, Border3DStyle.Etched); 384 385 int x = DisplayRectangle.X; 386 int y = DisplayRectangle.Y; 387 388 for (int i = 0; i < column_widths.Length - 1; i++) { 389 x += column_widths[i] + 2; 390 391 g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 3)); 392 g.DrawLine (Pens.White, new Point (x + 1, 1), new Point (x + 1, Bottom - 3)); 393 } 394 395 for (int j = 0; j < row_heights.Length - 1; j++) { 396 y += row_heights[j] + 2; 397 398 g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 3, y)); 399 g.DrawLine (Pens.White, new Point (1, y + 1), new Point (Right - 3, y + 1)); 400 } 401 } 402 DrawOutsetBorder(Graphics g, Rectangle rect)403 private void DrawOutsetBorder (Graphics g, Rectangle rect) 404 { 405 g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left + 1, rect.Top + 1, rect.Width - 2, rect.Height - 2)); 406 g.DrawRectangle (Pens.White, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2)); 407 408 int x = DisplayRectangle.X; 409 int y = DisplayRectangle.Y; 410 411 for (int i = 0; i < column_widths.Length - 1; i++) { 412 x += column_widths[i] + 2; 413 414 g.DrawLine (Pens.White, new Point (x, 1), new Point (x, Bottom - 3)); 415 g.DrawLine (SystemPens.ControlDark, new Point (x + 1, 1), new Point (x + 1, Bottom - 3)); 416 } 417 418 for (int j = 0; j < row_heights.Length - 1; j++) { 419 y += row_heights[j] + 2; 420 421 g.DrawLine (Pens.White, new Point (1, y), new Point (Right - 3, y)); 422 g.DrawLine (SystemPens.ControlDark, new Point (1, y + 1), new Point (Right - 3, y + 1)); 423 } 424 } 425 DrawOutsetDoubleBorder(Graphics g, Rectangle rect)426 private void DrawOutsetDoubleBorder (Graphics g, Rectangle rect) 427 { 428 rect.Width -= 1; 429 rect.Height -= 1; 430 431 g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left + 2, rect.Top + 2, rect.Width - 2, rect.Height - 2)); 432 g.DrawRectangle (Pens.White, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2)); 433 434 int x = DisplayRectangle.X; 435 int y = DisplayRectangle.Y; 436 437 for (int i = 0; i < column_widths.Length - 1; i++) { 438 x += column_widths[i] + 3; 439 440 g.DrawLine (Pens.White, new Point (x, 3), new Point (x, Bottom - 5)); 441 g.DrawLine (SystemPens.ControlDark, new Point (x + 2, 3), new Point (x + 2, Bottom - 5)); 442 } 443 444 for (int j = 0; j < row_heights.Length - 1; j++) { 445 y += row_heights[j] + 3; 446 447 g.DrawLine (Pens.White, new Point (3, y), new Point (Right - 4, y)); 448 g.DrawLine (SystemPens.ControlDark, new Point (3, y + 2), new Point (Right - 4, y + 2)); 449 } 450 451 x = DisplayRectangle.X; 452 y = DisplayRectangle.Y; 453 454 for (int i = 0; i < column_widths.Length - 1; i++) { 455 x += column_widths[i] + 3; 456 457 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5)); 458 } 459 460 for (int j = 0; j < row_heights.Length - 1; j++) { 461 y += row_heights[j] + 3; 462 463 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1)); 464 } 465 } 466 DrawInsetDoubleBorder(Graphics g, Rectangle rect)467 private void DrawInsetDoubleBorder (Graphics g, Rectangle rect) 468 { 469 rect.Width -= 1; 470 rect.Height -= 1; 471 472 g.DrawRectangle (Pens.White, new Rectangle (rect.Left + 2, rect.Top + 2, rect.Width - 2, rect.Height - 2)); 473 g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2)); 474 475 int x = DisplayRectangle.X; 476 int y = DisplayRectangle.Y; 477 478 for (int i = 0; i < column_widths.Length - 1; i++) { 479 x += column_widths[i] + 3; 480 481 g.DrawLine (SystemPens.ControlDark, new Point (x, 3), new Point (x, Bottom - 5)); 482 g.DrawLine (Pens.White, new Point (x + 2, 3), new Point (x + 2, Bottom - 5)); 483 } 484 485 for (int j = 0; j < row_heights.Length - 1; j++) { 486 y += row_heights[j] + 3; 487 488 g.DrawLine (SystemPens.ControlDark, new Point (3, y), new Point (Right - 4, y)); 489 g.DrawLine (Pens.White, new Point (3, y + 2), new Point (Right - 4, y + 2)); 490 } 491 492 x = DisplayRectangle.X; 493 y = DisplayRectangle.Y; 494 495 for (int i = 0; i < column_widths.Length - 1; i++) { 496 x += column_widths[i] + 3; 497 498 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5)); 499 } 500 501 for (int j = 0; j < row_heights.Length - 1; j++) { 502 y += row_heights[j] + 3; 503 504 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1)); 505 } 506 } 507 GetPreferredSizeCore(Size proposedSize)508 internal override Size GetPreferredSizeCore (Size proposedSize) 509 { 510 // If the tablelayoutowner is autosize, we have to make sure it is big enough 511 // to hold every non-autosize control 512 actual_positions = (LayoutEngine as TableLayout).CalculateControlPositions (this, Math.Max (ColumnCount, 1), Math.Max (RowCount, 1)); 513 514 // Use actual row/column counts, not user set ones 515 int actual_cols = actual_positions.GetLength (0); 516 int actual_rows = actual_positions.GetLength (1); 517 518 // Find the largest column-span/row-span values. A table entry that spans more than one 519 // column (row) should not be treated as though it's width (height) all belongs to the 520 // first column (row), but should be spread out across all the columns (rows) that are 521 // spanned. So we need to keep track of the widths (heights) of spans as well as 522 // individual columns (rows). 523 int max_colspan = 1, max_rowspan = 1; 524 foreach (Control c in Controls) 525 { 526 max_colspan = Math.Max(max_colspan, GetColumnSpan(c)); 527 max_rowspan = Math.Max(max_rowspan, GetRowSpan(c)); 528 } 529 530 // Figure out how wide the owner needs to be 531 int[] column_widths = new int[actual_cols]; 532 // Keep track of widths for spans as well as columns. column_span_widths[i,j] stores 533 // the maximum width for items column i than have a span of j+1 (ie, covers columns 534 // i through i+j). 535 int[,] column_span_widths = new int[actual_cols, max_colspan]; 536 int[] biggest = new int[max_colspan]; 537 float total_column_percentage = 0f; 538 539 // Figure out how wide each column wants to be 540 for (int i = 0; i < actual_cols; i++) { 541 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent) 542 total_column_percentage += ColumnStyles[i].Width; 543 int absolute_width = -1; 544 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Absolute) 545 absolute_width = (int)ColumnStyles[i].Width; // use the absolute width if it's absolute! 546 547 for (int s = 0; s < max_colspan; ++s) 548 biggest[s] = 0; 549 550 for (int j = 0; j < actual_rows; j++) { 551 Control c = actual_positions[i, j]; 552 553 if (c != null) { 554 int colspan = GetColumnSpan (c); 555 if (colspan == 0) 556 continue; 557 if (colspan == 1 && absolute_width > -1) 558 biggest[0] = absolute_width; // use the absolute width if the column has absolute width assigned! 559 else if (!c.AutoSize) 560 biggest[colspan-1] = Math.Max (biggest[colspan-1], c.ExplicitBounds.Width + c.Margin.Horizontal + Padding.Horizontal); 561 else 562 biggest[colspan-1] = Math.Max (biggest[colspan-1], c.PreferredSize.Width + c.Margin.Horizontal + Padding.Horizontal); 563 } 564 else if (absolute_width > -1) { 565 biggest[0] = absolute_width; 566 } 567 } 568 569 for (int s = 0; s < max_colspan; ++s) 570 column_span_widths[i,s] = biggest[s]; 571 } 572 573 for (int i = 0; i < actual_cols; ++i) { 574 for (int s = 1; s < max_colspan; ++s) { 575 if (column_span_widths[i,s] > 0) 576 AdjustWidthsForSpans (column_span_widths, i, s); 577 } 578 column_widths[i] = column_span_widths[i,0]; 579 } 580 581 // Because percentage based rows divy up the remaining space, 582 // we have to make the owner big enough so that all the rows 583 // get bigger, even if we only need one to be bigger. 584 int non_percent_total_width = 0; 585 int percent_total_width = 0; 586 587 for (int i = 0; i < actual_cols; i++) { 588 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent) 589 percent_total_width = Math.Max (percent_total_width, (int)(column_widths[i] / ((ColumnStyles[i].Width) / total_column_percentage))); 590 else 591 non_percent_total_width += column_widths[i]; 592 } 593 594 int border_width = GetCellBorderWidth (CellBorderStyle); 595 int needed_width = non_percent_total_width + percent_total_width + (border_width * (actual_cols + 1)); 596 597 // Figure out how tall the owner needs to be 598 int[] row_heights = new int[actual_rows]; 599 int[,] row_span_heights = new int[actual_rows, max_rowspan]; 600 biggest = new int[max_rowspan]; 601 float total_row_percentage = 0f; 602 603 // Figure out how tall each row wants to be 604 for (int j = 0; j < actual_rows; j++) { 605 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent) 606 total_row_percentage += RowStyles[j].Height; 607 int absolute_height = -1; 608 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Absolute) 609 absolute_height = (int)RowStyles[j].Height; // use the absolute height if it's absolute! 610 611 for (int s = 0; s < max_rowspan; ++s) 612 biggest[s] = 0; 613 614 for (int i = 0; i < actual_cols; i++) { 615 Control c = actual_positions[i, j]; 616 617 if (c != null) { 618 int rowspan = GetRowSpan (c); 619 if (rowspan == 0) 620 continue; 621 if (rowspan == 1 && absolute_height > -1) 622 biggest[0] = absolute_height; // use the absolute height if the row has absolute height assigned! 623 else if (!c.AutoSize) 624 biggest[rowspan-1] = Math.Max (biggest[rowspan-1], c.ExplicitBounds.Height + c.Margin.Vertical + Padding.Vertical); 625 else 626 biggest[rowspan-1] = Math.Max (biggest[rowspan-1], c.PreferredSize.Height + c.Margin.Vertical + Padding.Vertical); 627 } 628 else if (absolute_height > -1) { 629 biggest[0] = absolute_height; 630 } 631 } 632 633 for (int s = 0; s < max_rowspan; ++s) 634 row_span_heights[j,s] = biggest[s]; 635 } 636 637 for (int j = 0; j < actual_rows; ++j) { 638 for (int s = 1; s < max_rowspan; ++s) { 639 if (row_span_heights[j,s] > 0) 640 AdjustHeightsForSpans (row_span_heights, j, s); 641 } 642 row_heights[j] = row_span_heights[j,0]; 643 } 644 645 // Because percentage based rows divy up the remaining space, 646 // we have to make the owner big enough so that all the rows 647 // get bigger, even if we only need one to be bigger. 648 int non_percent_total_height = 0; 649 int percent_total_height = 0; 650 651 for (int j = 0; j < actual_rows; j++) { 652 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent) 653 percent_total_height = Math.Max (percent_total_height, (int)(row_heights[j] / ((RowStyles[j].Height) / total_row_percentage))); 654 else 655 non_percent_total_height += row_heights[j]; 656 } 657 658 int needed_height = non_percent_total_height + percent_total_height + (border_width * (actual_rows + 1)); 659 660 return new Size (needed_width, needed_height); 661 } 662 663 /// <summary> 664 /// Adjust the widths of the columns underlying a span if necessary. 665 /// </summary> AdjustWidthsForSpans(int[,] widths, int col, int span)666 private void AdjustWidthsForSpans (int[,] widths, int col, int span) 667 { 668 // Get the combined width of the columns underlying the span. 669 int existing_width = 0; 670 for (int i = col; i <= col+span; ++i) 671 existing_width += widths[i,0]; 672 if (widths[col,span] > existing_width) 673 { 674 // We need to expand one or more of the underlying columns to fit the span, 675 // preferably ones that are not Absolute style. 676 int excess = widths[col,span] - existing_width; 677 int remaining = excess; 678 List<int> adjusting = new List<int>(); 679 List<float> adjusting_widths = new List<float>(); 680 for (int i = col; i <= col+span; ++i) { 681 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType != SizeType.Absolute) { 682 adjusting.Add(i); 683 adjusting_widths.Add((float)widths[i,0]); 684 } 685 } 686 if (adjusting.Count == 0) { 687 // if every column is Absolute, spread the gain across every column 688 for (int i = col; i <= col+span; ++i) { 689 adjusting.Add(i); 690 adjusting_widths.Add((float)widths[i,0]); 691 } 692 } 693 float original_total = 0f; 694 foreach (var w in adjusting_widths) 695 original_total += w; 696 // Divide up the needed additional width proportionally. 697 for (int i = 0; i < adjusting.Count; ++i) { 698 var idx = adjusting[i]; 699 var percent = adjusting_widths[i] / original_total; 700 var adjust = (int)(percent * excess); 701 widths[idx,0] += adjust; 702 remaining -= adjust; 703 } 704 // Any remaining fragment (1 or 2 pixels?) is divided evenly. 705 while (remaining > 0) { 706 for (int i = 0; i < adjusting.Count && remaining > 0; ++i) { 707 ++widths[adjusting[i],0]; 708 --remaining; 709 } 710 } 711 } 712 } 713 714 /// <summary> 715 /// Adjust the heights of the rows underlying a span if necessary. 716 /// </summary> AdjustHeightsForSpans(int[,] heights, int row, int span)717 private void AdjustHeightsForSpans (int[,] heights, int row, int span) 718 { 719 // Get the combined height of the rows underlying the span. 720 int existing_height = 0; 721 for (int i = row; i <= row+span; ++i) 722 existing_height += heights[i,0]; 723 if (heights[row,span] > existing_height) 724 { 725 // We need to expand one or more of the underlying rows to fit the span, 726 // preferably ones that are not Absolute style. 727 int excess = heights[row,span] - existing_height; 728 int remaining = excess; 729 List<int> adjusting = new List<int>(); 730 List<float> adjusting_heights = new List<float>(); 731 for (int i = row; i <= row+span; ++i) { 732 if (i < RowStyles.Count && RowStyles[i].SizeType != SizeType.Absolute) { 733 adjusting.Add(i); 734 adjusting_heights.Add((float)heights[i,0]); 735 } 736 } 737 if (adjusting.Count == 0) { 738 // if every row is Absolute, spread the gain across every row 739 for (int i = row; i <= row+span; ++i) { 740 adjusting.Add(i); 741 adjusting_heights.Add((float)heights[i,0]); 742 } 743 } 744 float original_total = 0f; 745 foreach (var w in adjusting_heights) 746 original_total += w; 747 // Divide up the needed additional height proportionally. 748 for (int i = 0; i < adjusting.Count; ++i) { 749 var idx = adjusting[i]; 750 var percent = adjusting_heights[i] / original_total; 751 var adjust = (int)(percent * excess); 752 heights[idx,0] += adjust; 753 remaining -= adjust; 754 } 755 // Any remaining fragment (1 or 2 pixels?) is divided evenly. 756 while (remaining > 0) { 757 for (int i = 0; i < adjusting.Count && remaining > 0; ++i) { 758 ++heights[adjusting[i],0]; 759 --remaining; 760 } 761 } 762 } 763 } 764 #endregion 765 766 #region Public Events 767 static object CellPaintEvent = new object (); 768 769 public event TableLayoutCellPaintEventHandler CellPaint { 770 add { Events.AddHandler (CellPaintEvent, value); } 771 remove { Events.RemoveHandler (CellPaintEvent, value); } 772 } 773 #endregion 774 775 #region IExtenderProvider IExtenderProvider.CanExtend(object obj)776 bool IExtenderProvider.CanExtend (object obj) 777 { 778 if (obj is Control) 779 if ((obj as Control).Parent == this) 780 return true; 781 782 return false; 783 } 784 #endregion 785 786 } 787 } 788