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:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
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.
23 // Copyright (c) 2006 Jonathan Pobst
26 // Jonathan Pobst (monkey@jpobst.com)
31 using System.ComponentModel;
32 using System.Runtime.InteropServices;
33 using System.Windows.Forms.Layout;
34 using System.ComponentModel.Design.Serialization;
36 namespace System.Windows.Forms
39 [ClassInterface (ClassInterfaceType.AutoDispatch)]
40 [ProvideProperty ("CellPosition", typeof (Control))]
41 [ProvideProperty ("Column", typeof (Control))]
42 [ProvideProperty ("ColumnSpan", typeof (Control))]
43 [ProvideProperty ("Row", typeof (Control))]
44 [ProvideProperty ("RowSpan", typeof (Control))]
45 [DefaultProperty ("ColumnCount")]
46 [Docking (DockingBehavior.Never)]
47 [Designer ("System.Windows.Forms.Design.TableLayoutPanelDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
48 [DesignerSerializer ("System.Windows.Forms.Design.TableLayoutPanelCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
49 public class TableLayoutPanel : Panel, IExtenderProvider
51 private TableLayoutSettings settings;
52 private static TableLayout layout_engine = new TableLayout ();
53 private TableLayoutPanelCellBorderStyle cell_border_style;
55 // This is the row/column the Control actually got placed
56 internal Control[,] actual_positions;
58 // Widths and heights of each column/row
59 internal int[] column_widths;
60 internal int[] row_heights;
62 #region Public Constructor
63 public TableLayoutPanel ()
65 settings = new TableLayoutSettings(this);
66 cell_border_style = TableLayoutPanelCellBorderStyle.None;
67 column_widths = new int[0];
68 row_heights = new int[0];
73 #region Public Properties
76 [EditorBrowsable (EditorBrowsableState.Never)]
77 new public BorderStyle BorderStyle {
78 get { return base.BorderStyle; }
79 set { base.BorderStyle = value; }
83 [DefaultValue (TableLayoutPanelCellBorderStyle.None)]
84 public TableLayoutPanelCellBorderStyle CellBorderStyle {
85 get { return this.cell_border_style; }
87 if (this.cell_border_style != value) {
88 this.cell_border_style = value;
89 this.PerformLayout (this, "CellBorderStyle");
97 public int ColumnCount {
98 get { return settings.ColumnCount; }
99 set { settings.ColumnCount = value; }
103 [DisplayName ("Columns")]
104 [MergableProperty (false)]
105 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
106 public TableLayoutColumnStyleCollection ColumnStyles {
107 get { return settings.ColumnStyles; }
111 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
112 new public TableLayoutControlCollection Controls {
113 get { return (TableLayoutControlCollection) base.Controls; }
116 [DefaultValue (TableLayoutPanelGrowStyle.AddRows)]
117 public TableLayoutPanelGrowStyle GrowStyle {
118 get { return settings.GrowStyle; }
119 set { settings.GrowStyle = value; }
122 public override System.Windows.Forms.Layout.LayoutEngine LayoutEngine {
123 get { return TableLayoutPanel.layout_engine; }
127 [EditorBrowsable (EditorBrowsableState.Never)]
128 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
129 public TableLayoutSettings LayoutSettings {
130 get { return this.settings; }
132 if (value.isSerialized) {
133 // Serialized version doesn't calculate these.
134 value.ColumnCount = value.ColumnStyles.Count;
135 value.RowCount = value.RowStyles.Count;
138 this.settings = value;
139 value.isSerialized = false;
141 throw new NotSupportedException ("LayoutSettings value cannot be set directly.");
147 public int RowCount {
148 get { return settings.RowCount; }
149 set { settings.RowCount = value; }
153 [DisplayName ("Rows")]
154 [MergableProperty (false)]
155 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
156 public TableLayoutRowStyleCollection RowStyles {
157 get { return settings.RowStyles; }
161 #region Public Methods
163 [DisplayName ("Cell")]
164 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
165 public TableLayoutPanelCellPosition GetCellPosition (Control control)
167 return settings.GetCellPosition (control);
170 [DisplayName ("Column")]
172 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
173 public int GetColumn (Control control)
175 return settings.GetColumn (control);
178 [DisplayName ("ColumnSpan")]
180 public int GetColumnSpan (Control control)
182 return settings.GetColumnSpan (control);
186 [EditorBrowsable (EditorBrowsableState.Never)]
187 public int[] GetColumnWidths ()
189 return this.column_widths;
192 public Control GetControlFromPosition (int column, int row)
194 if (column < 0 || row < 0)
195 throw new ArgumentException ();
197 TableLayoutPanelCellPosition pos = new TableLayoutPanelCellPosition (column, row);
199 foreach (Control c in this.Controls)
200 if (settings.GetCellPosition (c) == pos)
206 public TableLayoutPanelCellPosition GetPositionFromControl (Control control)
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);
213 return new TableLayoutPanelCellPosition (-1, -1);
216 [DisplayName ("Row")]
217 [DefaultValue ("-1")]
218 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
219 public int GetRow (Control control)
221 return settings.GetRow (control);
225 [EditorBrowsable (EditorBrowsableState.Never)]
226 public int[] GetRowHeights ()
228 return this.row_heights;
231 [DisplayName ("RowSpan")]
233 public int GetRowSpan (Control control)
235 return settings.GetRowSpan (control);
238 public void SetCellPosition (Control control, TableLayoutPanelCellPosition position)
240 settings.SetCellPosition (control, position);
243 public void SetColumn (Control control, int column)
245 settings.SetColumn (control, column);
248 public void SetColumnSpan (Control control, int value)
250 settings.SetColumnSpan (control, value);
253 public void SetRow (Control control, int row)
255 settings.SetRow (control, row);
258 public void SetRowSpan (Control control, int value)
260 settings.SetRowSpan (control, value);
264 #region Protected Methods
265 [EditorBrowsable (EditorBrowsableState.Advanced)]
266 protected override ControlCollection CreateControlsInstance ()
268 return new TableLayoutControlCollection (this);
271 protected virtual void OnCellPaint (TableLayoutCellPaintEventArgs e)
273 TableLayoutCellPaintEventHandler eh = (TableLayoutCellPaintEventHandler)(Events [CellPaintEvent]);
278 [EditorBrowsable (EditorBrowsableState.Advanced)]
279 protected override void OnLayout (LayoutEventArgs levent)
281 base.OnLayout (levent);
285 protected override void OnPaintBackground (PaintEventArgs e)
287 base.OnPaintBackground (e);
291 int border_width = GetCellBorderWidth (CellBorderStyle);
293 int x = border_width;
294 int y = border_width;
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;
302 x += column_widths[i] + border_width;
307 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
309 base.ScaleControl (factor, specified);
312 [EditorBrowsable (EditorBrowsableState.Never)]
313 protected override void ScaleCore (float dx, float dy)
315 base.ScaleCore (dx, dy);
319 #region Internal Methods
320 internal static int GetCellBorderWidth (TableLayoutPanelCellBorderStyle style)
323 case TableLayoutPanelCellBorderStyle.Single:
325 case TableLayoutPanelCellBorderStyle.Inset:
326 case TableLayoutPanelCellBorderStyle.Outset:
328 case TableLayoutPanelCellBorderStyle.InsetDouble:
329 case TableLayoutPanelCellBorderStyle.OutsetPartial:
330 case TableLayoutPanelCellBorderStyle.OutsetDouble:
337 private void DrawCellBorders (PaintEventArgs e)
339 Rectangle paint_here = new Rectangle (Point.Empty, this.Size);
341 switch (CellBorderStyle) {
342 case TableLayoutPanelCellBorderStyle.Single:
343 DrawSingleBorder (e.Graphics, paint_here);
345 case TableLayoutPanelCellBorderStyle.Inset:
346 DrawInsetBorder (e.Graphics, paint_here);
348 case TableLayoutPanelCellBorderStyle.InsetDouble:
349 DrawInsetDoubleBorder (e.Graphics, paint_here);
351 case TableLayoutPanelCellBorderStyle.Outset:
352 DrawOutsetBorder (e.Graphics, paint_here);
354 case TableLayoutPanelCellBorderStyle.OutsetDouble:
355 case TableLayoutPanelCellBorderStyle.OutsetPartial:
356 DrawOutsetDoubleBorder (e.Graphics, paint_here);
361 private void DrawSingleBorder (Graphics g, Rectangle rect)
363 ControlPaint.DrawBorder (g, rect, SystemColors.ControlDark, ButtonBorderStyle.Solid);
365 int x = DisplayRectangle.X;
366 int y = DisplayRectangle.Y;
368 for (int i = 0; i < column_widths.Length - 1; i++) {
369 x += column_widths[i] + 1;
371 g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 2));
374 for (int j = 0; j < row_heights.Length - 1; j++) {
375 y += row_heights[j] + 1;
377 g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 2, y));
381 private void DrawInsetBorder (Graphics g, Rectangle rect)
383 ControlPaint.DrawBorder3D (g, rect, Border3DStyle.Etched);
385 int x = DisplayRectangle.X;
386 int y = DisplayRectangle.Y;
388 for (int i = 0; i < column_widths.Length - 1; i++) {
389 x += column_widths[i] + 2;
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));
395 for (int j = 0; j < row_heights.Length - 1; j++) {
396 y += row_heights[j] + 2;
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));
403 private void DrawOutsetBorder (Graphics g, Rectangle rect)
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));
408 int x = DisplayRectangle.X;
409 int y = DisplayRectangle.Y;
411 for (int i = 0; i < column_widths.Length - 1; i++) {
412 x += column_widths[i] + 2;
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));
418 for (int j = 0; j < row_heights.Length - 1; j++) {
419 y += row_heights[j] + 2;
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));
426 private void DrawOutsetDoubleBorder (Graphics g, Rectangle rect)
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));
434 int x = DisplayRectangle.X;
435 int y = DisplayRectangle.Y;
437 for (int i = 0; i < column_widths.Length - 1; i++) {
438 x += column_widths[i] + 3;
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));
444 for (int j = 0; j < row_heights.Length - 1; j++) {
445 y += row_heights[j] + 3;
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));
451 x = DisplayRectangle.X;
452 y = DisplayRectangle.Y;
454 for (int i = 0; i < column_widths.Length - 1; i++) {
455 x += column_widths[i] + 3;
457 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5));
460 for (int j = 0; j < row_heights.Length - 1; j++) {
461 y += row_heights[j] + 3;
463 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1));
467 private void DrawInsetDoubleBorder (Graphics g, Rectangle rect)
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));
475 int x = DisplayRectangle.X;
476 int y = DisplayRectangle.Y;
478 for (int i = 0; i < column_widths.Length - 1; i++) {
479 x += column_widths[i] + 3;
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));
485 for (int j = 0; j < row_heights.Length - 1; j++) {
486 y += row_heights[j] + 3;
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));
492 x = DisplayRectangle.X;
493 y = DisplayRectangle.Y;
495 for (int i = 0; i < column_widths.Length - 1; i++) {
496 x += column_widths[i] + 3;
498 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5));
501 for (int j = 0; j < row_heights.Length - 1; j++) {
502 y += row_heights[j] + 3;
504 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1));
508 internal override Size GetPreferredSizeCore (Size proposedSize)
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));
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);
518 // Figure out how wide the owner needs to be
519 int[] column_widths = new int[actual_cols];
520 float total_column_percentage = 0f;
522 // Figure out how tall each column wants to be
523 for (int i = 0; i < actual_cols; i++) {
524 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
525 total_column_percentage += ColumnStyles[i].Width;
529 for (int j = 0; j < actual_rows; j++) {
530 Control c = actual_positions[i, j];
534 biggest = Math.Max (biggest, c.ExplicitBounds.Width + c.Margin.Horizontal + Padding.Horizontal);
536 biggest = Math.Max (biggest, c.PreferredSize.Width + c.Margin.Horizontal + Padding.Horizontal);
540 column_widths[i] = biggest;
543 // Because percentage based rows divy up the remaining space,
544 // we have to make the owner big enough so that all the rows
545 // get bigger, even if we only need one to be bigger.
546 int non_percent_total_width = 0;
547 int percent_total_width = 0;
549 for (int i = 0; i < actual_cols; i++) {
550 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
551 percent_total_width = Math.Max (percent_total_width, (int)(column_widths[i] / ((ColumnStyles[i].Width) / total_column_percentage)));
553 non_percent_total_width += column_widths[i];
557 // Figure out how tall the owner needs to be
558 int[] row_heights = new int[actual_rows];
559 float total_row_percentage = 0f;
561 // Figure out how tall each row wants to be
562 for (int j = 0; j < actual_rows; j++) {
563 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
564 total_row_percentage += RowStyles[j].Height;
568 for (int i = 0; i < actual_cols; i++) {
569 Control c = actual_positions[i, j];
573 biggest = Math.Max (biggest, c.ExplicitBounds.Height + c.Margin.Vertical + Padding.Vertical);
575 biggest = Math.Max (biggest, c.PreferredSize.Height + c.Margin.Vertical + Padding.Vertical);
579 row_heights[j] = biggest;
582 // Because percentage based rows divy up the remaining space,
583 // we have to make the owner big enough so that all the rows
584 // get bigger, even if we only need one to be bigger.
585 int non_percent_total_height = 0;
586 int percent_total_height = 0;
588 for (int j = 0; j < actual_rows; j++) {
589 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
590 percent_total_height = Math.Max (percent_total_height, (int)(row_heights[j] / ((RowStyles[j].Height) / total_row_percentage)));
592 non_percent_total_height += row_heights[j];
595 int border_width = GetCellBorderWidth (CellBorderStyle);
596 return new Size (non_percent_total_width + percent_total_width + (border_width * (actual_cols + 1)), non_percent_total_height + percent_total_height + (border_width * (actual_rows + 1)));
600 #region Public Events
601 static object CellPaintEvent = new object ();
603 public event TableLayoutCellPaintEventHandler CellPaint {
604 add { Events.AddHandler (CellPaintEvent, value); }
605 remove { Events.RemoveHandler (CellPaintEvent, value); }
609 #region IExtenderProvider
610 bool IExtenderProvider.CanExtend (object obj)
613 if ((obj as Control).Parent == this)