|
//------------------------------------------------------------------------------
// <copyright file="TableLayoutPanel.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms {
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms.Layout;
using System.Reflection;
using System.Runtime.InteropServices;
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel"]/*' />
[ProvideProperty("ColumnSpan", typeof(Control))]
[ProvideProperty("RowSpan", typeof(Control))]
[ProvideProperty("Row", typeof(Control))]
[ProvideProperty("Column", typeof(Control))]
[ProvideProperty("CellPosition", typeof(Control))]
[DefaultProperty("ColumnCount")]
[DesignerSerializer("System.Windows.Forms.Design.TableLayoutPanelCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)]
[Docking(DockingBehavior.Never)]
[Designer("System.Windows.Forms.Design.TableLayoutPanelDesigner, " + AssemblyRef.SystemDesign)]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[SRDescription(SR.DescriptionTableLayoutPanel)]
public class TableLayoutPanel : Panel, IExtenderProvider {
private TableLayoutSettings _tableLayoutSettings;
private static readonly object EventCellPaint = new object();
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.TableLayoutPanel"]/*' />
public TableLayoutPanel() {
_tableLayoutSettings = TableLayout.CreateSettings(this);
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.LayoutEngine"]/*' />
public override LayoutEngine LayoutEngine {
get { return TableLayout.Instance; }
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public TableLayoutSettings LayoutSettings {
get {
return _tableLayoutSettings;
}
set {
if (value != null && value.IsStub) {
// WINRES only scenario.
// we only support table layout settings that have been created from a type converter.
// this is here for localization (WinRes) support.
using (new LayoutTransaction(this, this, PropertyNames.LayoutSettings)) {
// apply RowStyles, ColumnStyles, Row & Column assignments.
_tableLayoutSettings.ApplySettings(value);
}
}
else {
throw new NotSupportedException(SR.GetString(SR.TableLayoutSettingSettingsIsNotSupported));
}
}
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.BorderStyle"]/*' />
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
Localizable(true)
]
public new BorderStyle BorderStyle {
get { return base.BorderStyle; }
set {
base.BorderStyle = value;
Debug.Assert(BorderStyle == value, "BorderStyle should be the same as we set it");
}
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.CellBorderStyle"]/*' />
[
DefaultValue(TableLayoutPanelCellBorderStyle.None),
SRCategory(SR.CatAppearance),
SRDescription(SR.TableLayoutPanelCellBorderStyleDescr),
Localizable(true)
]
public TableLayoutPanelCellBorderStyle CellBorderStyle {
get { return _tableLayoutSettings.CellBorderStyle; }
set {
_tableLayoutSettings.CellBorderStyle = value;
// PERF: dont turn on ResizeRedraw unless we know we need it.
if (value != TableLayoutPanelCellBorderStyle.None) {
SetStyle(ControlStyles.ResizeRedraw, true);
}
this.Invalidate();
Debug.Assert(CellBorderStyle == value, "CellBorderStyle should be the same as we set it");
}
}
private int CellBorderWidth {
get { return _tableLayoutSettings.CellBorderWidth; }
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.Controls"]/*' />
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[SRDescription(SR.ControlControlsDescr)]
public new TableLayoutControlCollection Controls {
get { return (TableLayoutControlCollection)base.Controls; }
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.ColumnCount"]/*' />
/// <devdoc>
/// This sets the maximum number of columns allowed on this table instead of allocating
/// actual spaces for these columns. So it is OK to set ColumnCount to Int32.MaxValue without
/// causing out of memory exception
/// </devdoc>
[SRDescription(SR.GridPanelColumnsDescr)]
[SRCategory(SR.CatLayout)]
[DefaultValue(0)]
[Localizable(true)]
public int ColumnCount {
get { return _tableLayoutSettings.ColumnCount; }
set {
_tableLayoutSettings.ColumnCount = value;
Debug.Assert(ColumnCount == value, "ColumnCount should be the same as we set it");
}
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.GrowStyle"]/*' />
/// <devdoc>
/// Specifies if a TableLayoutPanel will gain additional rows or columns once its existing cells
/// become full. If the value is 'FixedSize' then the TableLayoutPanel will throw an exception
/// when the TableLayoutPanel is over-filled.
/// </devdoc>
[SRDescription(SR.TableLayoutPanelGrowStyleDescr)]
[SRCategory(SR.CatLayout)]
[DefaultValue(TableLayoutPanelGrowStyle.AddRows)]
public TableLayoutPanelGrowStyle GrowStyle {
get {
return _tableLayoutSettings.GrowStyle;
}
set {
_tableLayoutSettings.GrowStyle = value;
}
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.RowCount"]/*' />
/// <devdoc>
/// This sets the maximum number of rows allowed on this table instead of allocating
/// actual spaces for these rows. So it is OK to set RowCount to Int32.MaxValue without
/// causing out of memory exception
/// </devdoc>
[SRDescription(SR.GridPanelRowsDescr)]
[SRCategory(SR.CatLayout)]
[DefaultValue(0)]
[Localizable(true)]
public int RowCount {
get { return _tableLayoutSettings.RowCount; }
set { _tableLayoutSettings.RowCount = value; }
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.RowStyles"]/*' />
[SRDescription(SR.GridPanelRowStylesDescr)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[SRCategory(SR.CatLayout)]
[DisplayName("Rows")]
[MergableProperty(false)]
[Browsable(false)]
public TableLayoutRowStyleCollection RowStyles {
get { return _tableLayoutSettings.RowStyles; }
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.ColumnStyles"]/*' />
[SRDescription(SR.GridPanelColumnStylesDescr)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[SRCategory(SR.CatLayout)]
[DisplayName("Columns")]
[Browsable(false)]
[MergableProperty(false)]
public TableLayoutColumnStyleCollection ColumnStyles {
get { return _tableLayoutSettings.ColumnStyles; }
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.CreateControlsInstance"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override Control.ControlCollection CreateControlsInstance() {
return new TableLayoutControlCollection(this);
}
private bool ShouldSerializeControls() {
TableLayoutControlCollection collection = this.Controls;
return collection != null && collection.Count > 0;
}
#region Extended Properties
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.IExtenderProvider.CanExtend"]/*' />
/// <internalonly/>
bool IExtenderProvider.CanExtend(object obj) {
Control control = obj as Control;
return control != null && control.Parent == this;
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.GetColumnSpan"]/*' />
[SRDescription(SR.GridPanelGetColumnSpanDescr)]
[DefaultValue(1)]
[SRCategory(SR.CatLayout)]
[DisplayName("ColumnSpan")]
public int GetColumnSpan(Control control) {
return _tableLayoutSettings.GetColumnSpan(control);
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.SetColumnSpan"]/*' />
public void SetColumnSpan(Control control, int value) {
// layout.SetColumnSpan() throws ArgumentException if out of range.
_tableLayoutSettings.SetColumnSpan(control, value);
Debug.Assert(GetColumnSpan(control) == value, "GetColumnSpan should be the same as we set it");
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.GetRowSpan"]/*' />
[SRDescription(SR.GridPanelGetRowSpanDescr)]
[DefaultValue(1)]
[SRCategory(SR.CatLayout)]
[DisplayName("RowSpan")]
public int GetRowSpan(Control control) {
return _tableLayoutSettings.GetRowSpan(control);
}
/// <include file='doc\GridPanel.uex' path='docs/doc[@for="GridPanel.SetRowSpan"]/*' />
public void SetRowSpan(Control control, int value) {
// layout.SetRowSpan() throws ArgumentException if out of range.
_tableLayoutSettings.SetRowSpan(control, value);
Debug.Assert(GetRowSpan(control) == value, "GetRowSpan should be the same as we set it");
}
//get the row position of the control
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.GetRow"]/*' />
[DefaultValue(-1)] //if change this value, also change the SerializeViaAdd in TableLayoutControlCollectionCodeDomSerializer
[SRDescription(SR.GridPanelRowDescr)]
[SRCategory(SR.CatLayout)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DisplayName("Row")]
public int GetRow(Control control) {
return _tableLayoutSettings.GetRow(control);
}
//set the row position of the control
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.SetRow"]/*' />
public void SetRow(Control control, int row) {
_tableLayoutSettings.SetRow(control, row);
Debug.Assert(GetRow(control) == row, "GetRow should be the same as we set it");
}
//get the row and column position of the control
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.GetRow"]/*' />
[DefaultValue(typeof(TableLayoutPanelCellPosition), "-1,-1")] //if change this value, also change the SerializeViaAdd in TableLayoutControlCollectionCodeDomSerializer
[SRDescription(SR.GridPanelCellPositionDescr)]
[SRCategory(SR.CatLayout)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DisplayName("Cell")]
public TableLayoutPanelCellPosition GetCellPosition(Control control) {
return _tableLayoutSettings.GetCellPosition(control);
}
//set the row and column of the control
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.SetRow"]/*' />
public void SetCellPosition(Control control, TableLayoutPanelCellPosition position) {
_tableLayoutSettings.SetCellPosition(control, position);
}
//get the column position of the control
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.GetColumn"]/*' />
[DefaultValue(-1)] //if change this value, also change the SerializeViaAdd in TableLayoutControlCollectionCodeDomSerializer
[SRDescription(SR.GridPanelColumnDescr)]
[SRCategory(SR.CatLayout)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DisplayName("Column")]
public int GetColumn(Control control) {
return _tableLayoutSettings.GetColumn(control);
}
//set the column position of the control
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.SetColumn"]/*' />
public void SetColumn(Control control, int column) {
_tableLayoutSettings.SetColumn(control, column);
Debug.Assert(GetColumn(control) == column, "GetColumn should be the same as we set it");
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.GetControlFromPosition"]/*' />
/// <devdoc>
/// get the control which covers the specified row and column. return null if we can't find one
/// </devdoc>
public Control GetControlFromPosition (int column, int row) {
return (Control)_tableLayoutSettings.GetControlFromPosition(column, row);
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // Using Control instead of IArrangedElement intentionally
public TableLayoutPanelCellPosition GetPositionFromControl (Control control) {
return _tableLayoutSettings.GetPositionFromControl(control);
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.ColumnWidths"]/*' />
/// <devdoc>
/// This returns an array representing the widths (in pixels) of the columns in the TableLayoutPanel.
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public int[] GetColumnWidths() {
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(this);
if (containerInfo.Columns == null) {
return new int[0];
}
int[] cw = new int[containerInfo.Columns.Length];
for(int i = 0; i < containerInfo.Columns.Length; i++) {
cw[i] = containerInfo.Columns[i].MinSize;
}
return cw;
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.RowWidths"]/*' />
/// <devdoc>
/// This returns an array representing the heights (in pixels) of the rows in the TableLayoutPanel.
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public int[] GetRowHeights() {
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(this);
if (containerInfo.Rows == null) {
return new int[0];
}
int[] rh = new int[containerInfo.Rows.Length];
for(int i = 0; i < containerInfo.Rows.Length; i++) {
rh[i] = containerInfo.Rows[i].MinSize;
}
return rh;
}
#endregion
#region PaintCode
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.CellPaint"]/*' />
[SRCategory(SR.CatAppearance), SRDescription(SR.TableLayoutPanelOnPaintCellDescr)]
public event TableLayoutCellPaintEventHandler CellPaint {
add {
Events.AddHandler(EventCellPaint, value);
}
remove {
Events.RemoveHandler(EventCellPaint, value);
}
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.OnLayout"]/*' />
/// <internalonly/>
/// <devdoc>
/// When a layout fires, make sure we're painting all of our
/// cell borders.
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnLayout(LayoutEventArgs levent) {
base.OnLayout(levent);
this.Invalidate();
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.OnCellPaint"]/*' />
protected virtual void OnCellPaint(TableLayoutCellPaintEventArgs e) {
TableLayoutCellPaintEventHandler handler = (TableLayoutCellPaintEventHandler)Events[EventCellPaint];
if (handler != null) {
handler(this, e);
}
}
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutPanel.OnPaint"]/*' />
protected override void OnPaintBackground(PaintEventArgs e) {
base.OnPaintBackground(e);
// paint borderstyles on top of the background image in WM_ERASEBKGND
int cellBorderWidth = this.CellBorderWidth;
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(this);
TableLayout.Strip[] colStrips = containerInfo.Columns;
TableLayout.Strip[] rowStrips = containerInfo.Rows;
TableLayoutPanelCellBorderStyle cellBorderStyle = this.CellBorderStyle;
if (colStrips == null || rowStrips == null) {
return;
}
int cols = colStrips.Length;
int rows = rowStrips.Length;
int totalColumnWidths = 0, totalColumnHeights = 0;
Graphics g = e.Graphics;
Rectangle displayRect = DisplayRectangle;
Rectangle clipRect = e.ClipRectangle;
//leave the space for the border
int startx;
bool isRTL = (RightToLeft == RightToLeft.Yes);
if (isRTL) {
startx = displayRect.Right - (cellBorderWidth / 2);
}
else {
startx = displayRect.X + (cellBorderWidth / 2);
}
for (int i = 0; i < cols; i++) {
int starty = displayRect.Y + (cellBorderWidth / 2);
if (isRTL) {
startx -= colStrips[i].MinSize;
}
for (int j = 0; j < rows; j++) {
Rectangle outsideCellBounds = new Rectangle(startx, starty, ((TableLayout.Strip)colStrips[i]).MinSize, ((TableLayout.Strip)rowStrips[j]).MinSize);
Rectangle insideCellBounds = new Rectangle(outsideCellBounds.X + (cellBorderWidth + 1) / 2, outsideCellBounds.Y + (cellBorderWidth + 1)/ 2, outsideCellBounds.Width - (cellBorderWidth + 1) / 2, outsideCellBounds.Height - (cellBorderWidth + 1) / 2);
if (clipRect.IntersectsWith(insideCellBounds)) {
//first, call user's painting code
using (TableLayoutCellPaintEventArgs pcea = new TableLayoutCellPaintEventArgs(g, clipRect, insideCellBounds, i, j)) {
OnCellPaint(pcea);
}
// paint the table border on top.
ControlPaint.PaintTableCellBorder(cellBorderStyle, g, outsideCellBounds);
}
starty += rowStrips[j].MinSize;
// Only sum this up once...
if (i == 0) {
totalColumnHeights += rowStrips[j].MinSize;
}
}
if (!isRTL) {
startx += colStrips[i].MinSize;
}
totalColumnWidths += colStrips[i].MinSize;
}
if (!HScroll && !VScroll && cellBorderStyle != TableLayoutPanelCellBorderStyle.None) {
Rectangle tableBounds = new Rectangle(cellBorderWidth/2 + displayRect.X, cellBorderWidth/2 + displayRect.Y, displayRect.Width - cellBorderWidth, displayRect.Height - cellBorderWidth);
// paint the border of the table if we are not auto scrolling.
// if the borderStyle is Inset or Outset, we can only paint the lower bottom half since otherwise we will have 1 pixel loss at the border.
if (cellBorderStyle == TableLayoutPanelCellBorderStyle.Inset) {
g.DrawLine(SystemPens.ControlDark, tableBounds.Right, tableBounds.Y, tableBounds.Right, tableBounds.Bottom);
g.DrawLine(SystemPens.ControlDark, tableBounds.X, tableBounds.Y + tableBounds.Height - 1, tableBounds.X + tableBounds.Width - 1, tableBounds.Y + tableBounds.Height - 1);
}
else if (cellBorderStyle == TableLayoutPanelCellBorderStyle.Outset) {
using (Pen pen = new Pen(SystemColors.Window)) {
g.DrawLine(pen, tableBounds.X + tableBounds.Width - 1, tableBounds.Y, tableBounds.X + tableBounds.Width - 1, tableBounds.Y + tableBounds.Height - 1);
g.DrawLine(pen, tableBounds.X, tableBounds.Y + tableBounds.Height - 1, tableBounds.X + tableBounds.Width - 1, tableBounds.Y + tableBounds.Height - 1);
}
}
else {
ControlPaint.PaintTableCellBorder(cellBorderStyle, g, tableBounds);
}
ControlPaint.PaintTableControlBorder(cellBorderStyle, g, displayRect);
}
else {
ControlPaint.PaintTableControlBorder(cellBorderStyle, g, displayRect);
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void ScaleCore(float dx, float dy) {
base.ScaleCore(dx, dy);
ScaleAbsoluteStyles(new SizeF(dx,dy));
}
/// <include file='doc\Form.uex' path='docs/doc[@for="Form.ScaleControl"]/*' />
/// <devdoc>
/// Scale this form. Form overrides this to enforce a maximum / minimum size.
/// </devdoc>
protected override void ScaleControl(SizeF factor, BoundsSpecified specified) {
base.ScaleControl(factor, specified);
ScaleAbsoluteStyles(factor);
}
private void ScaleAbsoluteStyles(SizeF factor) {
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(this);
int i = 0;
// VSWhidbey 432427: the last row/column can be larger than the
// absolutely styled column width.
int lastRowHeight = -1;
int lastRow = containerInfo.Rows.Length -1;
if (containerInfo.Rows.Length > 0) {
lastRowHeight = containerInfo.Rows[lastRow].MinSize;
}
int lastColumnHeight = -1;
int lastColumn = containerInfo.Columns.Length -1;
if (containerInfo.Columns.Length > 0) {
lastColumnHeight = containerInfo.Columns[containerInfo.Columns.Length -1].MinSize;
}
foreach(ColumnStyle cs in ColumnStyles) {
if (cs.SizeType == SizeType.Absolute){
if (i == lastColumn && lastColumnHeight > 0) {
// the last column is typically expanded to fill the table. use the actual
// width in this case.
cs.Width = (float)Math.Round(lastColumnHeight * factor.Width);
}
else {
cs.Width = (float)Math.Round(cs.Width * factor.Width);
}
}
i++;
}
i = 0;
foreach(RowStyle rs in RowStyles) {
if (rs.SizeType == SizeType.Absolute) {
if (i == lastRow && lastRowHeight > 0) {
// the last row is typically expanded to fill the table. use the actual
// width in this case.
rs.Height = (float)Math.Round(lastRowHeight * factor.Height);
}
else {
rs.Height = (float)Math.Round(rs.Height * factor.Height);
}
}
}
}
#endregion
}
#region ControlCollection
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutControlCollection"]/*' />
/// <devdoc>
/// <para>Represents a collection of controls on the TableLayoutPanel.</para>
/// </devdoc>
[ListBindable(false)]
[DesignerSerializer("System.Windows.Forms.Design.TableLayoutControlCollectionCodeDomSerializer, " + AssemblyRef.SystemDesign, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + AssemblyRef.SystemDesign)]
public class TableLayoutControlCollection : Control.ControlCollection {
private TableLayoutPanel _container;
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutControlCollection.TableLayoutControlCollection"]/*' />
public TableLayoutControlCollection(TableLayoutPanel container) : base(container) {
_container = (TableLayoutPanel)container;
}
//the container of this TableLayoutControlCollection
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutControlCollection.Container"]/*' />
public TableLayoutPanel Container {
get { return _container; }
}
//Add control to cell (x, y) on the table. The control becomes absolutely positioned if neither x nor y is equal to -1
/// <include file='doc\TableLayoutPanel.uex' path='docs/doc[@for="TableLayoutControlCollection.Add"]/*' />
public virtual void Add(Control control, int column, int row) {
base.Add(control);
_container.SetColumn(control, column);
_container.SetRow(control, row);
}
}
#endregion
}
|