|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Windows.Forms.Layout;
namespace System.Windows.Forms;
/// <summary>
/// This is a wrapper class to expose interesting properties of TableLayout
/// </summary>
[TypeConverter(typeof(TableLayoutSettingsTypeConverter))]
[Serializable] // This class participates in resx serialization.
public sealed partial class TableLayoutSettings : LayoutSettings, ISerializable
{
private static readonly int[] s_borderStyleToOffset =
[
0, // None
1, // Single
2, // Inset
3, // InsetDouble
2, // Outset
3, // OutsetDouble
3 // OutsetPartial
];
private TableLayoutPanelCellBorderStyle _borderStyle;
private TableLayoutSettingsStub? _stub;
// used by TableLayoutSettingsTypeConverter
internal TableLayoutSettings()
: base(null)
{
_stub = new TableLayoutSettingsStub();
}
internal TableLayoutSettings(IArrangedElement owner)
: base(owner)
{
}
private TableLayoutSettings(SerializationInfo serializationInfo, StreamingContext context)
: this()
{
TypeConverter converter = TypeDescriptor.GetConverter(this);
string? stringVal = serializationInfo.GetString("SerializedString");
if (!string.IsNullOrEmpty(stringVal))
{
if (converter.ConvertFromInvariantString(stringVal) is TableLayoutSettings tls)
{
ApplySettings(tls);
}
}
}
public override LayoutEngine LayoutEngine => TableLayout.Instance;
/// <summary>
/// Internal as this is a TableLayoutPanel feature only.
/// </summary>
[DefaultValue(TableLayoutPanelCellBorderStyle.None)]
[SRCategory(nameof(SR.CatAppearance))]
[SRDescription(nameof(SR.TableLayoutPanelCellBorderStyleDescr))]
internal TableLayoutPanelCellBorderStyle CellBorderStyle
{
get => _borderStyle;
set
{
// valid values are 0x0 to 0x6
SourceGenerated.EnumValidator.Validate(value);
_borderStyle = value;
// set the CellBorderWidth according to the current CellBorderStyle.
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner!);
containerInfo.CellBorderWidth = s_borderStyleToOffset[(int)value];
LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.CellBorderStyle);
Debug.Assert(CellBorderStyle == value, "CellBorderStyle should be the same as we set");
}
}
[DefaultValue(0)]
internal int CellBorderWidth => TableLayout.GetContainerInfo(Owner!).CellBorderWidth;
/// <summary>
/// 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
/// </summary>
[SRDescription(nameof(SR.GridPanelColumnsDescr))]
[SRCategory(nameof(SR.CatLayout))]
[DefaultValue(0)]
public int ColumnCount
{
get
{
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner!);
return containerInfo.MaxColumns;
}
set
{
ArgumentOutOfRangeException.ThrowIfNegative(value);
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner!);
containerInfo.MaxColumns = value;
LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.Columns);
Debug.Assert(ColumnCount == value, "the max columns should equal to the value we set it to");
}
}
/// <summary>
/// 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
/// </summary>
[SRDescription(nameof(SR.GridPanelRowsDescr))]
[SRCategory(nameof(SR.CatLayout))]
[DefaultValue(0)]
public int RowCount
{
get
{
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner!);
return containerInfo.MaxRows;
}
set
{
ArgumentOutOfRangeException.ThrowIfNegative(value);
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner!);
containerInfo.MaxRows = value;
LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.Rows);
Debug.Assert(RowCount == value, "the max rows should equal to the value we set it to");
}
}
[SRDescription(nameof(SR.GridPanelRowStylesDescr))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[SRCategory(nameof(SR.CatLayout))]
public TableLayoutRowStyleCollection RowStyles
{
get
{
if (IsStub)
{
return _stub.RowStyles;
}
else
{
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner!);
return containerInfo.RowStyles;
}
}
}
[SRDescription(nameof(SR.GridPanelColumnStylesDescr))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[SRCategory(nameof(SR.CatLayout))]
public TableLayoutColumnStyleCollection ColumnStyles
{
get
{
if (IsStub)
{
return _stub.ColumnStyles;
}
else
{
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner!);
return containerInfo.ColumnStyles;
}
}
}
/// <summary>
/// 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.
/// </summary>
[SRDescription(nameof(SR.TableLayoutPanelGrowStyleDescr))]
[SRCategory(nameof(SR.CatLayout))]
[DefaultValue(TableLayoutPanelGrowStyle.AddRows)]
public TableLayoutPanelGrowStyle GrowStyle
{
get => TableLayout.GetContainerInfo(Owner!).GrowStyle;
set
{
// valid values are 0x0 to 0x2
SourceGenerated.EnumValidator.Validate(value);
TableLayout.ContainerInfo containerInfo = TableLayout.GetContainerInfo(Owner!);
if (containerInfo.GrowStyle != value)
{
containerInfo.GrowStyle = value;
LayoutTransaction.DoLayout(Owner, Owner, PropertyNames.GrowStyle);
}
}
}
[MemberNotNullWhen(true, nameof(_stub))]
internal bool IsStub
{
get
{
if (_stub is not null)
{
return true;
}
return false;
}
}
internal void ApplySettings(TableLayoutSettings settings)
{
if (settings.IsStub)
{
if (!IsStub)
{
// we're the real-live thing here, gotta walk through and touch controls
settings._stub.ApplySettings(this);
}
else
{
// we're just copying another stub into us, just replace the member
_stub = settings._stub;
}
}
}
#region Extended Properties
public int GetColumnSpan(object control)
{
ArgumentNullException.ThrowIfNull(control);
if (IsStub)
{
return _stub.GetColumnSpan(control);
}
else
{
IArrangedElement element = LayoutEngine.CastToArrangedElement(control);
return TableLayout.GetLayoutInfo(element).ColumnSpan;
}
}
public void SetColumnSpan(object control, int value)
{
ArgumentNullException.ThrowIfNull(control);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
if (IsStub)
{
_stub.SetColumnSpan(control, value);
}
else
{
IArrangedElement element = LayoutEngine.CastToArrangedElement(control);
if (element.Container is not null)
{
TableLayout.ClearCachedAssignments(TableLayout.GetContainerInfo(element.Container));
}
TableLayout.GetLayoutInfo(element).ColumnSpan = value;
LayoutTransaction.DoLayout(element.Container, element, PropertyNames.ColumnSpan);
Debug.Assert(GetColumnSpan(element) == value, "column span should equal to the value we set");
}
}
public int GetRowSpan(object control)
{
ArgumentNullException.ThrowIfNull(control);
if (IsStub)
{
return _stub.GetRowSpan(control);
}
else
{
IArrangedElement element = LayoutEngine.CastToArrangedElement(control);
return TableLayout.GetLayoutInfo(element).RowSpan;
}
}
public void SetRowSpan(object control, int value)
{
ArgumentNullException.ThrowIfNull(control);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
if (IsStub)
{
_stub.SetRowSpan(control, value);
}
else
{
IArrangedElement element = LayoutEngine.CastToArrangedElement(control);
if (element.Container is not null)
{
TableLayout.ClearCachedAssignments(TableLayout.GetContainerInfo(element.Container));
}
TableLayout.GetLayoutInfo(element).RowSpan = value;
LayoutTransaction.DoLayout(element.Container, element, PropertyNames.RowSpan);
Debug.Assert(GetRowSpan(element) == value, "row span should equal to the value we set");
}
}
/// <summary>
/// Get the row position of the element
/// </summary>
[SRDescription(nameof(SR.GridPanelRowDescr))]
[SRCategory(nameof(SR.CatLayout))]
[DefaultValue(-1)]
public int GetRow(object control)
{
ArgumentNullException.ThrowIfNull(control);
if (IsStub)
{
return _stub.GetRow(control);
}
else
{
IArrangedElement element = LayoutEngine.CastToArrangedElement(control);
TableLayout.LayoutInfo layoutInfo = TableLayout.GetLayoutInfo(element);
return layoutInfo.RowPosition;
}
}
/// <summary>
/// Set the row position of the element
/// If we set the row position to -1, it will automatically switch the control from
/// absolutely positioned to non-absolutely positioned
/// </summary>
public void SetRow(object control, int row)
{
ArgumentNullException.ThrowIfNull(control);
ArgumentOutOfRangeException.ThrowIfLessThan(row, -1);
SetCellPosition(control, row, -1, rowSpecified: true, colSpecified: false);
}
/// <summary>
/// Get the column position of the element
/// </summary>
[SRDescription(nameof(SR.TableLayoutSettingsGetCellPositionDescr))]
[SRCategory(nameof(SR.CatLayout))]
[DefaultValue(-1)]
public TableLayoutPanelCellPosition GetCellPosition(object control)
{
ArgumentNullException.ThrowIfNull(control);
return new TableLayoutPanelCellPosition(GetColumn(control), GetRow(control));
}
/// <summary>
/// Set the column position of the element
/// </summary>
[SRDescription(nameof(SR.TableLayoutSettingsSetCellPositionDescr))]
[SRCategory(nameof(SR.CatLayout))]
[DefaultValue(-1)]
public void SetCellPosition(object control, TableLayoutPanelCellPosition cellPosition)
{
ArgumentNullException.ThrowIfNull(control);
SetCellPosition(control, cellPosition.Row, cellPosition.Column, rowSpecified: true, colSpecified: true);
}
/// <summary>
/// Get the column position of the element
/// </summary>
[SRDescription(nameof(SR.GridPanelColumnDescr))]
[SRCategory(nameof(SR.CatLayout))]
[DefaultValue(-1)]
public int GetColumn(object control)
{
ArgumentNullException.ThrowIfNull(control);
if (IsStub)
{
return _stub.GetColumn(control);
}
else
{
IArrangedElement element = LayoutEngine.CastToArrangedElement(control);
TableLayout.LayoutInfo layoutInfo = TableLayout.GetLayoutInfo(element);
return layoutInfo.ColumnPosition;
}
}
/// <summary>
/// Set the column position of the element
/// If we set the column position to -1, it will automatically switch the control from
/// absolutely positioned to non-absolutely positioned
/// </summary>
public void SetColumn(object control, int column)
{
ArgumentNullException.ThrowIfNull(control);
ArgumentOutOfRangeException.ThrowIfLessThan(column, -1);
if (IsStub)
{
_stub.SetColumn(control, column);
}
else
{
SetCellPosition(control, -1, column, rowSpecified: false, colSpecified: true);
}
}
private void SetCellPosition(object control, int row, int column, bool rowSpecified, bool colSpecified)
{
if (IsStub)
{
if (colSpecified)
{
_stub.SetColumn(control, column);
}
if (rowSpecified)
{
_stub.SetRow(control, row);
}
}
else
{
IArrangedElement element = LayoutEngine.CastToArrangedElement(control);
if (element.Container is not null)
{
TableLayout.ClearCachedAssignments(TableLayout.GetContainerInfo(element.Container));
}
TableLayout.LayoutInfo layoutInfo = TableLayout.GetLayoutInfo(element);
if (colSpecified)
{
layoutInfo.ColumnPosition = column;
}
if (rowSpecified)
{
layoutInfo.RowPosition = row;
}
LayoutTransaction.DoLayout(element.Container, element, PropertyNames.TableIndex);
Debug.Assert(!colSpecified || GetColumn(element) == column, "column position should equal to what we set");
Debug.Assert(!rowSpecified || GetRow(element) == row, "row position should equal to what we set");
}
}
/// <summary>
/// Get the element which covers the specified row and column. return null if we can't find one
/// </summary>
internal IArrangedElement? GetControlFromPosition(int column, int row)
{
return TableLayout.GetControlFromPosition(Owner!, column, row);
}
internal TableLayoutPanelCellPosition GetPositionFromControl(IArrangedElement? element)
{
return TableLayout.GetPositionFromControl(Owner, element);
}
#endregion
void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
{
TypeConverter converter;
if (!Control.UseComponentModelRegisteredTypes)
{
converter = TypeDescriptor.GetConverter(this);
}
else
{
// Call the trim safe API
TypeDescriptor.RegisterType<TableLayoutSettings>();
converter = TypeDescriptor.GetConverterFromRegisteredType(this);
}
string? stringVal = converter.ConvertToInvariantString(this);
if (!string.IsNullOrEmpty(stringVal))
{
si.AddValue("SerializedString", stringVal);
}
}
internal List<ControlInformation> GetControlsInformation()
{
if (IsStub)
{
return _stub.GetControlsInformation();
}
else
{
List<ControlInformation> controlsInfo = new(Owner!.Children.Count);
if (Control.UseComponentModelRegisteredTypes)
{
TypeDescriptor.RegisterType<Control>();
}
foreach (IArrangedElement element in Owner.Children)
{
if (element is Control c)
{
ControlInformation controlInfo = default;
// We need to go through the PropertyDescriptor for the Name property
// since it is shadowed.
PropertyDescriptor? prop;
if (!Control.UseComponentModelRegisteredTypes)
{
prop = TypeDescriptor.GetProperties(c)["Name"];
}
else
{
// Call the trim safe API
prop = TypeDescriptor.GetPropertiesFromRegisteredType(c)["Name"];
}
if (prop is not null && prop.PropertyType == typeof(string))
{
controlInfo.Name = prop.GetValue(c);
}
controlInfo.Row = GetRow(c);
controlInfo.RowSpan = GetRowSpan(c);
controlInfo.Column = GetColumn(c);
controlInfo.ColumnSpan = GetColumnSpan(c);
controlsInfo.Add(controlInfo);
}
}
return controlsInfo;
}
}
}
|