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)
32 using System.ComponentModel;
33 using System.Runtime.InteropServices;
34 using System.Windows.Forms.Layout;
35 using System.ComponentModel.Design.Serialization;
37 namespace System.Windows.Forms
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
52 private TableLayoutSettings settings;
53 private static TableLayout layout_engine = new TableLayout ();
54 private TableLayoutPanelCellBorderStyle cell_border_style;
56 // This is the row/column the Control actually got placed
57 internal Control[,] actual_positions;
59 // Widths and heights of each column/row
60 internal int[] column_widths;
61 internal int[] row_heights;
63 #region Public Constructor
64 public TableLayoutPanel ()
66 settings = new TableLayoutSettings(this);
67 cell_border_style = TableLayoutPanelCellBorderStyle.None;
71 #region Public Properties
74 [EditorBrowsable (EditorBrowsableState.Never)]
75 new public BorderStyle BorderStyle {
76 get { return base.BorderStyle; }
77 set { base.BorderStyle = value; }
81 [DefaultValue (TableLayoutPanelCellBorderStyle.None)]
82 public TableLayoutPanelCellBorderStyle CellBorderStyle {
83 get { return this.cell_border_style; }
85 if (this.cell_border_style != value) {
86 this.cell_border_style = value;
87 this.PerformLayout (this, "CellBorderStyle");
95 public int ColumnCount {
96 get { return settings.ColumnCount; }
97 set { settings.ColumnCount = value; }
101 [DisplayName ("Columns")]
102 [MergableProperty (false)]
103 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
104 public TableLayoutColumnStyleCollection ColumnStyles {
105 get { return settings.ColumnStyles; }
109 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
110 new public TableLayoutControlCollection Controls {
111 get { return (TableLayoutControlCollection) base.Controls; }
114 [DefaultValue (TableLayoutPanelGrowStyle.AddRows)]
115 public TableLayoutPanelGrowStyle GrowStyle {
116 get { return settings.GrowStyle; }
117 set { settings.GrowStyle = value; }
120 public override System.Windows.Forms.Layout.LayoutEngine LayoutEngine {
121 get { return TableLayoutPanel.layout_engine; }
125 [EditorBrowsable (EditorBrowsableState.Never)]
126 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
127 public TableLayoutSettings LayoutSettings {
128 get { return this.settings; }
130 if (value.isSerialized) {
131 this.settings = value;
132 value.isSerialized = false;
134 throw new NotSupportedException ("LayoutSettings value cannot be set directly.");
140 public int RowCount {
141 get { return settings.RowCount; }
142 set { this.settings.RowCount = value; }
146 [DisplayName ("Rows")]
147 [MergableProperty (false)]
148 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
149 public TableLayoutRowStyleCollection RowStyles {
150 get { return settings.RowStyles; }
154 #region Public Methods
156 [DisplayName ("Cell")]
157 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
158 public TableLayoutPanelCellPosition GetCellPosition (Control control)
160 return settings.GetCellPosition (control);
163 [DisplayName ("Column")]
165 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
166 public int GetColumn (Control control)
168 return settings.GetColumn (control);
171 [DisplayName ("ColumnSpan")]
173 public int GetColumnSpan (Control control)
175 return settings.GetColumnSpan (control);
179 [EditorBrowsable (EditorBrowsableState.Never)]
180 public int[] GetColumnWidths ()
182 return this.column_widths;
185 public Control GetControlFromPosition (int column, int row)
187 if (column < 0 || row < 0)
188 throw new ArgumentException ();
190 TableLayoutPanelCellPosition pos = new TableLayoutPanelCellPosition (column, row);
192 foreach (Control c in this.Controls)
193 if (settings.GetCellPosition (c) == pos)
199 public TableLayoutPanelCellPosition GetPositionFromControl (Control control)
201 for (int x = 0; x < this.actual_positions.GetLength (0); x++)
202 for (int y = 0; y < this.actual_positions.GetLength (1); y++)
203 if (this.actual_positions[x, y] == control)
204 return new TableLayoutPanelCellPosition (x, y);
206 return new TableLayoutPanelCellPosition (-1, -1);
209 [DisplayName ("Row")]
210 [DefaultValue ("-1")]
211 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
212 public int GetRow (Control control)
214 return settings.GetRow (control);
218 [EditorBrowsable (EditorBrowsableState.Never)]
219 public int[] GetRowHeights ()
221 return this.row_heights;
224 [DisplayName ("RowSpan")]
226 public int GetRowSpan (Control control)
228 return settings.GetRowSpan (control);
231 public void SetCellPosition (Control control, TableLayoutPanelCellPosition position)
233 settings.SetCellPosition (control, position);
234 this.PerformLayout ();
237 public void SetColumn (Control control, int column)
239 settings.SetColumn (control, column);
240 this.PerformLayout ();
243 public void SetColumnSpan (Control control, int value)
245 settings.SetColumnSpan (control, value);
246 this.PerformLayout ();
249 public void SetRow (Control control, int row)
251 settings.SetRow (control, row);
252 this.PerformLayout ();
255 public void SetRowSpan (Control control, int value)
257 settings.SetRowSpan (control, value);
258 this.PerformLayout ();
262 #region Protected Methods
263 [EditorBrowsable (EditorBrowsableState.Advanced)]
264 protected override ControlCollection CreateControlsInstance ()
266 return new TableLayoutControlCollection (this);
269 protected virtual void OnCellPaint (TableLayoutCellPaintEventArgs e)
271 TableLayoutCellPaintEventHandler eh = (TableLayoutCellPaintEventHandler)(Events [CellPaintEvent]);
276 [EditorBrowsable (EditorBrowsableState.Advanced)]
277 protected override void OnLayout (LayoutEventArgs levent)
279 base.OnLayout (levent);
282 protected override void OnPaintBackground (PaintEventArgs e)
284 base.OnPaintBackground (e);
286 if (column_widths == null || row_heights == null)
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);
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);
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));
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));
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));
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));
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));
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 tablelayoutpanel 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 // Figure out how wide the panel needs to be
515 int[] column_widths = new int[ColumnCount];
516 float total_column_percentage = 0f;
518 // Figure out how tall each column wants to be
519 for (int i = 0; i < ColumnCount; i++) {
520 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
521 total_column_percentage += ColumnStyles[i].Width;
525 for (int j = 0; j < RowCount; j++) {
526 Control c = actual_positions[i, j];
530 biggest = Math.Max (biggest, c.ExplicitBounds.Width + c.Margin.Horizontal + Padding.Horizontal);
532 biggest = Math.Max (biggest, c.ExplicitBounds.Width + c.Margin.Horizontal + Padding.Horizontal);
536 column_widths[i] = biggest;
539 // Because percentage based rows divy up the remaining space,
540 // we have to make the panel big enough so that all the rows
541 // get bigger, even if we only need one to be bigger.
542 int non_percent_total_width = 0;
543 int percent_total_width = 0;
545 for (int i = 0; i < ColumnCount; i++) {
546 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
547 percent_total_width = Math.Max (percent_total_width, (int)(column_widths[i] / ((ColumnStyles[i].Width) / total_column_percentage)));
549 non_percent_total_width += column_widths[i];
553 // Figure out how tall the panel needs to be
554 int[] row_heights = new int[RowCount];
555 float total_row_percentage = 0f;
557 // Figure out how tall each row wants to be
558 for (int j = 0; j < RowCount; j++) {
559 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
560 total_row_percentage += RowStyles[j].Height;
564 for (int i = 0; i < ColumnCount; i++) {
565 Control c = actual_positions[i, j];
569 biggest = Math.Max (biggest, c.ExplicitBounds.Height + c.Margin.Vertical + Padding.Vertical);
571 biggest = Math.Max (biggest, c.PreferredSize.Height + c.Margin.Vertical + Padding.Vertical);
575 row_heights[j] = biggest;
578 // Because percentage based rows divy up the remaining space,
579 // we have to make the panel big enough so that all the rows
580 // get bigger, even if we only need one to be bigger.
581 int non_percent_total_height = 0;
582 int percent_total_height = 0;
584 for (int j = 0; j < RowCount; j++) {
585 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
586 percent_total_height = Math.Max (percent_total_height, (int)(row_heights[j] / ((RowStyles[j].Height) / total_row_percentage)));
588 non_percent_total_height += row_heights[j];
591 return new Size (non_percent_total_width + percent_total_width, non_percent_total_height + percent_total_height);
595 #region Public Events
596 static object CellPaintEvent = new object ();
598 public event TableLayoutCellPaintEventHandler CellPaint {
599 add { Events.AddHandler (CellPaintEvent, value); }
600 remove { Events.RemoveHandler (CellPaintEvent, value); }
604 #region IExtenderProvider
605 bool IExtenderProvider.CanExtend (object extendee)
607 if (extendee is Control)
608 if ((extendee as Control).Parent == this)