From f4e6cf083b8b3f8efc75c70bd7fa625481cb0073 Mon Sep 17 00:00:00 2001 From: chinboy Date: Thu, 16 Jan 2025 08:57:36 +0800 Subject: [PATCH] Add DataGridEnumerations enums --- .../AtomUI.Controls.DataGrid.csproj | 6 + .../DataGridEnumerations.cs | 98 +++++ src/AtomUI.Controls.DataGrid/DataGridError.cs | 201 ++++++++++ .../DataGridLength.cs | 15 + .../DataGridLengthConverter.cs | 6 + .../Primitives/DataGridCellsPresenter.cs | 344 ++++++++++++++++++ 6 files changed, 670 insertions(+) create mode 100644 src/AtomUI.Controls.DataGrid/DataGridEnumerations.cs create mode 100644 src/AtomUI.Controls.DataGrid/DataGridError.cs create mode 100644 src/AtomUI.Controls.DataGrid/DataGridLength.cs create mode 100644 src/AtomUI.Controls.DataGrid/DataGridLengthConverter.cs create mode 100644 src/AtomUI.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs diff --git a/src/AtomUI.Controls.DataGrid/AtomUI.Controls.DataGrid.csproj b/src/AtomUI.Controls.DataGrid/AtomUI.Controls.DataGrid.csproj index 065528396..c26890ae9 100644 --- a/src/AtomUI.Controls.DataGrid/AtomUI.Controls.DataGrid.csproj +++ b/src/AtomUI.Controls.DataGrid/AtomUI.Controls.DataGrid.csproj @@ -18,5 +18,11 @@ + + + + + + diff --git a/src/AtomUI.Controls.DataGrid/DataGridEnumerations.cs b/src/AtomUI.Controls.DataGrid/DataGridEnumerations.cs new file mode 100644 index 000000000..3012e2222 --- /dev/null +++ b/src/AtomUI.Controls.DataGrid/DataGridEnumerations.cs @@ -0,0 +1,98 @@ +namespace AtomUI.Controls; + +/// +/// Used to specify action to take out of edit mode. +/// +public enum DataGridEditAction +{ + /// + /// Cancel the changes. + /// + Cancel, + + /// + /// Commit edited value. + /// + Commit +} + +// Determines the location and visibility of the editing row. +internal enum DataGridEditingRowLocation +{ + Bottom = 0, // The editing row is collapsed below the displayed rows + Inline = 1, // The editing row is visible and displayed + Top = 2 // The editing row is collapsed above the displayed rows +} + +/// +/// Determines whether the inner cells' vertical/horizontal gridlines are shown or not. +/// +[Flags] +public enum DataGridGridLinesVisibility +{ + None = 0, + Horizontal = 1, + Vertical = 2, + All = 3, +} + +public enum DataGridEditingUnit +{ + Cell = 0, + Row = 1, +} + +/// +/// Determines whether the row/column headers are shown or not. +/// +[Flags] +public enum DataGridHeadersVisibility +{ + /// + /// Show Row, Column, and Corner Headers + /// + All = Row | Column, + + /// + /// Show only Column Headers with top-right corner Header + /// + Column = 0x01, + + /// + /// Show only Row Headers with bottom-left corner + /// + Row = 0x02, + + /// + /// Don’t show any Headers + /// + None = 0x00 +} + +public enum DataGridRowDetailsVisibilityMode +{ + Collapsed = 2, // Show no details. Developer is in charge of toggling visibility. + Visible = 1, // Show the details section for all rows. + VisibleWhenSelected = 0 // Show the details section only for the selected row(s). +} + +/// +/// Determines the type of action to take when selecting items +/// +internal enum DataGridSelectionAction +{ + AddCurrentToSelection, + None, + RemoveCurrentFromSelection, + SelectCurrent, + SelectFromAnchorToCurrent +} + +/// +/// Determines the selection model +/// +public enum DataGridSelectionMode +{ + Extended = 0, + Single = 1 +} \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/DataGridError.cs b/src/AtomUI.Controls.DataGrid/DataGridError.cs new file mode 100644 index 000000000..0f0ad6850 --- /dev/null +++ b/src/AtomUI.Controls.DataGrid/DataGridError.cs @@ -0,0 +1,201 @@ +// (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 System.Globalization; + +namespace AtomUI.Controls; + +internal static class DataGridError +{ + public static class DataGrid + { + public static InvalidOperationException CannotChangeItemsWhenLoadingRows() + { + return new InvalidOperationException( + "Items cannot be added, removed or reset while rows are loading or unloading."); + } + + public static InvalidOperationException CannotChangeColumnCollectionWhileAdjustingDisplayIndexes() + { + return new InvalidOperationException( + "Column collection cannot be changed while adjusting display indexes."); + } + + public static InvalidOperationException ColumnCannotBeCollapsed() + { + return new InvalidOperationException("Column cannot be collapsed."); + } + + public static InvalidOperationException ColumnCannotBeReassignedToDifferentDataGrid() + { + return new InvalidOperationException( + "Column already belongs to a DataGrid instance and cannot be reassigned."); + } + + public static ArgumentException ColumnNotInThisDataGrid() + { + return new ArgumentException("Provided column does not belong to this DataGrid."); + } + + public static ArgumentException ItemIsNotContainedInTheItemsSource(string paramName) + { + return new ArgumentException("The item is not contained in the ItemsSource.", paramName); + } + + public static InvalidOperationException NoCurrentRow() + { + return new InvalidOperationException("There is no current row. Operation cannot be completed."); + } + + public static InvalidOperationException NoOwningGrid(Type type) + { + return new InvalidOperationException(Format( + "There is no instance of DataGrid assigned to this {0}. Operation cannot be completed.", + type.FullName ?? type.Name)); + } + + public static InvalidOperationException UnderlyingPropertyIsReadOnly(string paramName) + { + return new InvalidOperationException( + Format("{0} cannot be set because the underlying property is read only.", paramName)); + } + + public static ArgumentException ValueCannotBeSetToInfinity(string paramName) + { + return new ArgumentException(Format("{0} cannot be set to infinity.", paramName)); + } + + public static ArgumentException ValueCannotBeSetToNAN(string paramName) + { + return new ArgumentException(Format("{0} cannot be set to double.NAN.", paramName)); + } + + public static ArgumentNullException ValueCannotBeSetToNull(string paramName, string valueName) + { + return new ArgumentNullException(paramName, Format("{0} cannot be set to a null value.", valueName)); + } + + public static ArgumentException ValueIsNotAnInstanceOf(string paramName, Type type) + { + return new ArgumentException(paramName, Format("The value is not an instance of {0}.", type.FullName ?? type.Name)); + } + + public static ArgumentException ValueIsNotAnInstanceOfEitherOr(string paramName, Type type1, Type type2) + { + return new ArgumentException(paramName, + Format("The value is not an instance of {0} or {1}.", type1.FullName ?? type1.Name, type2.FullName ?? type2.Name)); + } + + public static ArgumentOutOfRangeException ValueMustBeBetween(string paramName, string valueName, + object lowValue, bool lowInclusive, + object highValue, bool highInclusive) + { + string? message = null; + + if (lowInclusive && highInclusive) + { + message = "{0} must be greater than or equal to {1} and less than or equal to {2}."; + } + else if (lowInclusive && !highInclusive) + { + message = "{0} must be greater than or equal to {1} and less than {2}."; + } + else if (!lowInclusive && highInclusive) + { + message = "{0} must be greater than {1} and less than or equal to {2}."; + } + else + { + message = "{0} must be greater than {1} and less than {2}."; + } + + return new ArgumentOutOfRangeException(paramName, Format(message, valueName, lowValue, highValue)); + } + + public static ArgumentOutOfRangeException ValueMustBeGreaterThanOrEqualTo( + string paramName, string valueName, object value) + { + return new ArgumentOutOfRangeException(paramName, + Format("{0} must be greater than or equal to {1}.", valueName, value)); + } + + public static ArgumentOutOfRangeException ValueMustBeLessThanOrEqualTo( + string paramName, string valueName, object value) + { + return new ArgumentOutOfRangeException(paramName, + Format("{0} must be less than or equal to {1}.", valueName, value)); + } + + public static ArgumentOutOfRangeException ValueMustBeLessThan(string paramName, string valueName, object value) + { + return new ArgumentOutOfRangeException(paramName, Format("{0} must be less than {1}.", valueName, value)); + } + } + + public static class DataGridColumnHeader + { + public static NotSupportedException ContentDoesNotSupportUIElements() + { + return new NotSupportedException("Content does not support Controls; use ContentTemplate instead."); + } + } + + public static class DataGridLength + { + public static ArgumentException InvalidUnitType(string paramName) + { + return new ArgumentException(Format("{0} is not a valid DataGridLengthUnitType.", paramName), paramName); + } + } + + public static class DataGridLengthConverter + { + public static NotSupportedException CannotConvertFrom(string paramName) + { + return new NotSupportedException(Format("DataGridLengthConverter cannot convert from {0}.", paramName)); + } + + public static NotSupportedException CannotConvertTo(string paramName) + { + return new NotSupportedException(Format("Cannot convert from DataGridLength to {0}.", paramName)); + } + + public static NotSupportedException InvalidDataGridLength(string paramName) + { + return new NotSupportedException(Format("Invalid DataGridLength.", paramName)); + } + } + + public static class DataGridRow + { + public static InvalidOperationException InvalidRowIndexCannotCompleteOperation() + { + return new InvalidOperationException("Invalid row index. Operation cannot be completed."); + } + } + + public static class DataGridSelectedItemsCollection + { + public static InvalidOperationException CannotChangeSelectedItemsCollectionInSingleMode() + { + return new InvalidOperationException( + "Can only change SelectedItems collection in Extended selection mode. Use SelectedItem property in Single selection mode."); + } + } + + public static class DataGridTemplateColumn + { + public static TypeInitializationException MissingTemplateForType(Type type) + { + return new TypeInitializationException(Format("Missing template. Cannot initialize {0}.", type.FullName ?? type.Name), + null); + } + } + + private static string Format(string formatString, params object[] args) + { + return String.Format(CultureInfo.CurrentCulture, formatString, args); + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/DataGridLength.cs b/src/AtomUI.Controls.DataGrid/DataGridLength.cs new file mode 100644 index 000000000..81c514509 --- /dev/null +++ b/src/AtomUI.Controls.DataGrid/DataGridLength.cs @@ -0,0 +1,15 @@ +namespace AtomUI.Controls; + +public enum DataGridLengthUnitType +{ + Auto = 0, + Pixel = 1, + SizeToCells = 2, + SizeToHeader = 3, + Star = 4 +} + +public class DataGridLength +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/DataGridLengthConverter.cs b/src/AtomUI.Controls.DataGrid/DataGridLengthConverter.cs new file mode 100644 index 000000000..9f5536f34 --- /dev/null +++ b/src/AtomUI.Controls.DataGrid/DataGridLengthConverter.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +public class DataGridLengthConverter +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs b/src/AtomUI.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs new file mode 100644 index 000000000..c6c2ad5b0 --- /dev/null +++ b/src/AtomUI.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs @@ -0,0 +1,344 @@ +// using Avalonia.Controls; +// using Avalonia.LogicalTree; +// +// namespace AtomUI.Controls.Primitives; +// +// public sealed class DataGridCellsPresenter: Panel, IChildIndexProvider +// { +// private double _fillerLeftEdge; +// private EventHandler _childIndexChanged; +// +// +// // The desired height needs to be cached due to column virtualization; otherwise, the cells +// // would grow and shrink as the DataGrid scrolls horizontally +// private double DesiredHeight +// { +// get; +// set; +// } +// +// private DataGrid OwningGrid +// { +// get +// { +// return OwningRow?.OwningGrid; +// } +// } +// +// internal DataGridRow OwningRow +// { +// get; +// set; +// } +// +// event EventHandler IChildIndexProvider.ChildIndexChanged +// { +// add => _childIndexChanged += value; +// remove => _childIndexChanged -= value; +// } +// +// int IChildIndexProvider.GetChildIndex(ILogical child) +// { +// return child is DataGridCell cell +// ? cell.OwningColumn?.DisplayIndex ?? -1 +// : throw new InvalidOperationException("Invalid cell type"); +// } +// +// bool IChildIndexProvider.TryGetTotalCount(out int count) +// { +// count = Children.Count - 1; // Adjust for filler column +// return true; +// } +// +// /// +// /// Arranges the content of the . +// /// +// /// +// /// The actual size used by the . +// /// +// /// +// /// The final area within the parent that this element should use to arrange itself and its children. +// /// +// protected override Size ArrangeOverride(Size finalSize) +// { +// if (OwningGrid == null) +// { +// return base.ArrangeOverride(finalSize); +// } +// +// if (OwningGrid.AutoSizingColumns) +// { +// // When we initially load an auto-column, we have to wait for all the rows to be measured +// // before we know its final desired size. We need to trigger a new round of measures now +// // that the final sizes have been calculated. +// OwningGrid.AutoSizingColumns = false; +// return base.ArrangeOverride(finalSize); +// } +// +// double frozenLeftEdge = 0; +// double scrollingLeftEdge = -OwningGrid.HorizontalOffset; +// +// double cellLeftEdge; +// foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns()) +// { +// DataGridCell cell = OwningRow.Cells[column.Index]; +// Debug.Assert(cell.OwningColumn == column); +// Debug.Assert(column.IsVisible); +// +// if (column.IsFrozen) +// { +// cellLeftEdge = frozenLeftEdge; +// // This can happen before or after clipping because frozen cells aren't clipped +// frozenLeftEdge += column.ActualWidth; +// } +// else +// { +// cellLeftEdge = scrollingLeftEdge; +// } +// if (cell.IsVisible) +// { +// cell.Arrange(new Rect(cellLeftEdge, 0, column.LayoutRoundedWidth, finalSize.Height)); +// EnsureCellClip(cell, column.ActualWidth, finalSize.Height, frozenLeftEdge, scrollingLeftEdge); +// } +// scrollingLeftEdge += column.ActualWidth; +// column.IsInitialDesiredWidthDetermined = true; +// } +// +// _fillerLeftEdge = scrollingLeftEdge; +// +// OwningRow.FillerCell.Arrange(new Rect(_fillerLeftEdge, 0, OwningGrid.ColumnsInternal.FillerColumn.FillerWidth, finalSize.Height)); +// +// return finalSize; +// } +// +// private static void EnsureCellClip(DataGridCell cell, double width, double height, double frozenLeftEdge, double cellLeftEdge) +// { +// // Clip the cell only if it's scrolled under frozen columns. Unfortunately, we need to clip in this case +// // because cells could be transparent +// if (!cell.OwningColumn.IsFrozen && frozenLeftEdge > cellLeftEdge) +// { +// RectangleGeometry rg = new RectangleGeometry(); +// double xClip = Math.Round(Math.Min(width, frozenLeftEdge - cellLeftEdge)); +// rg.Rect = new Rect(xClip, 0, Math.Max(0, width - xClip), height); +// cell.Clip = rg; +// } +// else +// { +// cell.Clip = null; +// } +// } +// +// protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) +// { +// base.ChildrenChanged(sender, e); +// +// InvalidateChildIndex(); +// } +// +// private static void EnsureCellDisplay(DataGridCell cell, bool displayColumn) +// { +// if (cell.IsCurrent) +// { +// if (displayColumn) +// { +// cell.IsVisible = true; +// cell.Clip = null; +// } +// else +// { +// // Clip +// RectangleGeometry rg = new RectangleGeometry(); +// rg.Rect = default; +// cell.Clip = rg; +// } +// } +// else +// { +// cell.IsVisible = displayColumn; +// } +// } +// +// internal void EnsureFillerVisibility() +// { +// DataGridFillerColumn fillerColumn = OwningGrid.ColumnsInternal.FillerColumn; +// bool newVisibility = fillerColumn.IsActive; +// if (OwningRow.FillerCell.IsVisible != newVisibility) +// { +// OwningRow.FillerCell.IsVisible = newVisibility; +// if (newVisibility) +// { +// OwningRow.FillerCell.Arrange(new Rect(_fillerLeftEdge, 0, fillerColumn.FillerWidth, Bounds.Height)); +// } +// } +// +// // This must be done after the Filler visibility is determined. This also must be done +// // regardless of whether or not the filler visibility actually changed values because +// // we could scroll in a cell that didn't have EnsureGridLine called yet +// DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn; +// if (lastVisibleColumn != null) +// { +// DataGridCell cell = OwningRow.Cells[lastVisibleColumn.Index]; +// cell.EnsureGridLine(lastVisibleColumn); +// } +// } +// +// /// +// /// Measures the children of a to +// /// prepare for arranging them during the pass. +// /// +// /// +// /// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed. +// /// +// /// +// /// The size that the determines it needs during layout, based on its calculations of child object allocated sizes. +// /// +// protected override Size MeasureOverride(Size availableSize) +// { +// if (OwningGrid == null) +// { +// return base.MeasureOverride(availableSize); +// } +// +// bool autoSizeHeight; +// double measureHeight; +// if (double.IsNaN(OwningGrid.RowHeight)) +// { +// // No explicit height values were set so we can autosize +// autoSizeHeight = true; +// // We need to invalidate desired height in order to grow or shrink as needed +// InvalidateDesiredHeight(); +// measureHeight = double.PositiveInfinity; +// } +// else +// { +// DesiredHeight = OwningGrid.RowHeight; +// measureHeight = DesiredHeight; +// autoSizeHeight = false; +// } +// +// double frozenLeftEdge = 0; +// double totalDisplayWidth = 0; +// double scrollingLeftEdge = -OwningGrid.HorizontalOffset; +// OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); +// DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn; +// foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns()) +// { +// DataGridCell cell = OwningRow.Cells[column.Index]; +// // Measure the entire first row to make the horizontal scrollbar more accurate +// bool shouldDisplayCell = ShouldDisplayCell(column, frozenLeftEdge, scrollingLeftEdge) || OwningRow.Index == 0; +// EnsureCellDisplay(cell, shouldDisplayCell); +// if (shouldDisplayCell) +// { +// DataGridLength columnWidth = column.Width; +// bool autoGrowWidth = columnWidth.IsSizeToCells || columnWidth.IsAuto; +// if (column != lastVisibleColumn) +// { +// cell.EnsureGridLine(lastVisibleColumn); +// } +// +// // If we're not using star sizing or the current column can't be resized, +// // then just set the display width according to the column's desired width +// if (!OwningGrid.UsesStarSizing || (!column.ActualCanUserResize && !column.Width.IsStar)) +// { +// // In the edge-case where we're given infinite width and we have star columns, the +// // star columns grow to their predefined limit of 10,000 (or their MaxWidth) +// double newDisplayWidth = column.Width.IsStar ? +// Math.Min(column.ActualMaxWidth, DataGrid.DATAGRID_maximumStarColumnWidth) : +// Math.Max(column.ActualMinWidth, Math.Min(column.ActualMaxWidth, column.Width.DesiredValue)); +// column.SetWidthDisplayValue(newDisplayWidth); +// } +// +// // If we're auto-growing the column based on the cell content, we want to measure it at its maximum value +// if (autoGrowWidth) +// { +// cell.Measure(new Size(column.ActualMaxWidth, measureHeight)); +// OwningGrid.AutoSizeColumn(column, cell.DesiredSize.Width); +// column.ComputeLayoutRoundedWidth(totalDisplayWidth); +// } +// else if (!OwningGrid.UsesStarSizing) +// { +// column.ComputeLayoutRoundedWidth(scrollingLeftEdge); +// cell.Measure(new Size(column.LayoutRoundedWidth, measureHeight)); +// } +// +// // We need to track the largest height in order to auto-size +// if (autoSizeHeight) +// { +// DesiredHeight = Math.Max(DesiredHeight, cell.DesiredSize.Height); +// } +// } +// +// if (column.IsFrozen) +// { +// frozenLeftEdge += column.ActualWidth; +// } +// scrollingLeftEdge += column.ActualWidth; +// totalDisplayWidth += column.ActualWidth; +// } +// +// // If we're using star sizing (and we're not waiting for an auto-column to finish growing) +// // then we will resize all the columns to fit the available space. +// if (OwningGrid.UsesStarSizing && !OwningGrid.AutoSizingColumns) +// { +// double adjustment = OwningGrid.CellsWidth - totalDisplayWidth; +// totalDisplayWidth += adjustment - OwningGrid.AdjustColumnWidths(0, adjustment, false); +// +// // Since we didn't know the final widths of the columns until we resized, +// // we waited until now to measure each cell +// double leftEdge = 0; +// if (autoSizeHeight) +// DesiredHeight = 0; +// +// foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns()) +// { +// DataGridCell cell = OwningRow.Cells[column.Index]; +// column.ComputeLayoutRoundedWidth(leftEdge); +// cell.Measure(new Size(column.LayoutRoundedWidth, measureHeight)); +// if (autoSizeHeight) +// { +// DesiredHeight = Math.Max(DesiredHeight, cell.DesiredSize.Height); +// } +// leftEdge += column.ActualWidth; +// } +// } +// +// // Measure FillerCell, we're doing it unconditionally here because we don't know if we'll need the filler +// // column and we don't want to cause another Measure if we do +// OwningRow.FillerCell.Measure(new Size(double.PositiveInfinity, DesiredHeight)); +// +// OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); +// return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, DesiredHeight); +// } +// +// internal void Recycle() +// { +// // Clear out the cached desired height so it is not reused for other rows +// DesiredHeight = 0; +// } +// +// internal void InvalidateDesiredHeight() +// { +// DesiredHeight = 0; +// } +// +// internal void InvalidateChildIndex() +// { +// _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.ChildIndexesReset); +// } +// +// private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge) +// { +// if (!column.IsVisible) +// { +// return false; +// } +// +// scrollingLeftEdge += OwningGrid.HorizontalAdjustment; +// double leftEdge = column.IsFrozen ? frozenLeftEdge : scrollingLeftEdge; +// double rightEdge = leftEdge + column.ActualWidth; +// return +// MathUtilities.GreaterThan(rightEdge, 0) && +// MathUtilities.LessThanOrClose(leftEdge, OwningGrid.CellsWidth) && +// MathUtilities.GreaterThan(rightEdge, frozenLeftEdge); // scrolling column covered up by frozen column(s) +// } +// } \ No newline at end of file