// (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));
}
}
}
}
}