|
// 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.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Text;
using System.Xml;
namespace System.Windows.Forms.Layout;
public class TableLayoutSettingsTypeConverter : TypeConverter
{
/// <summary>
/// Determines if this converter can convert an object in the given source
/// type to the native type of the converter.
/// </summary>
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
/// <summary>
/// Gets a value indicating whether this converter can
/// convert an object to the given destination type using the context.
/// </summary>
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
{
if (destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
/// <summary>
/// Converts the given object to the converter's native type.
/// </summary>
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
if (value is string valueAsString)
{
XmlDocument tableLayoutSettingsXml = new();
tableLayoutSettingsXml.LoadXml(valueAsString);
TableLayoutSettings settings = new();
ParseControls(settings, tableLayoutSettingsXml.GetElementsByTagName("Control"));
ParseStyles(settings, tableLayoutSettingsXml.GetElementsByTagName("Columns"), /*isColumn=*/true);
ParseStyles(settings, tableLayoutSettingsXml.GetElementsByTagName("Rows"), /*isColumn=*/false);
return settings;
}
return base.ConvertFrom(context, culture, value);
}
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (value is TableLayoutSettings tableLayoutSettings && (destinationType == typeof(string)))
{
StringBuilder xmlStringBuilder = new();
XmlWriter xmlWriter = XmlWriter.Create(xmlStringBuilder);
xmlWriter.WriteStartElement("TableLayoutSettings");
// write controls
xmlWriter.WriteStartElement("Controls");
foreach (TableLayoutSettings.ControlInformation c in tableLayoutSettings.GetControlsInformation())
{
if (c.Name is null)
{
throw new InvalidOperationException(SR.TableLayoutSettingsConverterNoName);
}
xmlWriter.WriteStartElement("Control");
xmlWriter.WriteAttributeString("Name", c.Name.ToString());
xmlWriter.WriteAttributeString("Row", c.Row.ToString(CultureInfo.CurrentCulture));
xmlWriter.WriteAttributeString("RowSpan", c.RowSpan.ToString(CultureInfo.CurrentCulture));
xmlWriter.WriteAttributeString("Column", c.Column.ToString(CultureInfo.CurrentCulture));
xmlWriter.WriteAttributeString("ColumnSpan", c.ColumnSpan.ToString(CultureInfo.CurrentCulture));
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement(); // end Controls
// write columns
xmlWriter.WriteStartElement("Columns");
StringBuilder columnStyles = new();
foreach (ColumnStyle colStyle in tableLayoutSettings.ColumnStyles)
{
columnStyles.Append($"{colStyle.SizeType},{colStyle.Width},");
}
if (columnStyles.Length > 0)
{
columnStyles.Remove(columnStyles.Length - 1, 1);
}
xmlWriter.WriteAttributeString("Styles", columnStyles.ToString());
xmlWriter.WriteEndElement(); // end columns
// write rows
xmlWriter.WriteStartElement("Rows");
StringBuilder rowStyles = new();
foreach (RowStyle rowStyle in tableLayoutSettings.RowStyles)
{
rowStyles.Append($"{rowStyle.SizeType},{rowStyle.Height},");
}
if (rowStyles.Length > 0)
{
rowStyles.Remove(rowStyles.Length - 1, 1);
}
xmlWriter.WriteAttributeString("Styles", rowStyles.ToString());
xmlWriter.WriteEndElement(); // end Rows
xmlWriter.WriteEndElement(); // end TableLayoutSettings
xmlWriter.Close();
return xmlStringBuilder.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
private static string? GetAttributeValue(XmlNode node, string attribute)
{
XmlAttribute? attr = node.Attributes?[attribute];
return attr?.Value;
}
private static int GetAttributeValue(XmlNode node, string attribute, int valueIfNotFound)
{
string? attributeValue = GetAttributeValue(node, attribute);
return int.TryParse(attributeValue, out int result) ? result : valueIfNotFound;
}
private static void ParseControls(TableLayoutSettings settings, XmlNodeList controlXmlFragments)
{
foreach (XmlNode controlXmlNode in controlXmlFragments)
{
string? name = GetAttributeValue(controlXmlNode, "Name");
if (!string.IsNullOrEmpty(name))
{
int row = GetAttributeValue(controlXmlNode, "Row", /*default*/-1);
int rowSpan = GetAttributeValue(controlXmlNode, "RowSpan", /*default*/1);
int column = GetAttributeValue(controlXmlNode, "Column", /*default*/-1);
int columnSpan = GetAttributeValue(controlXmlNode, "ColumnSpan", /*default*/1);
settings.SetRow(name, row);
settings.SetColumn(name, column);
settings.SetRowSpan(name, rowSpan);
settings.SetColumnSpan(name, columnSpan);
}
}
}
private static void ParseStyles(TableLayoutSettings settings, XmlNodeList controlXmlFragments, bool columns)
{
foreach (XmlNode styleXmlNode in controlXmlFragments)
{
string? styleString = GetAttributeValue(styleXmlNode, "Styles");
Type sizeTypeType = typeof(SizeType);
// styleString will consist of N Column/Row styles serialized in the following format
// (Percent | Absolute | AutoSize), (24 | 24.4 | 24,4)
// Two examples:
// Percent,23.3,Percent,46.7,Percent,30
// Percent,23,3,Percent,46,7,Percent,30
// Note we get either . or , based on the culture the TableLayoutSettings were serialized in
if (!string.IsNullOrEmpty(styleString))
{
int currentIndex = 0;
int nextIndex;
while (currentIndex < styleString.Length)
{
// ---- SizeType Parsing -----------------
nextIndex = currentIndex;
while (char.IsLetter(styleString[nextIndex]))
{
nextIndex++;
}
SizeType type = (SizeType)Enum.Parse(sizeTypeType, styleString.AsSpan(currentIndex, nextIndex - currentIndex), true);
// ----- Float Parsing --------------
// Find the next Digit (start of the float)
while (!char.IsDigit(styleString[nextIndex]))
{
nextIndex++;
}
// Append digits left of the decimal delimiter(s)
StringBuilder floatStringBuilder = new();
while ((nextIndex < styleString.Length) && (char.IsDigit(styleString[nextIndex])))
{
floatStringBuilder.Append(styleString[nextIndex]);
nextIndex++;
}
// Append culture invariant delimiter
floatStringBuilder.Append('.');
// Append digits right of the decimal delimiter(s)
while ((nextIndex < styleString.Length) && (!char.IsLetter(styleString[nextIndex])))
{
if (char.IsDigit(styleString[nextIndex]))
{
floatStringBuilder.Append(styleString[nextIndex]);
}
nextIndex++;
}
string floatString = floatStringBuilder.ToString();
if (!float.TryParse(floatString, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out float width))
{
width = 0F;
}
// Add new Column/Row Style
if (columns)
{
settings.ColumnStyles.Add(new ColumnStyle(type, width));
}
else
{
settings.RowStyles.Add(new RowStyle(type, width));
}
// Go to the next Column/Row Style
currentIndex = nextIndex;
}
}
}
}
}
|