diff --git a/components/DataTable/samples/DataTable.md b/components/DataTable/samples/DataTable.md
index c9cdf1a98..38ddc422c 100644
--- a/components/DataTable/samples/DataTable.md
+++ b/components/DataTable/samples/DataTable.md
@@ -36,14 +36,6 @@ can be made to look like a table of data:
There are limitations here with having fixed column sizes that can be difficult to align. Their definitions are
also duplicated, and every item is recreating this layout and duplicating it within the Visual Tree.
-## DataRow Hybrid Setup
-
-As a first step, moving to **DataTable** is easy, just replace the `Grid` in your `ItemsTemplate` with the `DataRow` panel
-and remove the Column attributes from your controls. `DataRow` automatically will lay each subsequent control in the next column
-for you automatically:
-
-> [!Sample DataTableHybridSample]
-
## DataTable Setup
The `DataTable` setup provides an easier way to define and manage your columns within your header for this coordinated effort
diff --git a/components/DataTable/samples/DataTableHybridSample.xaml b/components/DataTable/samples/DataTableHybridSample.xaml
deleted file mode 100644
index b6eaf6023..000000000
--- a/components/DataTable/samples/DataTableHybridSample.xaml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/components/DataTable/samples/DataTableHybridSample.xaml.cs b/components/DataTable/samples/DataTableHybridSample.xaml.cs
deleted file mode 100644
index b47a44fed..000000000
--- a/components/DataTable/samples/DataTableHybridSample.xaml.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using CommunityToolkit.WinUI.Controls;
-
-namespace DataTableExperiment.Samples;
-
-[ToolkitSample(id: nameof(DataTableHybridSample), "Hybrid DataTable Example", description: $"A sample for showing how to create and use a {nameof(DataRow)} control alongside an existing traditional setup with Grid.")]
-public sealed partial class DataTableHybridSample : Page
-{
- public ObservableCollection InventoryItems { get; set; } = new()
- {
- new()
- {
- Id = 1002,
- Name = "Hydra",
- Description = "Multiple Launch Rocket System-2 Hydra",
- Quantity = 1,
- },
- new()
- {
- Id = 3456,
- Name = "MA40 AR",
- Description = "Regular assault rifle - updated version of MA5B or MA37 AR",
- Quantity = 4,
- },
- new()
- {
- Id = 5698,
- Name = "Needler",
- Description = "Alien weapon well-known for its iconic design with pink crystals",
- Quantity = 2,
- },
- new()
- {
- Id = 7043,
- Name = "Ravager",
- Description = "An incendiary plasma launcher",
- Quantity = 1,
- },
- };
-
- public DataTableHybridSample()
- {
- this.InitializeComponent();
- }
-}
diff --git a/components/DataTable/src/CommunityToolkit.WinUI.Controls.DataTable.csproj b/components/DataTable/src/CommunityToolkit.WinUI.Controls.DataTable.csproj
index a191e716f..4c6645576 100644
--- a/components/DataTable/src/CommunityToolkit.WinUI.Controls.DataTable.csproj
+++ b/components/DataTable/src/CommunityToolkit.WinUI.Controls.DataTable.csproj
@@ -12,7 +12,7 @@
-
+
$(PackageIdPrefix).$(PackageIdVariant).Controls.$(ToolkitComponentName)
diff --git a/components/DataTable/src/DataTable/DataColumn.cs b/components/DataTable/src/DataTable/DataColumn.cs
index 6f969b15e..eab48c3be 100644
--- a/components/DataTable/src/DataTable/DataColumn.cs
+++ b/components/DataTable/src/DataTable/DataColumn.cs
@@ -4,24 +4,40 @@
namespace CommunityToolkit.WinUI.Controls;
+///
+/// Column of .
+///
[TemplatePart(Name = nameof(PART_ColumnSizer), Type = typeof(ContentSizer))]
public partial class DataColumn : ContentControl
{
- private static GridLength StarLength = new GridLength(1, GridUnitType.Star);
-
private ContentSizer? PART_ColumnSizer;
private WeakReference? _parent;
+ internal DataTable? DataTable => _parent?.TryGetTarget(out DataTable? parent) == true ? parent : null;
+
///
- /// Gets or sets the width of the largest child contained within the visible s of the .
+ /// Gets or sets the internal calculated or manually set width of this column.
+ /// - Positive value: this column has a fixed or manually set width.
+ /// - Negative value: this column has a calculated width which is derived from DesiredWidth.
+ /// - NaN: this column should have a calculated width which isn't set yet.
///
- internal double MaxChildDesiredWidth { get; set; }
+ internal double CurrentWidth { get; set; } = double.NaN;
///
- /// Gets or sets the internal copy of the property to be used in calculations, this gets manipulated in Auto-Size mode.
+ /// Gets the internal calculated or manually set width of this column, as a positive value.
///
- internal GridLength CurrentWidth { get; private set; }
+ internal double ActualCurrentWidth => double.IsNaN(CurrentWidth) ? 0 : Math.Abs(CurrentWidth);
+
+ internal bool IsAbsolute => DesiredWidth.IsAbsolute;
+
+ internal bool IsAuto => DesiredWidth.IsAuto;
+
+ internal bool IsAutoFit => DesiredWidth.IsAuto && !(CurrentWidth > 0);
+
+ internal bool IsStar => DesiredWidth.IsStar;
+
+ internal bool IsStarProportion => DesiredWidth.IsStar && !(CurrentWidth > 0);
///
/// Gets or sets whether the column can be resized by the user.
@@ -36,10 +52,13 @@ public bool CanResize
/// Identifies the property.
///
public static readonly DependencyProperty CanResizeProperty =
- DependencyProperty.Register("CanResize", typeof(bool), typeof(DataColumn), new PropertyMetadata(false));
+ DependencyProperty.Register(
+ nameof(CanResize), typeof(bool), typeof(DataColumn),
+ new PropertyMetadata(false));
///
- /// Gets or sets the desired width of the column upon initialization. Defaults to a of 1 .
+ /// Gets or sets the desired width of the column upon initialization. Defaults to a
+ /// of 1 .
///
public GridLength DesiredWidth
{
@@ -51,22 +70,34 @@ public GridLength DesiredWidth
/// Identifies the property.
///
public static readonly DependencyProperty DesiredWidthProperty =
- DependencyProperty.Register(nameof(DesiredWidth), typeof(GridLength), typeof(DataColumn), new PropertyMetadata(GridLength.Auto, DesiredWidth_PropertyChanged));
+ DependencyProperty.Register(
+ nameof(DesiredWidth), typeof(GridLength), typeof(DataColumn),
+ new PropertyMetadata(GridLength.Auto, DesiredWidth_PropertyChanged));
private static void DesiredWidth_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
- // If the developer updates the size of the column, update our internal copy
- if (d is DataColumn col)
+ // If the developer updates the size of the column, update our internal value.
+ if (d is DataColumn column)
{
- col.CurrentWidth = col.DesiredWidth;
+ if (column.DesiredWidth is { GridUnitType: GridUnitType.Pixel, Value: var value })
+ {
+ column.CurrentWidth = value;
+ }
+
+ // Request to measure for the IsAutoFit or IsStarProportion columns.
+ column.DataTable?.InvalidateMeasure();
}
}
+ ///
+ /// Constructor.
+ ///
public DataColumn()
{
this.DefaultStyleKey = typeof(DataColumn);
}
+ ///
protected override void OnApplyTemplate()
{
if (PART_ColumnSizer != null)
@@ -108,13 +139,12 @@ private void PART_ColumnSizer_ManipulationCompleted(object sender, ManipulationC
private void ColumnResizedByUserSizer()
{
// Update our internal representation to be our size now as a fixed value.
- CurrentWidth = new(this.ActualWidth);
-
- // Notify the rest of the table to update
- if (_parent?.TryGetTarget(out DataTable? parent) == true
- && parent != null)
+ if (CurrentWidth != this.ActualWidth)
{
- parent.ColumnResized();
+ CurrentWidth = this.ActualWidth;
+
+ // Notify the rest of the table to update
+ DataTable?.ColumnResized();
}
}
}
diff --git a/components/DataTable/src/DataTable/DataColumn.xaml b/components/DataTable/src/DataTable/DataColumn.xaml
index f88fedfe8..dbe58fb44 100644
--- a/components/DataTable/src/DataTable/DataColumn.xaml
+++ b/components/DataTable/src/DataTable/DataColumn.xaml
@@ -12,45 +12,44 @@
diff --git a/components/DataTable/src/DataTable/DataRow.cs b/components/DataTable/src/DataTable/DataRow.cs
index 6546150ae..e96dd67c4 100644
--- a/components/DataTable/src/DataTable/DataRow.cs
+++ b/components/DataTable/src/DataTable/DataRow.cs
@@ -6,16 +6,21 @@
namespace CommunityToolkit.WinUI.Controls;
+///
+/// Row item of .
+///
public partial class DataRow : Panel
{
// TODO: Create our own helper class here for the Header as well vs. straight-Grid.
// TODO: WeakReference?
- private Panel? _parentPanel;
private DataTable? _parentTable;
private bool _isTreeView;
- private double _treePadding;
+ internal double TreePadding { get; private set; }
+ ///
+ /// Constructor.
+ ///
public DataRow()
{
Unloaded += this.DataRow_Unloaded;
@@ -26,40 +31,31 @@ private void DataRow_Unloaded(object sender, RoutedEventArgs e)
// Remove our references on unloaded
_parentTable?.Rows.Remove(this);
_parentTable = null;
- _parentPanel = null;
}
- private Panel? InitializeParentHeaderConnection()
+ private DataTable? InitializeParentHeaderConnection()
{
// TODO: Think about this expression instead...
// Drawback: Can't have Grid between table and header
// Positive: don't have to restart climbing the Visual Tree if we don't find ItemsPresenter...
////var parent = this.FindAscendant(static (element) => element is ItemsPresenter or Grid);
- // TODO: Investigate what a scenario with an ItemsRepeater would look like (with a StackLayout, but using DataRow as the item's panel inside)
+ // TODO: Investigate what a scenario with an ItemsRepeater would look like (with a StackLayout, but
+ // using DataRow as the item's panel inside)
Panel? panel = null;
// 1a. Get parent ItemsPresenter to find header
if (this.FindAscendant() is ItemsPresenter itemsPresenter)
{
- // 2. Quickly check if the header is just what we're looking for.
- if (itemsPresenter.Header is Grid or DataTable)
+ if (itemsPresenter.Header is DependencyObject header)
{
- panel = itemsPresenter.Header as Panel;
- }
- else
- {
- // 3. Otherwise, try and find the inner thing we want.
- panel = itemsPresenter.FindDescendant(static (element) => element is Grid or DataTable);
+ panel = header.FindDescendantOrSelf();
}
// Check if we're in a TreeView
_isTreeView = itemsPresenter.FindAscendant() is TreeView;
}
- // 1b. If we can't find the ItemsPresenter, then we reach up outside to find the next thing we could use as a parent
- panel ??= this.FindAscendant(static (element) => element is Grid or DataTable);
-
// Cache actual datatable reference
if (panel is DataTable table)
{
@@ -67,153 +63,158 @@ private void DataRow_Unloaded(object sender, RoutedEventArgs e)
_parentTable.Rows.Add(this); // Add us to the row list.
}
- return panel;
+ return _parentTable;
}
+ ///
protected override Size MeasureOverride(Size availableSize)
{
+ //Debug.WriteLine($"DataRow.MeasureOverride");
+
// We should probably only have to do this once ever?
- _parentPanel ??= InitializeParentHeaderConnection();
+ _parentTable ??= InitializeParentHeaderConnection();
double maxHeight = 0;
- if (Children.Count > 0)
+ // If we don't have a DataTable, just layout children like a horizontal StackPanel.
+ if (_parentTable is null)
{
- // If we don't have a grid, just measure first child to get row height and take available space
- if (_parentPanel is null)
+ double totalWidth = 0;
+
+ for (int i = 0; i < Children.Count; i++)
{
- Children[0].Measure(availableSize);
- return new Size(availableSize.Width, Children[0].DesiredSize.Height);
+ var child = Children[i];
+ if (child?.Visibility != Visibility.Visible)
+ continue;
+
+ child.Measure(availableSize);
+
+ totalWidth += child.DesiredSize.Width;
+ maxHeight = Math.Max(maxHeight, child.DesiredSize.Height);
}
- // Handle DataTable Parent
- else if (_parentTable != null
- && _parentTable.Children.Count == Children.Count)
+
+ return new Size(totalWidth, maxHeight);
+ }
+ // Handle DataTable Parent
+ else
+ {
+ int maxChildCount = Math.Min(_parentTable.Children.Count, Children.Count);
+
+ // Measure all children which have corresponding visible DataColumns.
+ for (int i = 0; i < maxChildCount; i++)
{
- // TODO: Need to check visibility
- // Measure all children since we need to determine the row's height at minimum
- for (int i = 0; i < Children.Count; i++)
+ var child = Children[i];
+ var column = _parentTable.Children[i] as DataColumn;
+ if (column?.Visibility != Visibility.Visible)
+ continue;
+
+ // For TreeView in the first column, we want the header to expand to encompass
+ // the maximum indentation of the tree.
+ //// TODO: We only want/need to do this once? We may want to do if we're not an Auto column too...?
+ if (i == 0 && _isTreeView)
{
- if (_parentTable.Children[i] is DataColumn { CurrentWidth.GridUnitType: GridUnitType.Auto } col)
+ // Get our containing grid from TreeViewItem, start with our indented padding
+ var parentContainer = this.FindAscendant("MultiSelectGrid") as Grid;
+ if (parentContainer != null)
{
- Children[i].Measure(availableSize);
-
- // For TreeView in the first column, we want the header to expand to encompass
- // the maximum indentation of the tree.
- double padding = 0;
- //// TODO: We only want/need to do this once? We may want to do if we're not an Auto column too...?
- if (i == 0 && _isTreeView)
+ TreePadding = parentContainer.Padding.Left;
+ // We assume our 'DataRow' is in the last child slot of the Grid, need to know
+ // how large the other columns are.
+ for (int j = 0; j < parentContainer.Children.Count - 1; j++)
{
- // Get our containing grid from TreeViewItem, start with our indented padding
- var parentContainer = this.FindAscendant("MultiSelectGrid") as Grid;
- if (parentContainer != null)
- {
- _treePadding = parentContainer.Padding.Left;
- // We assume our 'DataRow' is in the last child slot of the Grid, need to know how large the other columns are.
- for (int j = 0; j < parentContainer.Children.Count - 1; j++)
- {
- // TODO: We may need to get the actual size here later in Arrange?
- _treePadding += parentContainer.Children[j].DesiredSize.Width;
- }
- }
- padding = _treePadding;
+ // TODO: We may need to get the actual size here later in Arrange?
+ TreePadding += parentContainer.Children[j].DesiredSize.Width;
}
+ }
+ }
- // TODO: Do we want this to ever shrink back?
- var prev = col.MaxChildDesiredWidth;
- col.MaxChildDesiredWidth = Math.Max(col.MaxChildDesiredWidth, Children[i].DesiredSize.Width + padding);
- if (col.MaxChildDesiredWidth != prev)
- {
- // If our measure has changed, then we have to invalidate the arrange of the DataTable
- _parentTable.ColumnResized();
- }
+ double width = column.ActualCurrentWidth;
- }
- else if (_parentTable.Children[i] is DataColumn { CurrentWidth.GridUnitType: GridUnitType.Pixel } pixel)
- {
- Children[i].Measure(new(pixel.DesiredWidth.Value, availableSize.Height));
- }
- else
+ if (column.IsAutoFit)
+ {
+ // We should get the *required* width from the child.
+ child.Measure(new Size(double.PositiveInfinity, availableSize.Height));
+
+ var childWidth = child.DesiredSize.Width;
+ if (i == 0)
+ childWidth += TreePadding;
+
+ // If the adjusted column width is smaller than the current cell width,
+ // we should call DataTable.MeasureOverride() again to extend it.
+ if (!(width >= childWidth))
{
- Children[i].Measure(availableSize);
+ _parentTable.InvalidateMeasure();
}
-
- maxHeight = Math.Max(maxHeight, Children[i].DesiredSize.Height);
}
- }
- // Fallback for Grid Hybrid scenario...
- else if (_parentPanel is Grid grid
- && _parentPanel.Children.Count == Children.Count
- && grid.ColumnDefinitions.Count == Children.Count)
- {
- // TODO: Need to check visibility
- // Measure all children since we need to determine the row's height at minimum
- for (int i = 0; i < Children.Count; i++)
+ else
{
- if (grid.ColumnDefinitions[i].Width.GridUnitType == GridUnitType.Pixel)
- {
- Children[i].Measure(new(grid.ColumnDefinitions[i].Width.Value, availableSize.Height));
- }
- else
- {
- Children[i].Measure(availableSize);
- }
-
- maxHeight = Math.Max(maxHeight, Children[i].DesiredSize.Height);
+ child.Measure(new Size(width, availableSize.Height));
}
+
+ maxHeight = Math.Max(maxHeight, child.DesiredSize.Height);
}
- // TODO: What do we want to do if there's unequal children in the DataTable vs. DataRow?
- }
- // Otherwise, return our parent's size as the desired size.
- return new(_parentPanel?.DesiredSize.Width ?? availableSize.Width, maxHeight);
+ // Returns the same width as the DataTable requests, regardless of the IsAutoFit column presence.
+ return new Size(_parentTable.DesiredSize.Width, maxHeight);
+ }
}
+ ///
protected override Size ArrangeOverride(Size finalSize)
{
- int column = 0;
- double x = 0;
+ //Debug.WriteLine($"DataRow.ArrangeOverride");
+
+ // If we don't have DataTable, just layout children like a horizontal StackPanel.
+ if (_parentTable is null)
+ {
+ double x = 0;
+
+ for (int i = 0; i < Children.Count; i++)
+ {
+ var child = Children[i];
+ if (child?.Visibility != Visibility.Visible)
+ continue;
- // Try and grab Column Spacing from DataTable, if not a parent Grid, if not 0.
- double spacing = _parentTable?.ColumnSpacing ?? (_parentPanel as Grid)?.ColumnSpacing ?? 0;
+ double width = child.DesiredSize.Width;
- double width = 0;
+ child.Arrange(new Rect(x, 0, width, finalSize.Height));
- if (_parentPanel != null)
+ x += width;
+ }
+
+ return new Size(x, finalSize.Height);
+ }
+ // Handle DataTable Parent
+ else
{
- int i = 0;
- foreach (UIElement child in Children.Where(static e => e.Visibility == Visibility.Visible))
+ int maxChildCount = Math.Min(_parentTable.Children.Count, Children.Count);
+
+ double columnSpacing = _parentTable.ColumnSpacing;
+ double x = double.NaN;
+
+ // Arrange all children which have corresponding visible DataColumns.
+ for (int i = 0; i < maxChildCount; i++)
{
- if (_parentPanel is Grid grid &&
- column < grid.ColumnDefinitions.Count)
- {
- width = grid.ColumnDefinitions[column++].ActualWidth;
- }
- // TODO: Need to check Column visibility here as well...
- else if (_parentPanel is DataTable table &&
- column < table.Children.Count)
- {
- // TODO: This is messy...
- width = (table.Children[column++] as DataColumn)?.ActualWidth ?? 0;
- }
+ var column = _parentTable.Children[i] as DataColumn;
+ if (column?.Visibility != Visibility.Visible)
+ continue;
- // Note: For Auto, since we measured our children and bubbled that up to the DataTable layout, then the DataColumn size we grab above should account for the largest of our children.
- if (i == 0)
- {
- child.Arrange(new Rect(x, 0, width, finalSize.Height));
- }
+ if (double.IsNaN(x))
+ x = 0;
else
- {
- // If we're in a tree, remove the indentation from the layout of columns beyond the first.
- child.Arrange(new Rect(x - _treePadding, 0, width, finalSize.Height));
- }
+ x += columnSpacing;
- x += width + spacing;
- i++;
+ double width = column.ActualCurrentWidth;
+ if (i == 0)
+ width = Math.Max(0, width - TreePadding);
+
+ var child = Children[i];
+ child?.Arrange(new Rect(x, 0, width, finalSize.Height));
+
+ x += width;
}
- return new Size(x - spacing, finalSize.Height);
+ return new Size(x, finalSize.Height);
}
-
- return finalSize;
}
}
diff --git a/components/DataTable/src/DataTable/DataTable.cs b/components/DataTable/src/DataTable/DataTable.cs
index 7b78b2beb..7743718ef 100644
--- a/components/DataTable/src/DataTable/DataTable.cs
+++ b/components/DataTable/src/DataTable/DataTable.cs
@@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Runtime.CompilerServices;
-
namespace CommunityToolkit.WinUI.Controls;
///
@@ -12,15 +10,12 @@ namespace CommunityToolkit.WinUI.Controls;
///
public partial class DataTable : Panel
{
- // TODO: We should cache this result and update if column properties change
- internal bool IsAnyColumnAuto => Children.Any(static e => e is DataColumn { CurrentWidth.GridUnitType: GridUnitType.Auto });
-
// TODO: Check with Sergio if there's a better structure here, as I don't need a Dictionary like ConditionalWeakTable
internal HashSet Rows { get; private set; } = new();
internal void ColumnResized()
{
- InvalidateArrange();
+ InvalidateMeasure();
foreach (var row in Rows)
{
@@ -42,123 +37,174 @@ public double ColumnSpacing
/// Gets the .
///
public static readonly DependencyProperty ColumnSpacingProperty =
- DependencyProperty.Register(nameof(ColumnSpacing), typeof(double), typeof(DataTable), new PropertyMetadata(0d));
+ DependencyProperty.Register(
+ nameof(ColumnSpacing), typeof(double), typeof(DataTable),
+ new PropertyMetadata(0d));
+ ///
protected override Size MeasureOverride(Size availableSize)
{
- double fixedWidth = 0;
- double proportionalUnits = 0;
- double autoSized = 0;
+ //Debug.WriteLine($"DataTable.MeasureOverride");
+
+ int starRemains = 0;
+ double starAmounts = 0;
+ double columnSpacing = ColumnSpacing;
+ double totalWidth = double.NaN;
double maxHeight = 0;
- var elements = Children.Where(static e => e.Visibility == Visibility.Visible && e is DataColumn);
+ bool invokeRowsMeasures = false;
- // We only need to measure elements that are visible
- foreach (DataColumn column in elements)
+ for (int i = 0; i < Children.Count; i++)
{
- if (column.CurrentWidth.IsStar)
- {
- proportionalUnits += column.DesiredWidth.Value;
- }
- else if (column.CurrentWidth.IsAbsolute)
- {
- fixedWidth += column.DesiredWidth.Value;
- }
- }
+ // We only need to measure children that are visible
+ var column = Children[i] as DataColumn;
+ if (column?.Visibility != Visibility.Visible)
+ continue;
- // Add in spacing between columns to our fixed size allotment
- fixedWidth += (elements.Count() - 1) * ColumnSpacing;
+ if (double.IsNaN(totalWidth))
+ totalWidth = 0;
+ else
+ totalWidth += columnSpacing;
- // TODO: Handle infinite width?
- var proportionalAmount = (availableSize.Width - fixedWidth) / proportionalUnits;
+ double width = column.ActualCurrentWidth;
- foreach (DataColumn column in elements)
- {
- if (column.CurrentWidth.IsStar)
+ if (column.IsStarProportion)
{
- column.Measure(new Size(proportionalAmount * column.CurrentWidth.Value, availableSize.Height));
+ ++starRemains;
+ starAmounts += column.DesiredWidth.Value;
+ continue;
}
- else if (column.CurrentWidth.IsAbsolute)
+ else if (column.IsStar)
{
- column.Measure(new Size(column.CurrentWidth.Value, availableSize.Height));
+ starAmounts += column.DesiredWidth.Value;
+ //Debug.WriteLine($" Column[{i}] ({column.DesiredWidth}) width is fixed to: {width}");
+
+ // If availableSize.Width is infinite, the column will also get infinite available width.
+ column.Measure(new Size(width, availableSize.Height));
}
- else
+ else if (column.IsAbsolute)
{
- // TODO: Technically this is using 'Auto' on the Header content
- // What the developer probably intends is it to be adjusted based on the contents of the rows...
- // To enable this scenario, we'll need to actually measure the contents of the rows for that column
- // in DataRow and figure out the maximum size to report back and adjust here in some sort of hand-shake
- // for the layout process... (i.e. get the data in the measure step, use it in the arrange step here,
- // then invalidate the child arranges [don't re-measure and cause loop]...)
+ // column.CurrentWidth is already set in DesiredWidth_PropertyChanged.
+ //Debug.WriteLine($" Column[{i}] ({column.DesiredWidth}) width is fixed to: {width}");
+
+ column.Measure(new Size(width, availableSize.Height));
+ }
+ else // (column.IsAuto)
+ {
+ if (column.IsAutoFit)
+ {
+ // Calculate the best width of the header content.
+ column.Measure(new Size(double.PositiveInfinity, availableSize.Height));
+
+ width = column.DesiredSize.Width;
+ foreach (var row in Rows)
+ {
+ if (i < row.Children.Count)
+ {
+ var child = row.Children[i];
+
+ var childWidth = child.DesiredSize.Width;
+ if (i == 0)
+ childWidth += row.TreePadding;
+
+ width = Math.Max(width, childWidth);
+ }
+ }
+ //Debug.WriteLine($" Column[{i}] ({column.DesiredWidth}) width is adjusted to: {width}");
+
+ // The column width of the corresponding cell in each row
+ // is taken into account in the next layout pass.
+ invokeRowsMeasures = true;
+
+ // Store the calculated column width as a negative value.
+ column.CurrentWidth = -width;
+ }
+ else
+ {
+ //Debug.WriteLine($" Column[{i}] ({column.DesiredWidth}) width is fixed to: {width}");
+
+ column.Measure(new Size(width, availableSize.Height));
+ }
+ }
- // For now, we'll just use the header content as a guideline to see if things work.
+ totalWidth += width;
+ maxHeight = Math.Max(maxHeight, column.DesiredSize.Height);
+ }
- // Avoid negative values when columns don't fit `availableSize`. Otherwise the `Size` constructor will throw.
- column.Measure(new Size(Math.Max(availableSize.Width - fixedWidth - autoSized, 0), availableSize.Height));
+ if (double.IsNaN(totalWidth))
+ return new Size(0, 0);
- // Keep track of already 'allotted' space, use either the maximum child size (if we know it) or the header content
- autoSized += Math.Max(column.DesiredSize.Width, column.MaxChildDesiredWidth);
+ double starUnit = Math.Max(0, availableSize.Width - totalWidth) / starAmounts;
+ for (int i = 0; starRemains != 0; i++)
+ {
+ var column = Children[i] as DataColumn;
+ if (column?.Visibility != Visibility.Visible)
+ continue;
+
+ if (column.IsStarProportion)
+ {
+ --starRemains;
+
+ double width;
+ if (!double.IsInfinity(starUnit) && !double.IsNaN(starUnit))
+ {
+ // If the column width needs to be calculated, get the proportion of the remained space.
+ width = starUnit * column.DesiredWidth.Value;
+
+ column.Measure(new Size(width, availableSize.Height));
+ }
+ else
+ {
+ // If availableSize.Width is infinite, use DesiredSize.Width of the column.
+ column.Measure(new Size(double.PositiveInfinity, availableSize.Height));
+
+ width = column.DesiredSize.Width;
+ }
+ //Debug.WriteLine($" Column[{i}] ({column.DesiredWidth}) width is adjusted to: {width}");
+
+ // Store the calculated column width as a negative value.
+ column.CurrentWidth = -width;
+
+ totalWidth += width;
+ maxHeight = Math.Max(maxHeight, column.DesiredSize.Height);
}
+ }
- maxHeight = Math.Max(maxHeight, column.DesiredSize.Height);
+ if (invokeRowsMeasures)
+ {
+ foreach (var row in Rows)
+ row.InvalidateMeasure();
}
- return new Size(availableSize.Width, maxHeight);
+ return new Size(totalWidth, maxHeight);
}
+ ///
protected override Size ArrangeOverride(Size finalSize)
{
- double fixedWidth = 0;
- double proportionalUnits = 0;
- double autoSized = 0;
+ //Debug.WriteLine($"DataTable.ArrangeOverride");
- var elements = Children.Where(static e => e.Visibility == Visibility.Visible && e is DataColumn);
+ double columnSpacing = ColumnSpacing;
+ double x = double.NaN;
- // We only need to measure elements that are visible
- foreach (DataColumn column in elements)
+ for (int i = 0; i < Children.Count; i++)
{
- if (column.CurrentWidth.IsStar)
- {
- proportionalUnits += column.CurrentWidth.Value;
- }
- else if (column.CurrentWidth.IsAbsolute)
- {
- fixedWidth += column.CurrentWidth.Value;
- }
- else
- {
- autoSized += Math.Max(column.DesiredSize.Width, column.MaxChildDesiredWidth);
- }
- }
+ // We only need to measure children that are visible
+ var column = Children[i] as DataColumn;
+ if (column?.Visibility != Visibility.Visible)
+ continue;
- // TODO: Handle infinite width?
- // TODO: This can go out of bounds or something around here when pushing a resized column to the right...
- var proportionalAmount = (finalSize.Width - fixedWidth - autoSized) / proportionalUnits;
+ if (double.IsNaN(x))
+ x = 0;
+ else
+ x += columnSpacing;
- double width = 0;
- double x = 0;
+ double width = column.ActualCurrentWidth;
- foreach (DataColumn column in elements)
- {
- if (column.CurrentWidth.IsStar)
- {
- width = proportionalAmount * column.CurrentWidth.Value;
- column.Arrange(new Rect(x, 0, width, finalSize.Height));
- }
- else if (column.CurrentWidth.IsAbsolute)
- {
- width = column.CurrentWidth.Value;
- column.Arrange(new Rect(x, 0, width, finalSize.Height));
- }
- else
- {
- // TODO: We use the comparison of sizes a lot, should we cache in the DataColumn itself?
- width = Math.Max(column.DesiredSize.Width, column.MaxChildDesiredWidth);
- column.Arrange(new Rect(x, 0, width, finalSize.Height));
- }
+ column.Arrange(new Rect(x, 0, width, finalSize.Height));
- x += width + ColumnSpacing;
+ x += width;
}
return finalSize;