// (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using Avalonia.Utilities; using System; using System.ComponentModel; using System.Globalization; using Avalonia.Controls.Utils; namespace Avalonia.Controls { public enum DataGridLengthUnitType { Auto = 0, Pixel = 1, SizeToCells = 2, SizeToHeader = 3, Star = 4 } /// /// Represents the lengths of elements within the control. /// [TypeConverter(typeof(DataGridLengthConverter))] public struct DataGridLength : IEquatable { private double _desiredValue; // desired value storage private double _displayValue; // display value storage private double _unitValue; // unit value storage private DataGridLengthUnitType _unitType; // unit type storage // static instances of value invariant DataGridLengths private static readonly DataGridLength _auto = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.Auto); private static readonly DataGridLength _sizeToCells = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToCells); private static readonly DataGridLength _sizeToHeader = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToHeader); // WPF uses 1.0 as the default value as well internal const double DATAGRIDLENGTH_DefaultValue = 1.0; /// /// Initializes a new instance of the class. /// /// public DataGridLength(double value) : this(value, DataGridLengthUnitType.Pixel) { } /// /// Initializes to a specified value and unit. /// /// The value to hold. /// The unit of value. /// /// value is ignored unless type is /// DataGridLengthUnitType.Pixel or /// DataGridLengthUnitType.Star /// /// /// If value parameter is double.NaN /// or value parameter is double.NegativeInfinity /// or value parameter is double.PositiveInfinity. /// public DataGridLength(double value, DataGridLengthUnitType type) : this(value, type, (type == DataGridLengthUnitType.Pixel ? value : Double.NaN), (type == DataGridLengthUnitType.Pixel ? value : Double.NaN)) { } /// /// Initializes to a specified value and unit. /// /// The value to hold. /// The unit of value. /// /// /// /// value is ignored unless type is /// DataGridLengthUnitType.Pixel or /// DataGridLengthUnitType.Star /// /// /// If value parameter is double.NaN /// or value parameter is double.NegativeInfinity /// or value parameter is double.PositiveInfinity. /// public DataGridLength(double value, DataGridLengthUnitType type, double desiredValue, double displayValue) { if (double.IsNaN(value)) { throw DataGridError.DataGrid.ValueCannotBeSetToNAN("value"); } if (double.IsInfinity(value)) { throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("value"); } if (double.IsInfinity(desiredValue)) { throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("desiredValue"); } if (double.IsInfinity(displayValue)) { throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("displayValue"); } if (value < 0) { throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "value", 0); } if (desiredValue < 0) { throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("desiredValue", "desiredValue", 0); } if (displayValue < 0) { throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("displayValue", "displayValue", 0); } if (type != DataGridLengthUnitType.Auto && type != DataGridLengthUnitType.SizeToCells && type != DataGridLengthUnitType.SizeToHeader && type != DataGridLengthUnitType.Star && type != DataGridLengthUnitType.Pixel) { throw DataGridError.DataGridLength.InvalidUnitType("type"); } _desiredValue = desiredValue; _displayValue = displayValue; _unitValue = (type == DataGridLengthUnitType.Auto) ? DATAGRIDLENGTH_DefaultValue : value; _unitType = type; } /// /// Gets a structure that represents the standard automatic sizing mode. /// /// /// A structure that represents the standard automatic sizing mode. /// public static DataGridLength Auto { get { return _auto; } } /// /// Returns the desired value of this instance. /// public double DesiredValue { get { return _desiredValue; } } /// /// Returns the display value of this instance. /// public double DisplayValue { get { return _displayValue; } } /// /// Returns true if this DataGridLength instance holds /// an absolute (pixel) value. /// public bool IsAbsolute { get { return _unitType == DataGridLengthUnitType.Pixel; } } /// /// Returns true if this DataGridLength instance is /// automatic (not specified). /// public bool IsAuto { get { return _unitType == DataGridLengthUnitType.Auto; } } /// /// Returns true if this instance is to size to the cells of a column or row. /// public bool IsSizeToCells { get { return _unitType == DataGridLengthUnitType.SizeToCells; } } /// /// Returns true if this instance is to size to the header of a column or row. /// public bool IsSizeToHeader { get { return _unitType == DataGridLengthUnitType.SizeToHeader; } } /// /// Returns true if this DataGridLength instance holds a weighted proportion /// of available space. /// public bool IsStar { get { return _unitType == DataGridLengthUnitType.Star; } } /// /// Gets a structure that represents the cell-based automatic sizing mode. /// /// /// A structure that represents the cell-based automatic sizing mode. /// public static DataGridLength SizeToCells { get { return _sizeToCells; } } /// /// Gets a structure that represents the header-based automatic sizing mode. /// /// /// A structure that represents the header-based automatic sizing mode. /// public static DataGridLength SizeToHeader { get { return _sizeToHeader; } } /// /// Gets the that represents the current sizing mode. /// public DataGridLengthUnitType UnitType { get { return _unitType; } } /// /// Gets the absolute value of the in pixels. /// /// /// The absolute value of the in pixels. /// public double Value { get { return _unitValue; } } /// /// Overloaded operator, compares 2 DataGridLength's. /// /// first DataGridLength to compare. /// second DataGridLength to compare. /// true if specified DataGridLength have same value, /// unit type, desired value, and display value. public static bool operator ==(DataGridLength gl1, DataGridLength gl2) { return (gl1.UnitType == gl2.UnitType && gl1.Value == gl2.Value && gl1.DesiredValue == gl2.DesiredValue && gl1.DisplayValue == gl2.DisplayValue); } /// /// Overloaded operator, compares 2 DataGridLength's. /// /// first DataGridLength to compare. /// second DataGridLength to compare. /// true if specified DataGridLength have either different value, /// unit type, desired value, or display value. public static bool operator !=(DataGridLength gl1, DataGridLength gl2) { return (gl1.UnitType != gl2.UnitType || gl1.Value != gl2.Value || gl1.DesiredValue != gl2.DesiredValue || gl1.DisplayValue != gl2.DisplayValue); } /// /// Compares this instance of DataGridLength with another instance. /// /// DataGridLength length instance to compare. /// true if this DataGridLength instance has the same value /// and unit type as gridLength. public bool Equals(DataGridLength other) { return (this == other); } /// /// Compares this instance of DataGridLength with another object. /// /// Reference to an object for comparison. /// true if this DataGridLength instance has the same value /// and unit type as oCompare. public override bool Equals(object obj) { DataGridLength? dataGridLength = obj as DataGridLength?; if (dataGridLength.HasValue) { return (this == dataGridLength); } return false; } /// /// Returns a unique hash code for this DataGridLength /// /// hash code public override int GetHashCode() { return ((int)_unitValue + (int)_unitType) + (int)_desiredValue + (int)_displayValue; } } /// /// DataGridLengthConverter - Converter class for converting instances of other types to and from DataGridLength instances. /// public class DataGridLengthConverter : TypeConverter { private static string _starSuffix = "*"; private static string[] _valueInvariantUnitStrings = { "auto", "sizetocells", "sizetoheader" }; private static DataGridLength[] _valueInvariantDataGridLengths = { DataGridLength.Auto, DataGridLength.SizeToCells, DataGridLength.SizeToHeader }; /// /// Checks whether or not this class can convert from a given type. /// /// /// An ITypeDescriptorContext that provides a format context. /// /// The Type being queried for support. /// /// true if this converter can convert from the provided type, /// false otherwise. /// public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { // We can only handle strings, integral and floating types TypeCode tc = Type.GetTypeCode(sourceType); switch (tc) { case TypeCode.String: case TypeCode.Decimal: case TypeCode.Single: case TypeCode.Double: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: return true; default: return false; } } /// /// Checks whether or not this class can convert to a given type. /// /// /// An ITypeDescriptorContext that provides a format context. /// /// The Type being queried for support. /// /// true if this converter can convert to the provided type, /// false otherwise. /// public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(string); } /// /// Attempts to convert to a DataGridLength from the given object. /// /// /// An ITypeDescriptorContext that provides a format context. /// /// /// The CultureInfo to use for the conversion. /// /// The object to convert to a GridLength. /// /// The GridLength instance which was constructed. /// /// /// A NotSupportedException is thrown if the example object is null. /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { // GridLengthConverter in WPF throws a NotSupportedException on a null value as well. if (value == null) { throw DataGridError.DataGridLengthConverter.CannotConvertFrom("(null)"); } if (value is string stringValue) { stringValue = stringValue.Trim(); if (stringValue.EndsWith(_starSuffix, StringComparison.Ordinal)) { string stringValueWithoutSuffix = stringValue.Substring(0, stringValue.Length - _starSuffix.Length); double starWeight; if (string.IsNullOrEmpty(stringValueWithoutSuffix)) { starWeight = 1; } else { starWeight = Convert.ToDouble(stringValueWithoutSuffix, culture ?? CultureInfo.CurrentCulture); } return new DataGridLength(starWeight, DataGridLengthUnitType.Star); } for (int index = 0; index < _valueInvariantUnitStrings.Length; index++) { if (stringValue.Equals(_valueInvariantUnitStrings[index], StringComparison.OrdinalIgnoreCase)) { return _valueInvariantDataGridLengths[index]; } } } // Conversion from numeric type, WPF lets Convert exceptions bubble out here as well double doubleValue = Convert.ToDouble(value, culture ?? CultureInfo.CurrentCulture); if (double.IsNaN(doubleValue)) { // WPF returns Auto in this case as well return DataGridLength.Auto; } else { return new DataGridLength(doubleValue); } } /// /// Attempts to convert a DataGridLength instance to the given type. /// /// /// An ITypeDescriptorContext that provides a format context. /// /// /// The CultureInfo to use for the conversion. /// /// The DataGridLength to convert. /// The type to which to convert the DataGridLength instance. /// /// The object which was constructed. /// /// /// An ArgumentNullException is thrown if the example object is null. /// /// /// A NotSupportedException is thrown if the object is not null and is not a DataGridLength, /// or if the destinationType isn't one of the valid destination types. /// /// /// Critical: calls InstanceDescriptor ctor which LinkDemands /// PublicOK: can only make an InstanceDescriptor for DataGridLength, not an arbitrary class /// public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == null) { throw new ArgumentNullException("destinationType"); } if (destinationType != typeof(string)) { throw DataGridError.DataGridLengthConverter.CannotConvertTo(destinationType.ToString()); } DataGridLength? dataGridLength = value as DataGridLength?; if (!dataGridLength.HasValue) { throw DataGridError.DataGridLengthConverter.InvalidDataGridLength("value"); } else { // Convert dataGridLength to a string switch (dataGridLength.Value.UnitType) { // for Auto print out "Auto". value is always "1.0" case DataGridLengthUnitType.Auto: return "Auto"; case DataGridLengthUnitType.SizeToHeader: return "SizeToHeader"; case DataGridLengthUnitType.SizeToCells: return "SizeToCells"; // Star has one special case when value is "1.0". // in this case drop value part and print only "Star" case DataGridLengthUnitType.Star: return ( DoubleUtil.AreClose(1.0, dataGridLength.Value.Value) ? _starSuffix : Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture) + DataGridLengthConverter._starSuffix); default: return (Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture)); } } } } }