DataGridLength.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. // (c) Copyright Microsoft Corporation.
  2. // This source is subject to the Microsoft Public License (Ms-PL).
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  4. // All other rights reserved.
  5. using Avalonia.Utilities;
  6. using System;
  7. using System.ComponentModel;
  8. using System.Globalization;
  9. using Avalonia.Controls.Utils;
  10. namespace Avalonia.Controls
  11. {
  12. public enum DataGridLengthUnitType
  13. {
  14. Auto = 0,
  15. Pixel = 1,
  16. SizeToCells = 2,
  17. SizeToHeader = 3,
  18. Star = 4
  19. }
  20. /// <summary>
  21. /// Represents the lengths of elements within the <see cref="T:Avalonia.Controls.DataGrid" /> control.
  22. /// </summary>
  23. [TypeConverter(typeof(DataGridLengthConverter))]
  24. public struct DataGridLength : IEquatable<DataGridLength>
  25. {
  26. private double _desiredValue; // desired value storage
  27. private double _displayValue; // display value storage
  28. private double _unitValue; // unit value storage
  29. private DataGridLengthUnitType _unitType; // unit type storage
  30. // static instances of value invariant DataGridLengths
  31. private static readonly DataGridLength _auto = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.Auto);
  32. private static readonly DataGridLength _sizeToCells = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToCells);
  33. private static readonly DataGridLength _sizeToHeader = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToHeader);
  34. // WPF uses 1.0 as the default value as well
  35. internal const double DATAGRIDLENGTH_DefaultValue = 1.0;
  36. /// <summary>
  37. /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridLength" /> class.
  38. /// </summary>
  39. /// <param name="value"></param>
  40. public DataGridLength(double value)
  41. : this(value, DataGridLengthUnitType.Pixel)
  42. {
  43. }
  44. /// <summary>
  45. /// Initializes to a specified value and unit.
  46. /// </summary>
  47. /// <param name="value">The value to hold.</param>
  48. /// <param name="type">The unit of <c>value</c>.</param>
  49. /// <remarks>
  50. /// <c>value</c> is ignored unless <c>type</c> is
  51. /// <c>DataGridLengthUnitType.Pixel</c> or
  52. /// <c>DataGridLengthUnitType.Star</c>
  53. /// </remarks>
  54. /// <exception cref="ArgumentException">
  55. /// If <c>value</c> parameter is <c>double.NaN</c>
  56. /// or <c>value</c> parameter is <c>double.NegativeInfinity</c>
  57. /// or <c>value</c> parameter is <c>double.PositiveInfinity</c>.
  58. /// </exception>
  59. public DataGridLength(double value, DataGridLengthUnitType type)
  60. : this(value, type, (type == DataGridLengthUnitType.Pixel ? value : Double.NaN), (type == DataGridLengthUnitType.Pixel ? value : Double.NaN))
  61. {
  62. }
  63. /// <summary>
  64. /// Initializes to a specified value and unit.
  65. /// </summary>
  66. /// <param name="value">The value to hold.</param>
  67. /// <param name="type">The unit of <c>value</c>.</param>
  68. /// <param name="desiredValue"></param>
  69. /// <param name="displayValue"></param>
  70. /// <remarks>
  71. /// <c>value</c> is ignored unless <c>type</c> is
  72. /// <c>DataGridLengthUnitType.Pixel</c> or
  73. /// <c>DataGridLengthUnitType.Star</c>
  74. /// </remarks>
  75. /// <exception cref="ArgumentException">
  76. /// If <c>value</c> parameter is <c>double.NaN</c>
  77. /// or <c>value</c> parameter is <c>double.NegativeInfinity</c>
  78. /// or <c>value</c> parameter is <c>double.PositiveInfinity</c>.
  79. /// </exception>
  80. public DataGridLength(double value, DataGridLengthUnitType type, double desiredValue, double displayValue)
  81. {
  82. if (double.IsNaN(value))
  83. {
  84. throw DataGridError.DataGrid.ValueCannotBeSetToNAN("value");
  85. }
  86. if (double.IsInfinity(value))
  87. {
  88. throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("value");
  89. }
  90. if (double.IsInfinity(desiredValue))
  91. {
  92. throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("desiredValue");
  93. }
  94. if (double.IsInfinity(displayValue))
  95. {
  96. throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("displayValue");
  97. }
  98. if (value < 0)
  99. {
  100. throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "value", 0);
  101. }
  102. if (desiredValue < 0)
  103. {
  104. throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("desiredValue", "desiredValue", 0);
  105. }
  106. if (displayValue < 0)
  107. {
  108. throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("displayValue", "displayValue", 0);
  109. }
  110. if (type != DataGridLengthUnitType.Auto &&
  111. type != DataGridLengthUnitType.SizeToCells &&
  112. type != DataGridLengthUnitType.SizeToHeader &&
  113. type != DataGridLengthUnitType.Star &&
  114. type != DataGridLengthUnitType.Pixel)
  115. {
  116. throw DataGridError.DataGridLength.InvalidUnitType("type");
  117. }
  118. _desiredValue = desiredValue;
  119. _displayValue = displayValue;
  120. _unitValue = (type == DataGridLengthUnitType.Auto) ? DATAGRIDLENGTH_DefaultValue : value;
  121. _unitType = type;
  122. }
  123. /// <summary>
  124. /// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the standard automatic sizing mode.
  125. /// </summary>
  126. /// <returns>
  127. /// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the standard automatic sizing mode.
  128. /// </returns>
  129. public static DataGridLength Auto
  130. {
  131. get
  132. {
  133. return _auto;
  134. }
  135. }
  136. /// <summary>
  137. /// Returns the desired value of this instance.
  138. /// </summary>
  139. public double DesiredValue
  140. {
  141. get
  142. {
  143. return _desiredValue;
  144. }
  145. }
  146. /// <summary>
  147. /// Returns the display value of this instance.
  148. /// </summary>
  149. public double DisplayValue
  150. {
  151. get
  152. {
  153. return _displayValue;
  154. }
  155. }
  156. /// <summary>
  157. /// Returns <c>true</c> if this DataGridLength instance holds
  158. /// an absolute (pixel) value.
  159. /// </summary>
  160. public bool IsAbsolute
  161. {
  162. get
  163. {
  164. return _unitType == DataGridLengthUnitType.Pixel;
  165. }
  166. }
  167. /// <summary>
  168. /// Returns <c>true</c> if this DataGridLength instance is
  169. /// automatic (not specified).
  170. /// </summary>
  171. public bool IsAuto
  172. {
  173. get
  174. {
  175. return _unitType == DataGridLengthUnitType.Auto;
  176. }
  177. }
  178. /// <summary>
  179. /// Returns <c>true</c> if this instance is to size to the cells of a column or row.
  180. /// </summary>
  181. public bool IsSizeToCells
  182. {
  183. get
  184. {
  185. return _unitType == DataGridLengthUnitType.SizeToCells;
  186. }
  187. }
  188. /// <summary>
  189. /// Returns <c>true</c> if this instance is to size to the header of a column or row.
  190. /// </summary>
  191. public bool IsSizeToHeader
  192. {
  193. get
  194. {
  195. return _unitType == DataGridLengthUnitType.SizeToHeader;
  196. }
  197. }
  198. /// <summary>
  199. /// Returns <c>true</c> if this DataGridLength instance holds a weighted proportion
  200. /// of available space.
  201. /// </summary>
  202. public bool IsStar
  203. {
  204. get
  205. {
  206. return _unitType == DataGridLengthUnitType.Star;
  207. }
  208. }
  209. /// <summary>
  210. /// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the cell-based automatic sizing mode.
  211. /// </summary>
  212. /// <returns>
  213. /// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the cell-based automatic sizing mode.
  214. /// </returns>
  215. public static DataGridLength SizeToCells
  216. {
  217. get
  218. {
  219. return _sizeToCells;
  220. }
  221. }
  222. /// <summary>
  223. /// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the header-based automatic sizing mode.
  224. /// </summary>
  225. /// <returns>
  226. /// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the header-based automatic sizing mode.
  227. /// </returns>
  228. public static DataGridLength SizeToHeader
  229. {
  230. get
  231. {
  232. return _sizeToHeader;
  233. }
  234. }
  235. /// <summary>
  236. /// Gets the <see cref="T:Avalonia.Controls.DataGridLengthUnitType" /> that represents the current sizing mode.
  237. /// </summary>
  238. public DataGridLengthUnitType UnitType
  239. {
  240. get
  241. {
  242. return _unitType;
  243. }
  244. }
  245. /// <summary>
  246. /// Gets the absolute value of the <see cref="T:Avalonia.Controls.DataGridLength" /> in pixels.
  247. /// </summary>
  248. /// <returns>
  249. /// The absolute value of the <see cref="T:Avalonia.Controls.DataGridLength" /> in pixels.
  250. /// </returns>
  251. public double Value
  252. {
  253. get
  254. {
  255. return _unitValue;
  256. }
  257. }
  258. /// <summary>
  259. /// Overloaded operator, compares 2 DataGridLength's.
  260. /// </summary>
  261. /// <param name="gl1">first DataGridLength to compare.</param>
  262. /// <param name="gl2">second DataGridLength to compare.</param>
  263. /// <returns>true if specified DataGridLength have same value,
  264. /// unit type, desired value, and display value.</returns>
  265. public static bool operator ==(DataGridLength gl1, DataGridLength gl2)
  266. {
  267. return (gl1.UnitType == gl2.UnitType
  268. && gl1.Value == gl2.Value
  269. && gl1.DesiredValue == gl2.DesiredValue
  270. && gl1.DisplayValue == gl2.DisplayValue);
  271. }
  272. /// <summary>
  273. /// Overloaded operator, compares 2 DataGridLength's.
  274. /// </summary>
  275. /// <param name="gl1">first DataGridLength to compare.</param>
  276. /// <param name="gl2">second DataGridLength to compare.</param>
  277. /// <returns>true if specified DataGridLength have either different value,
  278. /// unit type, desired value, or display value.</returns>
  279. public static bool operator !=(DataGridLength gl1, DataGridLength gl2)
  280. {
  281. return (gl1.UnitType != gl2.UnitType
  282. || gl1.Value != gl2.Value
  283. || gl1.DesiredValue != gl2.DesiredValue
  284. || gl1.DisplayValue != gl2.DisplayValue);
  285. }
  286. /// <summary>
  287. /// Compares this instance of DataGridLength with another instance.
  288. /// </summary>
  289. /// <param name="other">DataGridLength length instance to compare.</param>
  290. /// <returns><c>true</c> if this DataGridLength instance has the same value
  291. /// and unit type as gridLength.</returns>
  292. public bool Equals(DataGridLength other)
  293. {
  294. return (this == other);
  295. }
  296. /// <summary>
  297. /// Compares this instance of DataGridLength with another object.
  298. /// </summary>
  299. /// <param name="obj">Reference to an object for comparison.</param>
  300. /// <returns><c>true</c> if this DataGridLength instance has the same value
  301. /// and unit type as oCompare.</returns>
  302. public override bool Equals(object obj)
  303. {
  304. DataGridLength? dataGridLength = obj as DataGridLength?;
  305. if (dataGridLength.HasValue)
  306. {
  307. return (this == dataGridLength);
  308. }
  309. return false;
  310. }
  311. /// <summary>
  312. /// Returns a unique hash code for this DataGridLength
  313. /// </summary>
  314. /// <returns>hash code</returns>
  315. public override int GetHashCode()
  316. {
  317. return ((int)_unitValue + (int)_unitType) + (int)_desiredValue + (int)_displayValue;
  318. }
  319. }
  320. /// <summary>
  321. /// DataGridLengthConverter - Converter class for converting instances of other types to and from DataGridLength instances.
  322. /// </summary>
  323. public class DataGridLengthConverter : TypeConverter
  324. {
  325. private static string _starSuffix = "*";
  326. private static string[] _valueInvariantUnitStrings = { "auto", "sizetocells", "sizetoheader" };
  327. private static DataGridLength[] _valueInvariantDataGridLengths = { DataGridLength.Auto, DataGridLength.SizeToCells, DataGridLength.SizeToHeader };
  328. /// <summary>
  329. /// Checks whether or not this class can convert from a given type.
  330. /// </summary>
  331. /// <param name="context">
  332. /// An ITypeDescriptorContext that provides a format context.
  333. /// </param>
  334. /// <param name="sourceType">The Type being queried for support.</param>
  335. /// <returns>
  336. /// <c>true</c> if this converter can convert from the provided type,
  337. /// <c>false</c> otherwise.
  338. /// </returns>
  339. public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  340. {
  341. // We can only handle strings, integral and floating types
  342. TypeCode tc = Type.GetTypeCode(sourceType);
  343. switch (tc)
  344. {
  345. case TypeCode.String:
  346. case TypeCode.Decimal:
  347. case TypeCode.Single:
  348. case TypeCode.Double:
  349. case TypeCode.Int16:
  350. case TypeCode.Int32:
  351. case TypeCode.Int64:
  352. case TypeCode.UInt16:
  353. case TypeCode.UInt32:
  354. case TypeCode.UInt64:
  355. return true;
  356. default:
  357. return false;
  358. }
  359. }
  360. /// <summary>
  361. /// Checks whether or not this class can convert to a given type.
  362. /// </summary>
  363. /// <param name="context">
  364. /// An ITypeDescriptorContext that provides a format context.
  365. /// </param>
  366. /// <param name="destinationType">The Type being queried for support.</param>
  367. /// <returns>
  368. /// <c>true</c> if this converter can convert to the provided type,
  369. /// <c>false</c> otherwise.
  370. /// </returns>
  371. public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
  372. {
  373. return destinationType == typeof(string);
  374. }
  375. /// <summary>
  376. /// Attempts to convert to a DataGridLength from the given object.
  377. /// </summary>
  378. /// <param name="context">
  379. /// An ITypeDescriptorContext that provides a format context.
  380. /// </param>
  381. /// <param name="culture">
  382. /// The CultureInfo to use for the conversion.
  383. /// </param>
  384. /// <param name="value">The object to convert to a GridLength.</param>
  385. /// <returns>
  386. /// The GridLength instance which was constructed.
  387. /// </returns>
  388. /// <exception cref="NotSupportedException">
  389. /// A NotSupportedException is thrown if the example object is null.
  390. /// </exception>
  391. public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
  392. {
  393. // GridLengthConverter in WPF throws a NotSupportedException on a null value as well.
  394. if (value == null)
  395. {
  396. throw DataGridError.DataGridLengthConverter.CannotConvertFrom("(null)");
  397. }
  398. if (value is string stringValue)
  399. {
  400. stringValue = stringValue.Trim();
  401. if (stringValue.EndsWith(_starSuffix, StringComparison.Ordinal))
  402. {
  403. string stringValueWithoutSuffix = stringValue.Substring(0, stringValue.Length - _starSuffix.Length);
  404. double starWeight;
  405. if (string.IsNullOrEmpty(stringValueWithoutSuffix))
  406. {
  407. starWeight = 1;
  408. }
  409. else
  410. {
  411. starWeight = Convert.ToDouble(stringValueWithoutSuffix, culture ?? CultureInfo.CurrentCulture);
  412. }
  413. return new DataGridLength(starWeight, DataGridLengthUnitType.Star);
  414. }
  415. for (int index = 0; index < _valueInvariantUnitStrings.Length; index++)
  416. {
  417. if (stringValue.Equals(_valueInvariantUnitStrings[index], StringComparison.OrdinalIgnoreCase))
  418. {
  419. return _valueInvariantDataGridLengths[index];
  420. }
  421. }
  422. }
  423. // Conversion from numeric type, WPF lets Convert exceptions bubble out here as well
  424. double doubleValue = Convert.ToDouble(value, culture ?? CultureInfo.CurrentCulture);
  425. if (double.IsNaN(doubleValue))
  426. {
  427. // WPF returns Auto in this case as well
  428. return DataGridLength.Auto;
  429. }
  430. else
  431. {
  432. return new DataGridLength(doubleValue);
  433. }
  434. }
  435. /// <summary>
  436. /// Attempts to convert a DataGridLength instance to the given type.
  437. /// </summary>
  438. /// <param name="context">
  439. /// An ITypeDescriptorContext that provides a format context.
  440. /// </param>
  441. /// <param name="culture">
  442. /// The CultureInfo to use for the conversion.
  443. /// </param>
  444. /// <param name="value">The DataGridLength to convert.</param>
  445. /// <param name="destinationType">The type to which to convert the DataGridLength instance.</param>
  446. /// <returns>
  447. /// The object which was constructed.
  448. /// </returns>
  449. /// <exception cref="ArgumentNullException">
  450. /// An ArgumentNullException is thrown if the example object is null.
  451. /// </exception>
  452. /// <exception cref="NotSupportedException">
  453. /// A NotSupportedException is thrown if the object is not null and is not a DataGridLength,
  454. /// or if the destinationType isn't one of the valid destination types.
  455. /// </exception>
  456. ///<SecurityNote>
  457. /// Critical: calls InstanceDescriptor ctor which LinkDemands
  458. /// PublicOK: can only make an InstanceDescriptor for DataGridLength, not an arbitrary class
  459. ///</SecurityNote>
  460. public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
  461. {
  462. if (destinationType == null)
  463. {
  464. throw new ArgumentNullException("destinationType");
  465. }
  466. if (destinationType != typeof(string))
  467. {
  468. throw DataGridError.DataGridLengthConverter.CannotConvertTo(destinationType.ToString());
  469. }
  470. DataGridLength? dataGridLength = value as DataGridLength?;
  471. if (!dataGridLength.HasValue)
  472. {
  473. throw DataGridError.DataGridLengthConverter.InvalidDataGridLength("value");
  474. }
  475. else
  476. {
  477. // Convert dataGridLength to a string
  478. switch (dataGridLength.Value.UnitType)
  479. {
  480. // for Auto print out "Auto". value is always "1.0"
  481. case DataGridLengthUnitType.Auto:
  482. return "Auto";
  483. case DataGridLengthUnitType.SizeToHeader:
  484. return "SizeToHeader";
  485. case DataGridLengthUnitType.SizeToCells:
  486. return "SizeToCells";
  487. // Star has one special case when value is "1.0".
  488. // in this case drop value part and print only "Star"
  489. case DataGridLengthUnitType.Star:
  490. return (
  491. DoubleUtil.AreClose(1.0, dataGridLength.Value.Value)
  492. ? _starSuffix
  493. : Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture) + DataGridLengthConverter._starSuffix);
  494. default:
  495. return (Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture));
  496. }
  497. }
  498. }
  499. }
  500. }