DataGridColumn.cs 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  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 System;
  6. using System.ComponentModel;
  7. using System.Diagnostics;
  8. using System.Linq;
  9. using Avalonia.Collections;
  10. using Avalonia.Controls.Templates;
  11. using Avalonia.Controls.Utils;
  12. using Avalonia.Data;
  13. using Avalonia.Input;
  14. using Avalonia.Interactivity;
  15. using Avalonia.Layout;
  16. using Avalonia.Markup.Xaml.MarkupExtensions;
  17. using Avalonia.Styling;
  18. using Avalonia.VisualTree;
  19. namespace Avalonia.Controls
  20. {
  21. public abstract class DataGridColumn : AvaloniaObject
  22. {
  23. internal const int DATAGRIDCOLUMN_maximumWidth = 65536;
  24. private const bool DATAGRIDCOLUMN_defaultIsReadOnly = false;
  25. private bool? _isReadOnly;
  26. private double? _maxWidth;
  27. private double? _minWidth;
  28. private bool _settingWidthInternally;
  29. private int _displayIndexWithFiller;
  30. private object _header;
  31. private IDataTemplate _headerTemplate;
  32. private DataGridColumnHeader _headerCell;
  33. private Control _editingElement;
  34. private ICellEditBinding _editBinding;
  35. private IBinding _clipboardContentBinding;
  36. private ControlTheme _cellTheme;
  37. private Classes _cellStyleClasses;
  38. private bool _setWidthInternalNoCallback;
  39. /// <summary>
  40. /// Occurs when the pointer is pressed over the column's header
  41. /// </summary>
  42. public event EventHandler<PointerPressedEventArgs> HeaderPointerPressed;
  43. /// <summary>
  44. /// Occurs when the pointer is released over the column's header
  45. /// </summary>
  46. public event EventHandler<PointerReleasedEventArgs> HeaderPointerReleased;
  47. /// <summary>
  48. /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumn" /> class.
  49. /// </summary>
  50. protected internal DataGridColumn()
  51. {
  52. _displayIndexWithFiller = -1;
  53. IsInitialDesiredWidthDetermined = false;
  54. InheritsWidth = true;
  55. }
  56. /// <summary>
  57. /// Gets the <see cref="T:Avalonia.Controls.DataGrid"/> control that contains this column.
  58. /// </summary>
  59. protected internal DataGrid OwningGrid
  60. {
  61. get;
  62. internal set;
  63. }
  64. internal int Index
  65. {
  66. get;
  67. set;
  68. }
  69. internal bool? CanUserReorderInternal
  70. {
  71. get;
  72. set;
  73. }
  74. internal bool? CanUserResizeInternal
  75. {
  76. get;
  77. set;
  78. }
  79. internal bool? CanUserSortInternal
  80. {
  81. get;
  82. set;
  83. }
  84. internal bool ActualCanUserResize
  85. {
  86. get
  87. {
  88. if (OwningGrid == null || OwningGrid.CanUserResizeColumns == false || this is DataGridFillerColumn)
  89. {
  90. return false;
  91. }
  92. return CanUserResizeInternal ?? true;
  93. }
  94. }
  95. // MaxWidth from local setting or DataGrid setting
  96. internal double ActualMaxWidth
  97. {
  98. get
  99. {
  100. return _maxWidth ?? OwningGrid?.MaxColumnWidth ?? double.PositiveInfinity;
  101. }
  102. }
  103. // MinWidth from local setting or DataGrid setting
  104. internal double ActualMinWidth
  105. {
  106. get
  107. {
  108. double minWidth = _minWidth ?? OwningGrid?.MinColumnWidth ?? 0;
  109. if (Width.IsStar)
  110. {
  111. return Math.Max(DataGrid.DATAGRID_minimumStarColumnWidth, minWidth);
  112. }
  113. return minWidth;
  114. }
  115. }
  116. internal bool DisplayIndexHasChanged
  117. {
  118. get;
  119. set;
  120. }
  121. internal int DisplayIndexWithFiller
  122. {
  123. get { return _displayIndexWithFiller; }
  124. set { _displayIndexWithFiller = value; }
  125. }
  126. internal bool HasHeaderCell
  127. {
  128. get
  129. {
  130. return _headerCell != null;
  131. }
  132. }
  133. internal DataGridColumnHeader HeaderCell
  134. {
  135. get
  136. {
  137. if (_headerCell == null)
  138. {
  139. _headerCell = CreateHeader();
  140. }
  141. return _headerCell;
  142. }
  143. }
  144. /// <summary>
  145. /// Tracks whether or not this column inherits its Width value from the DataGrid.
  146. /// </summary>
  147. internal bool InheritsWidth
  148. {
  149. get;
  150. private set;
  151. }
  152. /// <summary>
  153. /// When a column is initially added, we won't know its initial desired value
  154. /// until all rows have been measured. We use this variable to track whether or
  155. /// not the column has been fully measured.
  156. /// </summary>
  157. internal bool IsInitialDesiredWidthDetermined
  158. {
  159. get;
  160. set;
  161. }
  162. internal double LayoutRoundedWidth
  163. {
  164. get;
  165. private set;
  166. }
  167. internal ICellEditBinding CellEditBinding
  168. {
  169. get => _editBinding;
  170. }
  171. /// <summary>
  172. /// Defines the <see cref="IsVisible"/> property.
  173. /// </summary>
  174. public static readonly StyledProperty<bool> IsVisibleProperty =
  175. Control.IsVisibleProperty.AddOwner<DataGridColumn>();
  176. /// <summary>
  177. /// Determines whether or not this column is visible.
  178. /// </summary>
  179. public bool IsVisible
  180. {
  181. get => GetValue(IsVisibleProperty);
  182. set => SetValue(IsVisibleProperty, value);
  183. }
  184. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
  185. {
  186. base.OnPropertyChanged(change);
  187. if (change.Property == IsVisibleProperty)
  188. {
  189. OwningGrid?.OnColumnVisibleStateChanging(this);
  190. var isVisible = change.GetNewValue<bool>();
  191. if (_headerCell != null)
  192. {
  193. _headerCell.IsVisible = isVisible;
  194. }
  195. OwningGrid?.OnColumnVisibleStateChanged(this);
  196. NotifyPropertyChanged(change.Property.Name);
  197. }
  198. else if (change.Property == WidthProperty)
  199. {
  200. if (!_settingWidthInternally)
  201. {
  202. InheritsWidth = false;
  203. }
  204. if (_setWidthInternalNoCallback == false)
  205. {
  206. var grid = OwningGrid;
  207. var width = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).NewValue.Value;
  208. if (grid != null)
  209. {
  210. var oldWidth = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).OldValue.Value;
  211. if (width.IsStar != oldWidth.IsStar)
  212. {
  213. SetWidthInternalNoCallback(width);
  214. IsInitialDesiredWidthDetermined = false;
  215. grid.OnColumnWidthChanged(this);
  216. }
  217. else
  218. {
  219. Resize(oldWidth, width, false);
  220. }
  221. }
  222. else
  223. {
  224. SetWidthInternalNoCallback(width);
  225. }
  226. }
  227. }
  228. }
  229. /// <summary>
  230. /// Actual visible width after Width, MinWidth, and MaxWidth setting at the Column level and DataGrid level
  231. /// have been taken into account
  232. /// </summary>
  233. public double ActualWidth
  234. {
  235. get
  236. {
  237. if (OwningGrid == null || double.IsNaN(Width.DisplayValue))
  238. {
  239. return ActualMinWidth;
  240. }
  241. return Width.DisplayValue;
  242. }
  243. }
  244. /// <summary>
  245. /// Gets or sets a value that indicates whether the user can change the column display position by
  246. /// dragging the column header.
  247. /// </summary>
  248. /// <returns>
  249. /// true if the user can drag the column header to a new position; otherwise, false. The default is the current <see cref="P:Avalonia.Controls.DataGrid.CanUserReorderColumns" /> property value.
  250. /// </returns>
  251. public bool CanUserReorder
  252. {
  253. get
  254. {
  255. return
  256. CanUserReorderInternal ??
  257. OwningGrid?.CanUserReorderColumns ??
  258. DataGrid.DATAGRID_defaultCanUserResizeColumns;
  259. }
  260. set
  261. {
  262. CanUserReorderInternal = value;
  263. }
  264. }
  265. /// <summary>
  266. /// Gets or sets a value that indicates whether the user can adjust the column width using the mouse.
  267. /// </summary>
  268. /// <returns>
  269. /// true if the user can resize the column; false if the user cannot resize the column. The default is the current <see cref="P:Avalonia.Controls.DataGrid.CanUserResizeColumns" /> property value.
  270. /// </returns>
  271. public bool CanUserResize
  272. {
  273. get
  274. {
  275. return
  276. CanUserResizeInternal ??
  277. OwningGrid?.CanUserResizeColumns ??
  278. DataGrid.DATAGRID_defaultCanUserResizeColumns;
  279. }
  280. set
  281. {
  282. CanUserResizeInternal = value;
  283. OwningGrid?.OnColumnCanUserResizeChanged(this);
  284. }
  285. }
  286. /// <summary>
  287. /// Gets or sets a value that indicates whether the user can sort the column by clicking the column header.
  288. /// </summary>
  289. /// <returns>
  290. /// true if the user can sort the column; false if the user cannot sort the column. The default is the current <see cref="P:Avalonia.Controls.DataGrid.CanUserSortColumns" /> property value.
  291. /// </returns>
  292. public bool CanUserSort
  293. {
  294. get
  295. {
  296. if (CanUserSortInternal.HasValue)
  297. {
  298. return CanUserSortInternal.Value;
  299. }
  300. else if (OwningGrid != null)
  301. {
  302. string propertyPath = GetSortPropertyName();
  303. Type propertyType = OwningGrid.DataConnection.DataType.GetNestedPropertyType(propertyPath);
  304. // if the type is nullable, then we will compare the non-nullable type
  305. if (TypeHelper.IsNullableType(propertyType))
  306. {
  307. propertyType = TypeHelper.GetNonNullableType(propertyType);
  308. }
  309. // return whether or not the property type can be compared
  310. return typeof(IComparable).IsAssignableFrom(propertyType) ? true : false;
  311. }
  312. else
  313. {
  314. return DataGrid.DATAGRID_defaultCanUserSortColumns;
  315. }
  316. }
  317. set
  318. {
  319. CanUserSortInternal = value;
  320. }
  321. }
  322. /// <summary>
  323. /// Gets or sets the display position of the column relative to the other columns in the <see cref="T:Avalonia.Controls.DataGrid" />.
  324. /// </summary>
  325. /// <returns>
  326. /// The zero-based position of the column as it is displayed in the associated <see cref="T:Avalonia.Controls.DataGrid" />. The default is the index of the corresponding <see cref="P:System.Collections.ObjectModel.Collection`1.Item(System.Int32)" /> in the <see cref="P:Avalonia.Controls.DataGrid.Columns" /> collection.
  327. /// </returns>
  328. /// <exception cref="T:System.ArgumentOutOfRangeException">
  329. /// When setting this property, the specified value is less than -1 or equal to <see cref="F:System.Int32.MaxValue" />.
  330. ///
  331. /// -or-
  332. ///
  333. /// When setting this property on a column in a <see cref="T:Avalonia.Controls.DataGrid" />, the specified value is less than zero or greater than or equal to the number of columns in the <see cref="T:Avalonia.Controls.DataGrid" />.
  334. /// </exception>
  335. /// <exception cref="T:System.InvalidOperationException">
  336. /// When setting this property, the <see cref="T:Avalonia.Controls.DataGrid" /> is already making <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" /> adjustments. For example, this exception is thrown when you attempt to set <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" /> in a <see cref="E:Avalonia.Controls.DataGrid.ColumnDisplayIndexChanged" /> event handler.
  337. ///
  338. /// -or-
  339. ///
  340. /// When setting this property, the specified value would result in a frozen column being displayed in the range of unfrozen columns, or an unfrozen column being displayed in the range of frozen columns.
  341. /// </exception>
  342. public int DisplayIndex
  343. {
  344. get
  345. {
  346. if (OwningGrid != null && OwningGrid.ColumnsInternal.RowGroupSpacerColumn.IsRepresented)
  347. {
  348. return _displayIndexWithFiller - 1;
  349. }
  350. else
  351. {
  352. return _displayIndexWithFiller;
  353. }
  354. }
  355. set
  356. {
  357. if (value == Int32.MaxValue)
  358. {
  359. throw DataGridError.DataGrid.ValueMustBeLessThan(nameof(value), nameof(DisplayIndex), Int32.MaxValue);
  360. }
  361. if (OwningGrid != null)
  362. {
  363. if (OwningGrid.ColumnsInternal.RowGroupSpacerColumn.IsRepresented)
  364. {
  365. value++;
  366. }
  367. if (_displayIndexWithFiller != value)
  368. {
  369. if (value < 0 || value >= OwningGrid.ColumnsItemsInternal.Count)
  370. {
  371. throw DataGridError.DataGrid.ValueMustBeBetween(nameof(value), nameof(DisplayIndex), 0, true, OwningGrid.Columns.Count, false);
  372. }
  373. // Will throw an error if a visible frozen column is placed inside a non-frozen area or vice-versa.
  374. OwningGrid.OnColumnDisplayIndexChanging(this, value);
  375. _displayIndexWithFiller = value;
  376. try
  377. {
  378. OwningGrid.InDisplayIndexAdjustments = true;
  379. OwningGrid.OnColumnDisplayIndexChanged(this);
  380. OwningGrid.OnColumnDisplayIndexChanged_PostNotification();
  381. }
  382. finally
  383. {
  384. OwningGrid.InDisplayIndexAdjustments = false;
  385. }
  386. }
  387. }
  388. else
  389. {
  390. if (value < -1)
  391. {
  392. throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(DisplayIndex), -1);
  393. }
  394. _displayIndexWithFiller = value;
  395. }
  396. }
  397. }
  398. public Classes CellStyleClasses => _cellStyleClasses ??= new();
  399. /// <summary>
  400. /// Backing field for CellTheme property.
  401. /// </summary>
  402. public static readonly DirectProperty<DataGridColumn, ControlTheme> CellThemeProperty =
  403. AvaloniaProperty.RegisterDirect<DataGridColumn, ControlTheme>(
  404. nameof(CellTheme),
  405. o => o.CellTheme,
  406. (o, v) => o.CellTheme = v);
  407. /// <summary>
  408. /// Gets or sets the <see cref="DataGridColumnHeader"/> cell theme.
  409. /// </summary>
  410. public ControlTheme CellTheme
  411. {
  412. get { return _cellTheme; }
  413. set { SetAndRaise(CellThemeProperty, ref _cellTheme, value); }
  414. }
  415. /// <summary>
  416. /// Backing field for Header property
  417. /// </summary>
  418. public static readonly DirectProperty<DataGridColumn, object> HeaderProperty =
  419. AvaloniaProperty.RegisterDirect<DataGridColumn, object>(
  420. nameof(Header),
  421. o => o.Header,
  422. (o, v) => o.Header = v);
  423. /// <summary>
  424. /// Gets or sets the <see cref="DataGridColumnHeader"/> content
  425. /// </summary>
  426. public object Header
  427. {
  428. get { return _header; }
  429. set { SetAndRaise(HeaderProperty, ref _header, value); }
  430. }
  431. /// <summary>
  432. /// Backing field for Header property
  433. /// </summary>
  434. public static readonly DirectProperty<DataGridColumn, IDataTemplate> HeaderTemplateProperty =
  435. AvaloniaProperty.RegisterDirect<DataGridColumn, IDataTemplate>(
  436. nameof(HeaderTemplate),
  437. o => o.HeaderTemplate,
  438. (o, v) => o.HeaderTemplate = v);
  439. /// <summary>
  440. /// Gets or sets an <see cref="IDataTemplate"/> for the <see cref="Header"/>
  441. /// </summary>
  442. public IDataTemplate HeaderTemplate
  443. {
  444. get { return _headerTemplate; }
  445. set { SetAndRaise(HeaderTemplateProperty, ref _headerTemplate, value); }
  446. }
  447. public bool IsAutoGenerated
  448. {
  449. get;
  450. internal set;
  451. }
  452. public bool IsFrozen
  453. {
  454. get;
  455. internal set;
  456. }
  457. public virtual bool IsReadOnly
  458. {
  459. get
  460. {
  461. if (OwningGrid == null)
  462. {
  463. return _isReadOnly ?? DATAGRIDCOLUMN_defaultIsReadOnly;
  464. }
  465. if (_isReadOnly != null)
  466. {
  467. return _isReadOnly.Value || OwningGrid.IsReadOnly;
  468. }
  469. return OwningGrid.GetColumnReadOnlyState(this, DATAGRIDCOLUMN_defaultIsReadOnly);
  470. }
  471. set
  472. {
  473. if (value != _isReadOnly)
  474. {
  475. OwningGrid?.OnColumnReadOnlyStateChanging(this, value);
  476. _isReadOnly = value;
  477. }
  478. }
  479. }
  480. public double MaxWidth
  481. {
  482. get
  483. {
  484. return _maxWidth ?? double.PositiveInfinity;
  485. }
  486. set
  487. {
  488. if (value < 0)
  489. {
  490. throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "MaxWidth", 0);
  491. }
  492. if (value < ActualMinWidth)
  493. {
  494. throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "MaxWidth", "MinWidth");
  495. }
  496. if (!_maxWidth.HasValue || _maxWidth.Value != value)
  497. {
  498. double oldValue = ActualMaxWidth;
  499. _maxWidth = value;
  500. if (OwningGrid != null && OwningGrid.ColumnsInternal != null)
  501. {
  502. OwningGrid.OnColumnMaxWidthChanged(this, oldValue);
  503. }
  504. }
  505. }
  506. }
  507. public double MinWidth
  508. {
  509. get
  510. {
  511. return _minWidth ?? 0;
  512. }
  513. set
  514. {
  515. if (double.IsNaN(value))
  516. {
  517. throw DataGridError.DataGrid.ValueCannotBeSetToNAN("MinWidth");
  518. }
  519. if (value < 0)
  520. {
  521. throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "MinWidth", 0);
  522. }
  523. if (double.IsPositiveInfinity(value))
  524. {
  525. throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("MinWidth");
  526. }
  527. if (value > ActualMaxWidth)
  528. {
  529. throw DataGridError.DataGrid.ValueMustBeLessThanOrEqualTo("value", "MinWidth", "MaxWidth");
  530. }
  531. if (!_minWidth.HasValue || _minWidth.Value != value)
  532. {
  533. double oldValue = ActualMinWidth;
  534. _minWidth = value;
  535. if (OwningGrid != null && OwningGrid.ColumnsInternal != null)
  536. {
  537. OwningGrid.OnColumnMinWidthChanged(this, oldValue);
  538. }
  539. }
  540. }
  541. }
  542. public static readonly StyledProperty<DataGridLength> WidthProperty = AvaloniaProperty
  543. .Register<DataGridColumn, DataGridLength>(nameof(Width)
  544. , coerce: CoerceWidth
  545. );
  546. public DataGridLength Width
  547. {
  548. get => this.GetValue(WidthProperty);
  549. set => SetValue(WidthProperty, value);
  550. }
  551. /// <summary>
  552. /// The binding that will be used to get or set cell content for the clipboard.
  553. /// </summary>
  554. public virtual IBinding ClipboardContentBinding
  555. {
  556. get
  557. {
  558. return _clipboardContentBinding;
  559. }
  560. set
  561. {
  562. _clipboardContentBinding = value;
  563. }
  564. }
  565. /// <summary>
  566. /// Gets the value of a cell according to the the specified binding.
  567. /// </summary>
  568. /// <param name="item">The item associated with a cell.</param>
  569. /// <param name="binding">The binding to get the value of.</param>
  570. /// <returns>The resultant cell value.</returns>
  571. internal object GetCellValue(object item, IBinding binding)
  572. {
  573. Debug.Assert(OwningGrid != null);
  574. object content = null;
  575. if (binding != null)
  576. {
  577. OwningGrid.ClipboardContentControl.DataContext = item;
  578. var sub = OwningGrid.ClipboardContentControl.Bind(ContentControl.ContentProperty, binding);
  579. content = OwningGrid.ClipboardContentControl.GetValue(ContentControl.ContentProperty);
  580. sub.Dispose();
  581. }
  582. return content;
  583. }
  584. public Control GetCellContent(DataGridRow dataGridRow)
  585. {
  586. dataGridRow = dataGridRow ?? throw new ArgumentNullException(nameof(dataGridRow));
  587. if (OwningGrid == null)
  588. {
  589. throw DataGridError.DataGrid.NoOwningGrid(GetType());
  590. }
  591. if (dataGridRow.OwningGrid == OwningGrid)
  592. {
  593. DataGridCell dataGridCell = dataGridRow.Cells[Index];
  594. if (dataGridCell != null)
  595. {
  596. return dataGridCell.Content as Control;
  597. }
  598. }
  599. return null;
  600. }
  601. public Control GetCellContent(object dataItem)
  602. {
  603. dataItem = dataItem ?? throw new ArgumentNullException(nameof(dataItem));
  604. if (OwningGrid == null)
  605. {
  606. throw DataGridError.DataGrid.NoOwningGrid(GetType());
  607. }
  608. DataGridRow dataGridRow = OwningGrid.GetRowFromItem(dataItem);
  609. if (dataGridRow == null)
  610. {
  611. return null;
  612. }
  613. return GetCellContent(dataGridRow);
  614. }
  615. /// <summary>
  616. /// Returns the column which contains the given element
  617. /// </summary>
  618. /// <param name="element">element contained in a column</param>
  619. /// <returns>Column that contains the element, or null if not found
  620. /// </returns>
  621. public static DataGridColumn GetColumnContainingElement(Control element)
  622. {
  623. // Walk up the tree to find the DataGridCell or DataGridColumnHeader that contains the element
  624. Visual parent = element;
  625. while (parent != null)
  626. {
  627. if (parent is DataGridCell cell)
  628. {
  629. return cell.OwningColumn;
  630. }
  631. if (parent is DataGridColumnHeader columnHeader)
  632. {
  633. return columnHeader.OwningColumn;
  634. }
  635. parent = parent.GetVisualParent();
  636. }
  637. return null;
  638. }
  639. /// <summary>
  640. /// Clears the current sort direction
  641. /// </summary>
  642. public void ClearSort()
  643. {
  644. //InvokeProcessSort is already validating if sorting is possible
  645. _headerCell?.InvokeProcessSort(KeyboardHelper.GetPlatformCtrlOrCmdKeyModifier(OwningGrid));
  646. }
  647. /// <summary>
  648. /// Switches the current state of sort direction
  649. /// </summary>
  650. public void Sort()
  651. {
  652. //InvokeProcessSort is already validating if sorting is possible
  653. _headerCell?.InvokeProcessSort(Input.KeyModifiers.None);
  654. }
  655. /// <summary>
  656. /// Changes the sort direction of this column
  657. /// </summary>
  658. /// <param name="direction">New sort direction</param>
  659. public void Sort(ListSortDirection direction)
  660. {
  661. //InvokeProcessSort is already validating if sorting is possible
  662. _headerCell?.InvokeProcessSort(Input.KeyModifiers.None, direction);
  663. }
  664. /// <summary>
  665. /// When overridden in a derived class, causes the column cell being edited to revert to the unedited value.
  666. /// </summary>
  667. /// <param name="editingElement">
  668. /// The element that the column displays for a cell in editing mode.
  669. /// </param>
  670. /// <param name="uneditedValue">
  671. /// The previous, unedited value in the cell being edited.
  672. /// </param>
  673. protected virtual void CancelCellEdit(Control editingElement, object uneditedValue)
  674. { }
  675. /// <summary>
  676. /// When overridden in a derived class, gets an editing element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
  677. /// </summary>
  678. /// <param name="cell">
  679. /// The cell that will contain the generated element.
  680. /// </param>
  681. /// <param name="dataItem">
  682. /// The data item represented by the row that contains the intended cell.
  683. /// </param>
  684. /// <param name="binding">When the method returns, contains the applied binding.</param>
  685. /// <returns>
  686. /// A new editing element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
  687. /// </returns>
  688. protected abstract Control GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding binding);
  689. /// <summary>
  690. /// When overridden in a derived class, gets a read-only element that is bound to the column's
  691. /// <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
  692. /// </summary>
  693. /// <param name="cell">
  694. /// The cell that will contain the generated element.
  695. /// </param>
  696. /// <param name="dataItem">
  697. /// The data item represented by the row that contains the intended cell.
  698. /// </param>
  699. /// <returns>
  700. /// A new, read-only element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
  701. /// </returns>
  702. protected abstract Control GenerateElement(DataGridCell cell, object dataItem);
  703. /// <summary>
  704. /// Called by a specific column type when one of its properties changed,
  705. /// and its current cells need to be updated.
  706. /// </summary>
  707. /// <param name="propertyName">Indicates which property changed and caused this call</param>
  708. protected void NotifyPropertyChanged(string propertyName)
  709. {
  710. OwningGrid?.RefreshColumnElements(this, propertyName);
  711. }
  712. /// <summary>
  713. /// When overridden in a derived class, called when a cell in the column enters editing mode.
  714. /// </summary>
  715. /// <param name="editingElement">
  716. /// The element that the column displays for a cell in editing mode.
  717. /// </param>
  718. /// <param name="editingEventArgs">
  719. /// Information about the user gesture that is causing a cell to enter editing mode.
  720. /// </param>
  721. /// <returns>
  722. /// The unedited value.
  723. /// </returns>
  724. protected abstract object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs);
  725. /// <summary>
  726. /// Called by the DataGrid control when a column asked for its
  727. /// elements to be refreshed, typically because one of its properties changed.
  728. /// </summary>
  729. /// <param name="element">Indicates the element that needs to be refreshed</param>
  730. /// <param name="propertyName">Indicates which property changed and caused this call</param>
  731. protected internal virtual void RefreshCellContent(Control element, string propertyName)
  732. { }
  733. /// <summary>
  734. /// When overridden in a derived class, called when a cell in the column exits editing mode.
  735. /// </summary>
  736. protected virtual void EndCellEdit()
  737. { }
  738. internal void CancelCellEditInternal(Control editingElement, object uneditedValue)
  739. {
  740. CancelCellEdit(editingElement, uneditedValue);
  741. }
  742. internal void EndCellEditInternal()
  743. {
  744. EndCellEdit();
  745. }
  746. /// <summary>
  747. /// Coerces a DataGridLength to a valid value. If any value components are double.NaN, this method
  748. /// coerces them to a proper initial value. For star columns, the desired width is calculated based
  749. /// on the rest of the star columns. For pixel widths, the desired value is based on the pixel value.
  750. /// For auto widths, the desired value is initialized as the column's minimum width.
  751. /// </summary>
  752. /// <param name="source"></param>
  753. /// <param name="width">The DataGridLength to coerce.</param>
  754. /// <returns>The resultant (coerced) DataGridLength.</returns>
  755. private static DataGridLength CoerceWidth(AvaloniaObject source, DataGridLength width)
  756. {
  757. var target = (DataGridColumn)source;
  758. if (target._setWidthInternalNoCallback)
  759. {
  760. return width;
  761. }
  762. if (!target.IsSet(WidthProperty))
  763. {
  764. return target.OwningGrid?.ColumnWidth ??
  765. DataGridLength.Auto;
  766. }
  767. double desiredValue = width.DesiredValue;
  768. if (double.IsNaN(desiredValue))
  769. {
  770. if (width.IsStar && target.OwningGrid != null && target.OwningGrid.ColumnsInternal != null)
  771. {
  772. double totalStarValues = 0;
  773. double totalStarDesiredValues = 0;
  774. double totalNonStarDisplayWidths = 0;
  775. foreach (DataGridColumn column in target.OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != target && !double.IsNaN(c.Width.DesiredValue)))
  776. {
  777. if (column.Width.IsStar)
  778. {
  779. totalStarValues += column.Width.Value;
  780. totalStarDesiredValues += column.Width.DesiredValue;
  781. }
  782. else
  783. {
  784. totalNonStarDisplayWidths += column.ActualWidth;
  785. }
  786. }
  787. if (totalStarValues == 0)
  788. {
  789. // Compute the new star column's desired value based on the available space if there are no other visible star columns
  790. desiredValue = Math.Max(target.ActualMinWidth, target.OwningGrid.CellsWidth - totalNonStarDisplayWidths);
  791. }
  792. else
  793. {
  794. // Otherwise, compute its desired value based on those of other visible star columns
  795. desiredValue = totalStarDesiredValues * width.Value / totalStarValues;
  796. }
  797. }
  798. else if (width.IsAbsolute)
  799. {
  800. desiredValue = width.Value;
  801. }
  802. else
  803. {
  804. desiredValue = target.ActualMinWidth;
  805. }
  806. }
  807. double displayValue = width.DisplayValue;
  808. if (double.IsNaN(displayValue))
  809. {
  810. displayValue = desiredValue;
  811. }
  812. displayValue = Math.Max(target.ActualMinWidth, Math.Min(target.ActualMaxWidth, displayValue));
  813. return new DataGridLength(width.Value, width.UnitType, desiredValue, displayValue);
  814. }
  815. /// <summary>
  816. /// If the DataGrid is using layout rounding, the pixel snapping will force all widths to
  817. /// whole numbers. Since the column widths aren't visual elements, they don't go through the normal
  818. /// rounding process, so we need to do it ourselves. If we don't, then we'll end up with some
  819. /// pixel gaps and/or overlaps between columns.
  820. /// </summary>
  821. /// <param name="leftEdge"></param>
  822. internal void ComputeLayoutRoundedWidth(double leftEdge)
  823. {
  824. if (OwningGrid != null && OwningGrid.UseLayoutRounding)
  825. {
  826. var scale = LayoutHelper.GetLayoutScale(HeaderCell);
  827. var roundSize = LayoutHelper.RoundLayoutSizeUp(new Size(leftEdge + ActualWidth, 1), scale, scale);
  828. LayoutRoundedWidth = roundSize.Width - leftEdge;
  829. }
  830. else
  831. {
  832. LayoutRoundedWidth = ActualWidth;
  833. }
  834. }
  835. internal virtual DataGridColumnHeader CreateHeader()
  836. {
  837. var result = new DataGridColumnHeader
  838. {
  839. OwningColumn = this
  840. };
  841. result[!ContentControl.ContentProperty] = this[!HeaderProperty];
  842. result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty];
  843. if (OwningGrid.ColumnHeaderTheme is { } columnTheme)
  844. {
  845. result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.Template);
  846. }
  847. result.PointerPressed += (s, e) => { HeaderPointerPressed?.Invoke(this, e); };
  848. result.PointerReleased += (s, e) => { HeaderPointerReleased?.Invoke(this, e); };
  849. return result;
  850. }
  851. /// <summary>
  852. /// Ensures that this column's width has been coerced to a valid value.
  853. /// </summary>
  854. internal void EnsureWidth()
  855. {
  856. SetWidthInternalNoCallback(CoerceWidth(this, Width));
  857. }
  858. internal Control GenerateElementInternal(DataGridCell cell, object dataItem)
  859. {
  860. return GenerateElement(cell, dataItem);
  861. }
  862. internal object PrepareCellForEditInternal(Control editingElement, RoutedEventArgs editingEventArgs)
  863. {
  864. var result = PrepareCellForEdit(editingElement, editingEventArgs);
  865. editingElement.Focus();
  866. return result;
  867. }
  868. /// <summary>
  869. /// Attempts to resize the column's width to the desired DisplayValue, but limits the final size
  870. /// to the column's minimum and maximum values. If star sizing is being used, then the column
  871. /// can only decrease in size by the amount that the columns after it can increase in size.
  872. /// Likewise, the column can only increase in size if other columns can spare the width.
  873. /// </summary>
  874. /// <param name="oldWidth">with before resize.</param>
  875. /// <param name="newWidth">with after resize.</param>
  876. /// <param name="userInitiated">Whether or not this resize was initiated by a user action.</param>
  877. // double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue
  878. internal void Resize(DataGridLength oldWidth, DataGridLength newWidth, bool userInitiated)
  879. {
  880. double newValue = newWidth.Value;
  881. double newDesiredValue = newWidth.DesiredValue;
  882. double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, newWidth.DisplayValue));
  883. DataGridLengthUnitType newUnitType = newWidth.UnitType;
  884. int starColumnsCount = 0;
  885. double totalDisplayWidth = 0;
  886. foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
  887. {
  888. column.EnsureWidth();
  889. totalDisplayWidth += column.ActualWidth;
  890. starColumnsCount += (column != this && column.Width.IsStar) ? 1 : 0;
  891. }
  892. bool hasInfiniteAvailableWidth = !OwningGrid.RowsPresenterAvailableSize.HasValue || double.IsPositiveInfinity(OwningGrid.RowsPresenterAvailableSize.Value.Width);
  893. // If we're using star sizing, we can only resize the column as much as the columns to the
  894. // right will allow (i.e. until they hit their max or min widths).
  895. if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (newUnitType == DataGridLengthUnitType.Star && newWidth.IsStar && userInitiated)))
  896. {
  897. double limitedDisplayValue = oldWidth.DisplayValue;
  898. double availableIncrease = Math.Max(0, OwningGrid.CellsWidth - totalDisplayWidth);
  899. double desiredChange = newDisplayValue - oldWidth.DisplayValue;
  900. if (desiredChange > availableIncrease)
  901. {
  902. // The desired change is greater than the amount of available space,
  903. // so we need to decrease the widths of columns to the right to make room.
  904. desiredChange -= availableIncrease;
  905. double actualChange = desiredChange + OwningGrid.DecreaseColumnWidths(DisplayIndex + 1, -desiredChange, userInitiated);
  906. limitedDisplayValue += availableIncrease + actualChange;
  907. }
  908. else if (desiredChange > 0)
  909. {
  910. // The desired change is positive but less than the amount of available space,
  911. // so there's no need to decrease the widths of columns to the right.
  912. limitedDisplayValue += desiredChange;
  913. }
  914. else
  915. {
  916. // The desired change is negative, so we need to increase the widths of columns to the right.
  917. limitedDisplayValue += desiredChange + OwningGrid.IncreaseColumnWidths(DisplayIndex + 1, -desiredChange, userInitiated);
  918. }
  919. if (ActualCanUserResize || (oldWidth.IsStar && !userInitiated))
  920. {
  921. newDisplayValue = limitedDisplayValue;
  922. }
  923. }
  924. if (userInitiated)
  925. {
  926. newDesiredValue = newDisplayValue;
  927. if (!Width.IsStar)
  928. {
  929. InheritsWidth = false;
  930. newValue = newDisplayValue;
  931. newUnitType = DataGridLengthUnitType.Pixel;
  932. }
  933. else if (starColumnsCount > 0 && !hasInfiniteAvailableWidth)
  934. {
  935. // Recalculate star weight of this column based on the new desired value
  936. InheritsWidth = false;
  937. newValue = Width.Value * newDisplayValue / ActualWidth;
  938. }
  939. }
  940. newDisplayValue = Math.Min(double.MaxValue, newValue);
  941. newWidth = new DataGridLength(newDisplayValue, newUnitType, newDesiredValue, newDisplayValue);
  942. SetWidthInternalNoCallback(newWidth);
  943. if (newWidth != oldWidth)
  944. {
  945. OwningGrid.OnColumnWidthChanged(this);
  946. }
  947. }
  948. /// <summary>
  949. /// Sets the column's Width to a new DataGridLength with a different DesiredValue.
  950. /// </summary>
  951. /// <param name="desiredValue">The new DesiredValue.</param>
  952. internal void SetWidthDesiredValue(double desiredValue)
  953. {
  954. SetWidthInternalNoCallback(new DataGridLength(Width.Value, Width.UnitType, desiredValue, Width.DisplayValue));
  955. }
  956. /// <summary>
  957. /// Sets the column's Width to a new DataGridLength with a different DisplayValue.
  958. /// </summary>
  959. /// <param name="displayValue">The new DisplayValue.</param>
  960. internal void SetWidthDisplayValue(double displayValue)
  961. {
  962. SetWidthInternalNoCallback(new DataGridLength(Width.Value, Width.UnitType, Width.DesiredValue, displayValue));
  963. }
  964. /// <summary>
  965. /// Set the column's Width without breaking inheritance.
  966. /// </summary>
  967. /// <param name="width">The new Width.</param>
  968. internal void SetWidthInternal(DataGridLength width)
  969. {
  970. bool originalValue = _settingWidthInternally;
  971. _settingWidthInternally = true;
  972. try
  973. {
  974. Width = width;
  975. }
  976. finally
  977. {
  978. _settingWidthInternally = originalValue;
  979. }
  980. }
  981. /// <summary>
  982. /// Sets the column's Width directly, without any callback effects.
  983. /// </summary>
  984. /// <param name="width">The new Width.</param>
  985. internal void SetWidthInternalNoCallback(DataGridLength width)
  986. {
  987. var originalValue = _setWidthInternalNoCallback;
  988. _setWidthInternalNoCallback = true;
  989. try
  990. {
  991. Width = width;
  992. }
  993. finally
  994. {
  995. _setWidthInternalNoCallback = originalValue;
  996. }
  997. }
  998. /// <summary>
  999. /// Set the column's star value. Whenever the star value changes, width inheritance is broken.
  1000. /// </summary>
  1001. /// <param name="value">The new star value.</param>
  1002. internal void SetWidthStarValue(double value)
  1003. {
  1004. InheritsWidth = false;
  1005. SetWidthInternalNoCallback(new DataGridLength(value, Width.UnitType, Width.DesiredValue, Width.DisplayValue));
  1006. }
  1007. //TODO Binding
  1008. internal Control GenerateEditingElementInternal(DataGridCell cell, object dataItem)
  1009. {
  1010. if (_editingElement == null)
  1011. {
  1012. _editingElement = GenerateEditingElement(cell, dataItem, out _editBinding);
  1013. }
  1014. return _editingElement;
  1015. }
  1016. /// <summary>
  1017. /// Clears the cached editing element.
  1018. /// </summary>
  1019. //TODO Binding
  1020. internal void RemoveEditingElement()
  1021. {
  1022. _editingElement = null;
  1023. }
  1024. /// <summary>
  1025. /// Holds the name of the member to use for sorting, if not using the default.
  1026. /// </summary>
  1027. public string SortMemberPath
  1028. {
  1029. get;
  1030. set;
  1031. }
  1032. /// <summary>
  1033. /// Gets or sets an object associated with this column.
  1034. /// </summary>
  1035. public object Tag
  1036. {
  1037. get;
  1038. set;
  1039. }
  1040. /// <summary>
  1041. /// Holds a Comparer to use for sorting, if not using the default.
  1042. /// </summary>
  1043. public System.Collections.IComparer CustomSortComparer
  1044. {
  1045. get;
  1046. set;
  1047. }
  1048. /// <summary>
  1049. /// We get the sort description from the data source. We don't worry whether we can modify sort -- perhaps the sort description
  1050. /// describes an unchangeable sort that exists on the data.
  1051. /// </summary>
  1052. internal DataGridSortDescription GetSortDescription()
  1053. {
  1054. if (OwningGrid != null
  1055. && OwningGrid.DataConnection != null
  1056. && OwningGrid.DataConnection.SortDescriptions != null)
  1057. {
  1058. if (CustomSortComparer != null)
  1059. {
  1060. return
  1061. OwningGrid.DataConnection.SortDescriptions
  1062. .OfType<DataGridComparerSortDescription>()
  1063. .FirstOrDefault(s => s.SourceComparer == CustomSortComparer);
  1064. }
  1065. string propertyName = GetSortPropertyName();
  1066. return OwningGrid.DataConnection.SortDescriptions.FirstOrDefault(s => s.HasPropertyPath && s.PropertyPath == propertyName);
  1067. }
  1068. return null;
  1069. }
  1070. internal string GetSortPropertyName()
  1071. {
  1072. string result = SortMemberPath;
  1073. if (String.IsNullOrEmpty(result))
  1074. {
  1075. if (this is DataGridBoundColumn boundColumn)
  1076. {
  1077. if (boundColumn.Binding is Binding binding)
  1078. {
  1079. result = binding.Path;
  1080. }
  1081. else if (boundColumn.Binding is CompiledBindingExtension compiledBinding)
  1082. {
  1083. result = compiledBinding.Path.ToString();
  1084. }
  1085. }
  1086. }
  1087. return result;
  1088. }
  1089. }
  1090. }