Browse Source

feat(DataGrid): Allow binding DataGridColumn Width

pull/12088/head
Giuseppe Lippolis 5 years ago
parent
commit
ed6331cd3d
  1. 159
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  2. 4
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  3. 17
      src/Avalonia.Controls.DataGrid/DataGridColumns.cs
  4. 1
      src/Avalonia.Controls.DataGrid/DataGridLength.cs

159
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -7,7 +7,6 @@ using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
using Avalonia.Collections;
using Avalonia.Utilities;
using System;
using System.ComponentModel;
using System.Linq;
@ -24,8 +23,6 @@ namespace Avalonia.Controls
{
internal const int DATAGRIDCOLUMN_maximumWidth = 65536;
private const bool DATAGRIDCOLUMN_defaultIsReadOnly = false;
private DataGridLength? _width; // Null by default, null means inherit the Width from the DataGrid
private bool? _isReadOnly;
private double? _maxWidth;
private double? _minWidth;
@ -39,6 +36,7 @@ namespace Avalonia.Controls
private IBinding _clipboardContentBinding;
private ControlTheme _cellTheme;
private Classes _cellStyleClasses;
private bool _setWidthInternalNoCallback;
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumn" /> class.
@ -214,6 +212,36 @@ namespace Avalonia.Controls
OwningGrid?.OnColumnVisibleStateChanged(this);
NotifyPropertyChanged(change.Property.Name);
}
else if (change.Property == WidthProperty)
{
if (!_settingWidthInternally)
{
InheritsWidth = false;
}
if (_setWidthInternalNoCallback == false)
{
var grid = OwningGrid;
var width = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).NewValue.Value;
if (grid != null)
{
var oldWidth = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).OldValue.Value;
if (width.IsStar != oldWidth.IsStar)
{
SetWidthInternalNoCallback(width);
IsInitialDesiredWidthDetermined = false;
grid.OnColumnWidthChanged(this);
}
else
{
Resize(oldWidth, width, false);
}
}
else
{
SetWidthInternalNoCallback(width);
}
}
}
}
@ -549,48 +577,15 @@ namespace Avalonia.Controls
}
}
public static readonly StyledProperty<DataGridLength> WidthProperty = AvaloniaProperty
.Register<DataGridColumn, DataGridLength>(nameof(Width)
, coerce: CoerceWidth
);
public DataGridLength Width
{
get
{
return
_width ??
OwningGrid?.ColumnWidth ??
// We don't have a good choice here because we don't want to make this property nullable, see DevDiv Bugs 196581
DataGridLength.Auto;
}
set
{
if (!_width.HasValue || _width.Value != value)
{
if (!_settingWidthInternally)
{
InheritsWidth = false;
}
if (OwningGrid != null)
{
DataGridLength width = CoerceWidth(value);
if (width.IsStar != Width.IsStar)
{
// If a column has changed either from or to a star value, we want to recalculate all
// star column widths. They are recalculated during Measure based off what the value we set here.
SetWidthInternalNoCallback(width);
IsInitialDesiredWidthDetermined = false;
OwningGrid.OnColumnWidthChanged(this);
}
else
{
// If a column width's value is simply changing, we resize it (to the right only).
Resize(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue, false);
}
}
else
{
SetWidthInternalNoCallback(value);
}
}
}
get => this.GetValue(WidthProperty);
set => SetValue(WidthProperty, value);
}
/// <summary>
@ -812,19 +807,34 @@ namespace Avalonia.Controls
/// on the rest of the star columns. For pixel widths, the desired value is based on the pixel value.
/// For auto widths, the desired value is initialized as the column's minimum width.
/// </summary>
/// <param name="source"></param>
/// <param name="width">The DataGridLength to coerce.</param>
/// <returns>The resultant (coerced) DataGridLength.</returns>
internal DataGridLength CoerceWidth(DataGridLength width)
static DataGridLength CoerceWidth(AvaloniaObject source, DataGridLength width)
{
var target = (DataGridColumn)source;
if (target._setWidthInternalNoCallback)
{
return width;
}
if (!target.IsSet(WidthProperty))
{
return target.OwningGrid?.ColumnWidth ??
DataGridLength.Auto;
}
double desiredValue = width.DesiredValue;
if (double.IsNaN(desiredValue))
{
if (width.IsStar && OwningGrid != null && OwningGrid.ColumnsInternal != null)
if (width.IsStar && target.OwningGrid != null && target.OwningGrid.ColumnsInternal != null)
{
double totalStarValues = 0;
double totalStarDesiredValues = 0;
double totalNonStarDisplayWidths = 0;
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != this && !double.IsNaN(c.Width.DesiredValue)))
foreach (DataGridColumn column in target.OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != target && !double.IsNaN(c.Width.DesiredValue)))
{
if (column.Width.IsStar)
{
@ -839,7 +849,7 @@ namespace Avalonia.Controls
if (totalStarValues == 0)
{
// Compute the new star column's desired value based on the available space if there are no other visible star columns
desiredValue = Math.Max(ActualMinWidth, OwningGrid.CellsWidth - totalNonStarDisplayWidths);
desiredValue = Math.Max(target.ActualMinWidth, target.OwningGrid.CellsWidth - totalNonStarDisplayWidths);
}
else
{
@ -853,7 +863,7 @@ namespace Avalonia.Controls
}
else
{
desiredValue = ActualMinWidth;
desiredValue = target.ActualMinWidth;
}
}
@ -862,7 +872,7 @@ namespace Avalonia.Controls
{
displayValue = desiredValue;
}
displayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, displayValue));
displayValue = Math.Max(target.ActualMinWidth, Math.Min(target.ActualMaxWidth, displayValue));
return new DataGridLength(width.Value, width.UnitType, desiredValue, displayValue);
}
@ -896,7 +906,7 @@ namespace Avalonia.Controls
};
result[!ContentControl.ContentProperty] = this[!HeaderProperty];
result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty];
if (OwningGrid.ColumnHeaderTheme is {} columnTheme)
if (OwningGrid.ColumnHeaderTheme is { } columnTheme)
{
result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.Template);
}
@ -909,7 +919,7 @@ namespace Avalonia.Controls
/// </summary>
internal void EnsureWidth()
{
SetWidthInternalNoCallback(CoerceWidth(Width));
SetWidthInternalNoCallback(CoerceWidth(this, Width));
}
internal Control GenerateElementInternal(DataGridCell cell, object dataItem)
@ -931,17 +941,17 @@ namespace Avalonia.Controls
/// can only decrease in size by the amount that the columns after it can increase in size.
/// Likewise, the column can only increase in size if other columns can spare the width.
/// </summary>
/// <param name="value">The new Value.</param>
/// <param name="unitType">The new UnitType.</param>
/// <param name="desiredValue">The new DesiredValue.</param>
/// <param name="displayValue">The new DisplayValue.</param>
/// <param name="oldWidth">with before resize.</param>
/// <param name="newWidth">with after resize.</param>
/// <param name="userInitiated">Whether or not this resize was initiated by a user action.</param>
internal void Resize(double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue, bool userInitiated)
// double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue
internal void Resize(DataGridLength oldWidth, DataGridLength newWidth, bool userInitiated)
{
double newValue = value;
double newDesiredValue = desiredValue;
double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, displayValue));
DataGridLengthUnitType newUnitType = unitType;
double newValue = newWidth.Value;
double newDesiredValue = newWidth.DesiredValue;
double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, newWidth.DisplayValue));
DataGridLengthUnitType newUnitType = newWidth.UnitType;
int starColumnsCount = 0;
double totalDisplayWidth = 0;
@ -955,11 +965,11 @@ namespace Avalonia.Controls
// If we're using star sizing, we can only resize the column as much as the columns to the
// right will allow (i.e. until they hit their max or min widths).
if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (unitType == DataGridLengthUnitType.Star && Width.IsStar && userInitiated)))
if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (newUnitType == DataGridLengthUnitType.Star && newWidth.IsStar && userInitiated)))
{
double limitedDisplayValue = Width.DisplayValue;
double limitedDisplayValue = oldWidth.DisplayValue;
double availableIncrease = Math.Max(0, OwningGrid.CellsWidth - totalDisplayWidth);
double desiredChange = newDisplayValue - Width.DisplayValue;
double desiredChange = newDisplayValue - oldWidth.DisplayValue;
if (desiredChange > availableIncrease)
{
// The desired change is greater than the amount of available space,
@ -979,7 +989,7 @@ namespace Avalonia.Controls
// The desired change is negative, so we need to increase the widths of columns to the right.
limitedDisplayValue += desiredChange + OwningGrid.IncreaseColumnWidths(DisplayIndex + 1, -desiredChange, userInitiated);
}
if (ActualCanUserResize || (Width.IsStar && !userInitiated))
if (ActualCanUserResize || (oldWidth.IsStar && !userInitiated))
{
newDisplayValue = limitedDisplayValue;
}
@ -1002,9 +1012,10 @@ namespace Avalonia.Controls
}
}
DataGridLength oldWidth = Width;
SetWidthInternalNoCallback(new DataGridLength(Math.Min(double.MaxValue, newValue), newUnitType, newDesiredValue, newDisplayValue));
if (Width != oldWidth)
newDisplayValue = Math.Min(double.MaxValue, newValue);
newWidth = new DataGridLength(newDisplayValue, newUnitType, newDesiredValue, newDisplayValue);
SetWidthInternalNoCallback(newWidth);
if (newWidth != oldWidth)
{
OwningGrid.OnColumnWidthChanged(this);
}
@ -1052,7 +1063,17 @@ namespace Avalonia.Controls
/// <param name="width">The new Width.</param>
internal void SetWidthInternalNoCallback(DataGridLength width)
{
_width = width;
var originalValue = _setWidthInternalNoCallback;
_setWidthInternalNoCallback = true;
try
{
Width = width;
}
finally
{
_setWidthInternalNoCallback = originalValue;
}
}
/// <summary>
@ -1122,7 +1143,7 @@ namespace Avalonia.Controls
&& OwningGrid.DataConnection != null
&& OwningGrid.DataConnection.SortDescriptions != null)
{
if(CustomSortComparer != null)
if (CustomSortComparer != null)
{
return
OwningGrid.DataConnection.SortDescriptions

4
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -785,7 +785,9 @@ namespace Avalonia.Controls
double desiredWidth = _originalWidth + mouseDelta;
desiredWidth = Math.Max(_dragColumn.ActualMinWidth, Math.Min(_dragColumn.ActualMaxWidth, desiredWidth));
_dragColumn.Resize(_dragColumn.Width.Value, _dragColumn.Width.UnitType, _dragColumn.Width.DesiredValue, desiredWidth, true);
_dragColumn.Resize(_dragColumn.Width,
new(_dragColumn.Width.Value, _dragColumn.Width.UnitType, _dragColumn.Width.DesiredValue, desiredWidth),
true);
OwningGrid.UpdateHorizontalOffset(_originalHorizontalOffset);

17
src/Avalonia.Controls.DataGrid/DataGridColumns.cs

@ -91,7 +91,10 @@ namespace Avalonia.Controls
// this column is newly added, we'll just set its display value directly.
if (UsesStarSizing && column.IsInitialDesiredWidthDetermined)
{
column.Resize(column.Width.Value, column.Width.UnitType, desiredWidth, desiredWidth, false);
var oldWidth = column.Width;
column.Resize(oldWidth,
new(column.Width.Value, column.Width.UnitType, desiredWidth, desiredWidth),
false);
}
else
{
@ -142,7 +145,7 @@ namespace Avalonia.Controls
{
Debug.Assert(dataGridColumn != null);
if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn &&
if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn &&
dataGridBoundColumn.Binding is BindingBase binding)
{
var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString();
@ -359,6 +362,7 @@ namespace Avalonia.Controls
if (column.IsVisible && oldValue != column.ActualMaxWidth)
{
DataGridLength oldWitdh = new(oldValue, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue);
if (column.ActualMaxWidth < column.Width.DisplayValue)
{
// If the maximum width has caused the column to decrease in size, try first to resize
@ -371,7 +375,9 @@ namespace Avalonia.Controls
{
// If the column was previously limited by its maximum value but has more room now,
// attempt to resize the column to its desired width.
column.Resize(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue, false);
column.Resize(oldWitdh,
new (column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue),
false);
}
OnColumnWidthChanged(column);
}
@ -388,6 +394,7 @@ namespace Avalonia.Controls
if (column.IsVisible && oldValue != column.ActualMinWidth)
{
DataGridLength oldWitdh = new(oldValue, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue);
if (column.ActualMinWidth > column.Width.DisplayValue)
{
// If the minimum width has caused the column to increase in size, try first to resize
@ -400,7 +407,9 @@ namespace Avalonia.Controls
{
// If the column was previously limited by its minimum value but but can be smaller now,
// attempt to resize the column to its desired width.
column.Resize(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue, false);
column.Resize(oldWitdh,
new(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue),
false);
}
OnColumnWidthChanged(column);
}

1
src/Avalonia.Controls.DataGrid/DataGridLength.cs

@ -7,7 +7,6 @@ using Avalonia.Utilities;
using System;
using System.ComponentModel;
using System.Globalization;
using Avalonia.Controls.Utils;
namespace Avalonia.Controls
{

Loading…
Cancel
Save