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;
140 throw new NotSupportedException ("LayoutSettings value cannot be set directly.");
146 public int RowCount {
147 get { return settings.RowCount; }
148 set { settings.RowCount = value; }
152 [DisplayName ("Rows")]
153 [MergableProperty (false)]
154 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
155 public TableLayoutRowStyleCollection RowStyles {
156 get { return settings.RowStyles; }
160 #region Public Methods
162 [DisplayName ("Cell")]
163 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
164 public TableLayoutPanelCellPosition GetCellPosition (Control control)
166 return settings.GetCellPosition (control);
169 [DisplayName ("Column")]
171 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
172 public int GetColumn (Control control)
174 return settings.GetColumn (control);
177 [DisplayName ("ColumnSpan")]
179 public int GetColumnSpan (Control control)
181 return settings.GetColumnSpan (control);
185 [EditorBrowsable (EditorBrowsableState.Never)]
186 public int[] GetColumnWidths ()
188 return this.column_widths;
191 public Control GetControlFromPosition (int column, int row)
193 if (column < 0 || row < 0)
194 throw new ArgumentException ();
196 TableLayoutPanelCellPosition pos = new TableLayoutPanelCellPosition (column, row);
198 foreach (Control c in this.Controls)
199 if (settings.GetCellPosition (c) == pos)
205 public TableLayoutPanelCellPosition GetPositionFromControl (Control control)
207 for (int x = 0; x < this.actual_positions.GetLength (0); x++)
208 for (int y = 0; y < this.actual_positions.GetLength (1); y++)
209 if (this.actual_positions[x, y] == control)
210 return new TableLayoutPanelCellPosition (x, y);
212 return new TableLayoutPanelCellPosition (-1, -1);
215 [DisplayName ("Row")]
216 [DefaultValue ("-1")]
217 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
218 public int GetRow (Control control)
220 return settings.GetRow (control);
224 [EditorBrowsable (EditorBrowsableState.Never)]
225 public int[] GetRowHeights ()
227 return this.row_heights;
230 [DisplayName ("RowSpan")]
232 public int GetRowSpan (Control control)
234 return settings.GetRowSpan (control);
237 public void SetCellPosition (Control control, TableLayoutPanelCellPosition position)
239 settings.SetCellPosition (control, position);
242 public void SetColumn (Control control, int column)
244 settings.SetColumn (control, column);
247 public void SetColumnSpan (Control control, int value)
249 settings.SetColumnSpan (control, value);
252 public void SetRow (Control control, int row)
254 settings.SetRow (control, row);
257 public void SetRowSpan (Control control, int value)
259 settings.SetRowSpan (control, value);
263 #region Protected Methods
264 [EditorBrowsable (EditorBrowsableState.Advanced)]
265 protected override ControlCollection CreateControlsInstance ()
267 return new TableLayoutControlCollection (this);
270 protected virtual void OnCellPaint (TableLayoutCellPaintEventArgs e)
272 TableLayoutCellPaintEventHandler eh = (TableLayoutCellPaintEventHandler)(Events [CellPaintEvent]);
277 [EditorBrowsable (EditorBrowsableState.Advanced)]
278 protected override void OnLayout (LayoutEventArgs levent)
280 base.OnLayout (levent);
284 protected override void OnPaintBackground (PaintEventArgs e)
286 base.OnPaintBackground (e);
290 int border_width = GetCellBorderWidth (CellBorderStyle);
292 int x = border_width;
293 int y = border_width;
295 for (int i = 0; i < column_widths.Length; i++) {
296 for (int j = 0; j < row_heights.Length; j++) {
297 this.OnCellPaint (new TableLayoutCellPaintEventArgs (e.Graphics, e.ClipRectangle, new Rectangle (x, y, column_widths[i] + border_width, row_heights[j] + border_width), i, j));
298 y += row_heights[j] + border_width;
301 x += column_widths[i] + border_width;
306 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
308 base.ScaleControl (factor, specified);
311 [EditorBrowsable (EditorBrowsableState.Never)]
312 protected override void ScaleCore (float dx, float dy)
314 base.ScaleCore (dx, dy);
318 #region Internal Methods
319 internal static int GetCellBorderWidth (TableLayoutPanelCellBorderStyle style)
322 case TableLayoutPanelCellBorderStyle.Single:
324 case TableLayoutPanelCellBorderStyle.Inset:
325 case TableLayoutPanelCellBorderStyle.Outset:
327 case TableLayoutPanelCellBorderStyle.InsetDouble:
328 case TableLayoutPanelCellBorderStyle.OutsetPartial:
329 case TableLayoutPanelCellBorderStyle.OutsetDouble:
336 private void DrawCellBorders (PaintEventArgs e)
338 Rectangle paint_here = new Rectangle (Point.Empty, this.Size);
340 switch (CellBorderStyle) {
341 case TableLayoutPanelCellBorderStyle.Single:
342 DrawSingleBorder (e.Graphics, paint_here);
344 case TableLayoutPanelCellBorderStyle.Inset:
345 DrawInsetBorder (e.Graphics, paint_here);
347 case TableLayoutPanelCellBorderStyle.InsetDouble:
348 DrawInsetDoubleBorder (e.Graphics, paint_here);
350 case TableLayoutPanelCellBorderStyle.Outset:
351 DrawOutsetBorder (e.Graphics, paint_here);
353 case TableLayoutPanelCellBorderStyle.OutsetDouble:
354 case TableLayoutPanelCellBorderStyle.OutsetPartial:
355 DrawOutsetDoubleBorder (e.Graphics, paint_here);
360 private void DrawSingleBorder (Graphics g, Rectangle rect)
362 ControlPaint.DrawBorder (g, rect, SystemColors.ControlDark, ButtonBorderStyle.Solid);
364 int x = DisplayRectangle.X;
365 int y = DisplayRectangle.Y;
367 for (int i = 0; i < column_widths.Length - 1; i++) {
368 x += column_widths[i] + 1;
370 g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 2));
373 for (int j = 0; j < row_heights.Length - 1; j++) {
374 y += row_heights[j] + 1;
376 g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 2, y));
380 private void DrawInsetBorder (Graphics g, Rectangle rect)
382 ControlPaint.DrawBorder3D (g, rect, Border3DStyle.Etched);
384 int x = DisplayRectangle.X;
385 int y = DisplayRectangle.Y;
387 for (int i = 0; i < column_widths.Length - 1; i++) {
388 x += column_widths[i] + 2;
390 g.DrawLine (SystemPens.ControlDark, new Point (x, 1), new Point (x, Bottom - 3));
391 g.DrawLine (Pens.White, new Point (x + 1, 1), new Point (x + 1, Bottom - 3));
394 for (int j = 0; j < row_heights.Length - 1; j++) {
395 y += row_heights[j] + 2;
397 g.DrawLine (SystemPens.ControlDark, new Point (1, y), new Point (Right - 3, y));
398 g.DrawLine (Pens.White, new Point (1, y + 1), new Point (Right - 3, y + 1));
402 private void DrawOutsetBorder (Graphics g, Rectangle rect)
404 g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left + 1, rect.Top + 1, rect.Width - 2, rect.Height - 2));
405 g.DrawRectangle (Pens.White, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
407 int x = DisplayRectangle.X;
408 int y = DisplayRectangle.Y;
410 for (int i = 0; i < column_widths.Length - 1; i++) {
411 x += column_widths[i] + 2;
413 g.DrawLine (Pens.White, new Point (x, 1), new Point (x, Bottom - 3));
414 g.DrawLine (SystemPens.ControlDark, new Point (x + 1, 1), new Point (x + 1, Bottom - 3));
417 for (int j = 0; j < row_heights.Length - 1; j++) {
418 y += row_heights[j] + 2;
420 g.DrawLine (Pens.White, new Point (1, y), new Point (Right - 3, y));
421 g.DrawLine (SystemPens.ControlDark, new Point (1, y + 1), new Point (Right - 3, y + 1));
425 private void DrawOutsetDoubleBorder (Graphics g, Rectangle rect)
430 g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left + 2, rect.Top + 2, rect.Width - 2, rect.Height - 2));
431 g.DrawRectangle (Pens.White, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
433 int x = DisplayRectangle.X;
434 int y = DisplayRectangle.Y;
436 for (int i = 0; i < column_widths.Length - 1; i++) {
437 x += column_widths[i] + 3;
439 g.DrawLine (Pens.White, new Point (x, 3), new Point (x, Bottom - 5));
440 g.DrawLine (SystemPens.ControlDark, new Point (x + 2, 3), new Point (x + 2, Bottom - 5));
443 for (int j = 0; j < row_heights.Length - 1; j++) {
444 y += row_heights[j] + 3;
446 g.DrawLine (Pens.White, new Point (3, y), new Point (Right - 4, y));
447 g.DrawLine (SystemPens.ControlDark, new Point (3, y + 2), new Point (Right - 4, y + 2));
450 x = DisplayRectangle.X;
451 y = DisplayRectangle.Y;
453 for (int i = 0; i < column_widths.Length - 1; i++) {
454 x += column_widths[i] + 3;
456 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5));
459 for (int j = 0; j < row_heights.Length - 1; j++) {
460 y += row_heights[j] + 3;
462 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1));
466 private void DrawInsetDoubleBorder (Graphics g, Rectangle rect)
471 g.DrawRectangle (Pens.White, new Rectangle (rect.Left + 2, rect.Top + 2, rect.Width - 2, rect.Height - 2));
472 g.DrawRectangle (SystemPens.ControlDark, new Rectangle (rect.Left, rect.Top, rect.Width - 2, rect.Height - 2));
474 int x = DisplayRectangle.X;
475 int y = DisplayRectangle.Y;
477 for (int i = 0; i < column_widths.Length - 1; i++) {
478 x += column_widths[i] + 3;
480 g.DrawLine (SystemPens.ControlDark, new Point (x, 3), new Point (x, Bottom - 5));
481 g.DrawLine (Pens.White, new Point (x + 2, 3), new Point (x + 2, Bottom - 5));
484 for (int j = 0; j < row_heights.Length - 1; j++) {
485 y += row_heights[j] + 3;
487 g.DrawLine (SystemPens.ControlDark, new Point (3, y), new Point (Right - 4, y));
488 g.DrawLine (Pens.White, new Point (3, y + 2), new Point (Right - 4, y + 2));
491 x = DisplayRectangle.X;
492 y = DisplayRectangle.Y;
494 for (int i = 0; i < column_widths.Length - 1; i++) {
495 x += column_widths[i] + 3;
497 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (x + 1, 3), new Point (x + 1, Bottom - 5));
500 for (int j = 0; j < row_heights.Length - 1; j++) {
501 y += row_heights[j] + 3;
503 g.DrawLine (ThemeEngine.Current.ResPool.GetPen (BackColor), new Point (3, y + 1), new Point (Right - 4, y + 1));
507 internal override Size GetPreferredSizeCore (Size proposedSize)
509 // If the tablelayoutowner is autosize, we have to make sure it is big enough
510 // to hold every non-autosize control
511 actual_positions = (LayoutEngine as TableLayout).CalculateControlPositions (this, Math.Max (ColumnCount, 1), Math.Max (RowCount, 1));
513 // Use actual row/column counts, not user set ones
514 int actual_cols = actual_positions.GetLength (0);
515 int actual_rows = actual_positions.GetLength (1);
517 // Figure out how wide the owner needs to be
518 int[] column_widths = new int[actual_cols];
519 float total_column_percentage = 0f;
521 // Figure out how tall each column wants to be
522 for (int i = 0; i < actual_cols; i++) {
523 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
524 total_column_percentage += ColumnStyles[i].Width;
528 for (int j = 0; j < actual_rows; j++) {
529 Control c = actual_positions[i, j];
533 biggest = Math.Max (biggest, c.ExplicitBounds.Width + c.Margin.Horizontal + Padding.Horizontal);
535 biggest = Math.Max (biggest, c.PreferredSize.Width + c.Margin.Horizontal + Padding.Horizontal);
539 column_widths[i] = biggest;
542 // Because percentage based rows divy up the remaining space,
543 // we have to make the owner big enough so that all the rows
544 // get bigger, even if we only need one to be bigger.
545 int non_percent_total_width = 0;
546 int percent_total_width = 0;
548 for (int i = 0; i < actual_cols; i++) {
549 if (i < ColumnStyles.Count && ColumnStyles[i].SizeType == SizeType.Percent)
550 percent_total_width = Math.Max (percent_total_width, (int)(column_widths[i] / ((ColumnStyles[i].Width) / total_column_percentage)));
552 non_percent_total_width += column_widths[i];
556 // Figure out how tall the owner needs to be
557 int[] row_heights = new int[actual_rows];
558 float total_row_percentage = 0f;
560 // Figure out how tall each row wants to be
561 for (int j = 0; j < actual_rows; j++) {
562 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
563 total_row_percentage += RowStyles[j].Height;
567 for (int i = 0; i < actual_cols; i++) {
568 Control c = actual_positions[i, j];
572 biggest = Math.Max (biggest, c.ExplicitBounds.Height + c.Margin.Vertical + Padding.Vertical);
574 biggest = Math.Max (biggest, c.PreferredSize.Height + c.Margin.Vertical + Padding.Vertical);
578 row_heights[j] = biggest;
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_height = 0;
585 int percent_total_height = 0;
587 for (int j = 0; j < actual_rows; j++) {
588 if (j < RowStyles.Count && RowStyles[j].SizeType == SizeType.Percent)
589 percent_total_height = Math.Max (percent_total_height, (int)(row_heights[j] / ((RowStyles[j].Height) / total_row_percentage)));
591 non_percent_total_height += row_heights[j];
594 int border_width = GetCellBorderWidth (CellBorderStyle);
595 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)));
599 #region Public Events
600 static object CellPaintEvent = new object ();
602 public event TableLayoutCellPaintEventHandler CellPaint {
603 add { Events.AddHandler (CellPaintEvent, value); }
604 remove { Events.RemoveHandler (CellPaintEvent, value); }
608 #region IExtenderProvider
609 bool IExtenderProvider.CanExtend (object obj)
612 if ((obj as Control).Parent == this)