Skip to content

Commit 705442b

Browse files
authored
Merge pull request #12088 from workgroupengineering/features/Controls/DataGrid/DataGridColumn_Witdh_Binding_Issue_4326
feat(DataGrid): Allow binding DataGridColumn Witdh
2 parents 14f8914 + a5f615d commit 705442b

File tree

6 files changed

+155
-78
lines changed

6 files changed

+155
-78
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Globalization;
3+
using Avalonia.Data.Converters;
4+
5+
namespace ControlCatalog.Models;
6+
7+
internal class GDPdLengthConverter : IValueConverter
8+
{
9+
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
10+
{
11+
if (value is double d)
12+
{
13+
return new Avalonia.Controls.DataGridLength(d,Avalonia.Controls.DataGridLengthUnitType.Pixel,d,d);
14+
}
15+
else if (value is decimal d2)
16+
{
17+
var dv =System.Convert.ToDouble(d2);
18+
return new Avalonia.Controls.DataGridLength(dv, Avalonia.Controls.DataGridLengthUnitType.Pixel, dv, dv);
19+
}
20+
return value;
21+
}
22+
23+
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
24+
{
25+
if (value is Avalonia.Controls.DataGridLength width)
26+
{
27+
return System.Convert.ToDecimal(width.DisplayValue);
28+
}
29+
return value;
30+
}
31+
}

samples/ControlCatalog/Pages/DataGridPage.xaml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
<UserControl xmlns="https://github.com/avaloniaui"
22
xmlns:local="using:ControlCatalog.Models"
3+
xmlns:lc="using:ControlCatalog.Converter"
34
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
45
xmlns:pages="clr-namespace:ControlCatalog.Pages"
56
x:Class="ControlCatalog.Pages.DataGridPage"
67
x:DataType="pages:DataGridPage">
78
<UserControl.Resources>
9+
810
<local:GDPValueConverter x:Key="GDPConverter" />
11+
<local:GDPdLengthConverter x:Key="GDPWidthConverter"/>
912
<DataTemplate x:Key="Demo.DataTemplates.CountryHeader" x:DataType="local:Country">
1013
<StackPanel Orientation="Horizontal" Spacing="5">
1114
<PathIcon Height="12" Data="M 255 116 A 1 1 0 0 0 254 117 L 254 130 A 1 1 0 0 0 255 131 A 1 1 0 0 0 256 130 L 256 123.87109 C 256.1125 123.90694 256.2187 123.94195 256.33984 123.97852 C 257.18636 124.23404 258.19155 124.5 259 124.5 C 259.80845 124.5 260.52133 124.2168 261.17773 123.9668 C 261.83414 123.7168 262.43408 123.5 263 123.5 C 263.56592 123.5 264.5612 123.73404 265.37109 123.97852 C 266.18098 124.22299 266.82227 124.4668 266.82227 124.4668 A 0.50005 0.50005 0 0 0 267.5 124 L 267.5 118 A 0.50005 0.50005 0 0 0 267.17773 117.5332 C 267.17773 117.5332 266.50667 117.27701 265.66016 117.02148 C 264.81364 116.76596 263.80845 116.5 263 116.5 C 262.19155 116.5 261.47867 116.7832 260.82227 117.0332 C 260.16586 117.2832 259.56592 117.5 259 117.5 C 258.43408 117.5 257.4388 117.26596 256.62891 117.02148 C 256.39123 116.94974 256.17716 116.87994 255.98047 116.81445 A 1 1 0 0 0 255 116 z M 263 117.5 C 263.56592 117.5 264.5612 117.73404 265.37109 117.97852 C 266.00097 118.16865 266.29646 118.28239 266.5 118.35742 L 266.5 120.29297 C 266.25708 120.21012 265.97978 120.11797 265.66016 120.02148 C 264.81364 119.76596 263.80845 119.5 263 119.5 C 262.19155 119.5 261.47867 119.7832 260.82227 120.0332 C 260.16586 120.2832 259.56592 120.5 259 120.5 C 258.43408 120.5 257.4388 120.26596 256.62891 120.02148 C 256.39971 119.9523 256.19148 119.88388 256 119.82031 L 256 117.87109 C 256.1125 117.90694 256.2187 117.94195 256.33984 117.97852 C 257.18636 118.23404 258.19155 118.5 259 118.5 C 259.80845 118.5 260.52133 118.2168 261.17773 117.9668 C 261.83414 117.7168 262.43408 117.5 263 117.5 z M 263 120.5 C 263.56592 120.5 264.5612 120.73404 265.37109 120.97852 C 265.8714 121.12954 266.2398 121.25641 266.5 121.34961 L 266.5 123.30469 C 266.22286 123.20649 266.12863 123.1629 265.66016 123.02148 C 264.81364 122.76596 263.80845 122.5 263 122.5 C 262.19155 122.5 261.47867 122.7832 260.82227 123.0332 C 260.16586 123.2832 259.56592 123.5 259 123.5 C 258.43408 123.5 257.4388 123.26596 256.62891 123.02148 C 256.39971 122.9523 256.19148 122.88388 256 122.82031 L 256 120.87109 C 256.1125 120.90694 256.2187 120.94195 256.33984 120.97852 C 257.18636 121.23404 258.19155 121.5 259 121.5 C 259.80845 121.5 260.52133 121.2168 261.17773 120.9668 C 261.83414 120.7168 262.43408 120.5 263 120.5 z" />
@@ -28,8 +31,18 @@
2831
<TabControl Grid.Row="2">
2932
<TabItem Header="DataGrid">
3033
<DockPanel>
31-
<CheckBox x:Name="ShowGDP" IsChecked="True" Content="Toggle GDP Column Visibility"
32-
DockPanel.Dock="Top"/>
34+
<StackPanel Orientation="Horizontal"
35+
DockPanel.Dock="Top"
36+
Spacing="5">
37+
<CheckBox x:Name="ShowGDP" IsChecked="True" Content="Toggle GDP Column Visibility"/>
38+
<TextBlock Text="GDP Width:" VerticalAlignment="Center"/>
39+
<NumericUpDown x:Name="GDPWidth"
40+
Minimum="200"
41+
Maximum="350"
42+
Width="200"
43+
Increment="10"
44+
Value="200"/>
45+
</StackPanel>
3346
<DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"
3447
RowBackground="#1000">
3548
<DataGrid.Columns>
@@ -38,9 +51,11 @@
3851
<DataGridTextColumn Header="Region" Binding="{Binding Region}" Width="4*" x:DataType="local:Country" />
3952
<DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" x:DataType="local:Country" />
4053
<DataGridTextColumn Header="Area" Binding="{Binding Area}" Width="3*" x:DataType="local:Country" />
41-
<DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*"
54+
<DataGridTextColumn Header="GDP" Binding="{Binding GDP}"
55+
Width="{Binding #GDPWidth.Value, Mode=TwoWay, Converter={StaticResource GDPWidthConverter}}"
4256
CellTheme="{StaticResource GdpCell}"
4357
MinWidth="200"
58+
MaxWidth="350"
4459
IsVisible="{Binding #ShowGDP.IsChecked}"
4560
x:DataType="local:Country" />
4661
</DataGrid.Columns>

src/Avalonia.Controls.DataGrid/DataGridColumn.cs

Lines changed: 90 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Avalonia.Interactivity;
88
using Avalonia.VisualTree;
99
using Avalonia.Collections;
10-
using Avalonia.Utilities;
1110
using System;
1211
using System.ComponentModel;
1312
using System.Linq;
@@ -24,8 +23,6 @@ public abstract class DataGridColumn : AvaloniaObject
2423
{
2524
internal const int DATAGRIDCOLUMN_maximumWidth = 65536;
2625
private const bool DATAGRIDCOLUMN_defaultIsReadOnly = false;
27-
28-
private DataGridLength? _width; // Null by default, null means inherit the Width from the DataGrid
2926
private bool? _isReadOnly;
3027
private double? _maxWidth;
3128
private double? _minWidth;
@@ -39,6 +36,7 @@ public abstract class DataGridColumn : AvaloniaObject
3936
private IBinding _clipboardContentBinding;
4037
private ControlTheme _cellTheme;
4138
private Classes _cellStyleClasses;
39+
private bool _setWidthInternalNoCallback;
4240

4341
/// <summary>
4442
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumn" /> class.
@@ -214,6 +212,36 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
214212
OwningGrid?.OnColumnVisibleStateChanged(this);
215213
NotifyPropertyChanged(change.Property.Name);
216214
}
215+
else if (change.Property == WidthProperty)
216+
{
217+
if (!_settingWidthInternally)
218+
{
219+
InheritsWidth = false;
220+
}
221+
if (_setWidthInternalNoCallback == false)
222+
{
223+
var grid = OwningGrid;
224+
var width = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).NewValue.Value;
225+
if (grid != null)
226+
{
227+
var oldWidth = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).OldValue.Value;
228+
if (width.IsStar != oldWidth.IsStar)
229+
{
230+
SetWidthInternalNoCallback(width);
231+
IsInitialDesiredWidthDetermined = false;
232+
grid.OnColumnWidthChanged(this);
233+
}
234+
else
235+
{
236+
Resize(oldWidth, width, false);
237+
}
238+
}
239+
else
240+
{
241+
SetWidthInternalNoCallback(width);
242+
}
243+
}
244+
}
217245
}
218246

219247

@@ -549,48 +577,15 @@ public double MinWidth
549577
}
550578
}
551579

580+
public static readonly StyledProperty<DataGridLength> WidthProperty = AvaloniaProperty
581+
.Register<DataGridColumn, DataGridLength>(nameof(Width)
582+
, coerce: CoerceWidth
583+
);
584+
552585
public DataGridLength Width
553586
{
554-
get
555-
{
556-
return
557-
_width ??
558-
OwningGrid?.ColumnWidth ??
559-
// We don't have a good choice here because we don't want to make this property nullable, see DevDiv Bugs 196581
560-
DataGridLength.Auto;
561-
}
562-
set
563-
{
564-
if (!_width.HasValue || _width.Value != value)
565-
{
566-
if (!_settingWidthInternally)
567-
{
568-
InheritsWidth = false;
569-
}
570-
571-
if (OwningGrid != null)
572-
{
573-
DataGridLength width = CoerceWidth(value);
574-
if (width.IsStar != Width.IsStar)
575-
{
576-
// If a column has changed either from or to a star value, we want to recalculate all
577-
// star column widths. They are recalculated during Measure based off what the value we set here.
578-
SetWidthInternalNoCallback(width);
579-
IsInitialDesiredWidthDetermined = false;
580-
OwningGrid.OnColumnWidthChanged(this);
581-
}
582-
else
583-
{
584-
// If a column width's value is simply changing, we resize it (to the right only).
585-
Resize(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue, false);
586-
}
587-
}
588-
else
589-
{
590-
SetWidthInternalNoCallback(value);
591-
}
592-
}
593-
}
587+
get => this.GetValue(WidthProperty);
588+
set => SetValue(WidthProperty, value);
594589
}
595590

596591
/// <summary>
@@ -812,19 +807,34 @@ internal void EndCellEditInternal()
812807
/// on the rest of the star columns. For pixel widths, the desired value is based on the pixel value.
813808
/// For auto widths, the desired value is initialized as the column's minimum width.
814809
/// </summary>
810+
/// <param name="source"></param>
815811
/// <param name="width">The DataGridLength to coerce.</param>
816812
/// <returns>The resultant (coerced) DataGridLength.</returns>
817-
internal DataGridLength CoerceWidth(DataGridLength width)
813+
static DataGridLength CoerceWidth(AvaloniaObject source, DataGridLength width)
818814
{
815+
var target = (DataGridColumn)source;
816+
817+
if (target._setWidthInternalNoCallback)
818+
{
819+
return width;
820+
}
821+
822+
if (!target.IsSet(WidthProperty))
823+
{
824+
825+
return target.OwningGrid?.ColumnWidth ??
826+
DataGridLength.Auto;
827+
}
828+
819829
double desiredValue = width.DesiredValue;
820830
if (double.IsNaN(desiredValue))
821831
{
822-
if (width.IsStar && OwningGrid != null && OwningGrid.ColumnsInternal != null)
832+
if (width.IsStar && target.OwningGrid != null && target.OwningGrid.ColumnsInternal != null)
823833
{
824834
double totalStarValues = 0;
825835
double totalStarDesiredValues = 0;
826836
double totalNonStarDisplayWidths = 0;
827-
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != this && !double.IsNaN(c.Width.DesiredValue)))
837+
foreach (DataGridColumn column in target.OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != target && !double.IsNaN(c.Width.DesiredValue)))
828838
{
829839
if (column.Width.IsStar)
830840
{
@@ -839,7 +849,7 @@ internal DataGridLength CoerceWidth(DataGridLength width)
839849
if (totalStarValues == 0)
840850
{
841851
// Compute the new star column's desired value based on the available space if there are no other visible star columns
842-
desiredValue = Math.Max(ActualMinWidth, OwningGrid.CellsWidth - totalNonStarDisplayWidths);
852+
desiredValue = Math.Max(target.ActualMinWidth, target.OwningGrid.CellsWidth - totalNonStarDisplayWidths);
843853
}
844854
else
845855
{
@@ -853,7 +863,7 @@ internal DataGridLength CoerceWidth(DataGridLength width)
853863
}
854864
else
855865
{
856-
desiredValue = ActualMinWidth;
866+
desiredValue = target.ActualMinWidth;
857867
}
858868
}
859869

@@ -862,7 +872,7 @@ internal DataGridLength CoerceWidth(DataGridLength width)
862872
{
863873
displayValue = desiredValue;
864874
}
865-
displayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, displayValue));
875+
displayValue = Math.Max(target.ActualMinWidth, Math.Min(target.ActualMaxWidth, displayValue));
866876

867877
return new DataGridLength(width.Value, width.UnitType, desiredValue, displayValue);
868878
}
@@ -896,7 +906,7 @@ internal virtual DataGridColumnHeader CreateHeader()
896906
};
897907
result[!ContentControl.ContentProperty] = this[!HeaderProperty];
898908
result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty];
899-
if (OwningGrid.ColumnHeaderTheme is {} columnTheme)
909+
if (OwningGrid.ColumnHeaderTheme is { } columnTheme)
900910
{
901911
result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.Template);
902912
}
@@ -909,7 +919,7 @@ internal virtual DataGridColumnHeader CreateHeader()
909919
/// </summary>
910920
internal void EnsureWidth()
911921
{
912-
SetWidthInternalNoCallback(CoerceWidth(Width));
922+
SetWidthInternalNoCallback(CoerceWidth(this, Width));
913923
}
914924

915925
internal Control GenerateElementInternal(DataGridCell cell, object dataItem)
@@ -931,17 +941,17 @@ internal object PrepareCellForEditInternal(Control editingElement, RoutedEventAr
931941
/// can only decrease in size by the amount that the columns after it can increase in size.
932942
/// Likewise, the column can only increase in size if other columns can spare the width.
933943
/// </summary>
934-
/// <param name="value">The new Value.</param>
935-
/// <param name="unitType">The new UnitType.</param>
936-
/// <param name="desiredValue">The new DesiredValue.</param>
937-
/// <param name="displayValue">The new DisplayValue.</param>
944+
/// <param name="oldWidth">with before resize.</param>
945+
/// <param name="newWidth">with after resize.</param>
938946
/// <param name="userInitiated">Whether or not this resize was initiated by a user action.</param>
939-
internal void Resize(double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue, bool userInitiated)
947+
948+
// double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue
949+
internal void Resize(DataGridLength oldWidth, DataGridLength newWidth, bool userInitiated)
940950
{
941-
double newValue = value;
942-
double newDesiredValue = desiredValue;
943-
double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, displayValue));
944-
DataGridLengthUnitType newUnitType = unitType;
951+
double newValue = newWidth.Value;
952+
double newDesiredValue = newWidth.DesiredValue;
953+
double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, newWidth.DisplayValue));
954+
DataGridLengthUnitType newUnitType = newWidth.UnitType;
945955

946956
int starColumnsCount = 0;
947957
double totalDisplayWidth = 0;
@@ -955,11 +965,11 @@ internal void Resize(double value, DataGridLengthUnitType unitType, double desir
955965

956966
// If we're using star sizing, we can only resize the column as much as the columns to the
957967
// right will allow (i.e. until they hit their max or min widths).
958-
if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (unitType == DataGridLengthUnitType.Star && Width.IsStar && userInitiated)))
968+
if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (newUnitType == DataGridLengthUnitType.Star && newWidth.IsStar && userInitiated)))
959969
{
960-
double limitedDisplayValue = Width.DisplayValue;
970+
double limitedDisplayValue = oldWidth.DisplayValue;
961971
double availableIncrease = Math.Max(0, OwningGrid.CellsWidth - totalDisplayWidth);
962-
double desiredChange = newDisplayValue - Width.DisplayValue;
972+
double desiredChange = newDisplayValue - oldWidth.DisplayValue;
963973
if (desiredChange > availableIncrease)
964974
{
965975
// The desired change is greater than the amount of available space,
@@ -979,7 +989,7 @@ internal void Resize(double value, DataGridLengthUnitType unitType, double desir
979989
// The desired change is negative, so we need to increase the widths of columns to the right.
980990
limitedDisplayValue += desiredChange + OwningGrid.IncreaseColumnWidths(DisplayIndex + 1, -desiredChange, userInitiated);
981991
}
982-
if (ActualCanUserResize || (Width.IsStar && !userInitiated))
992+
if (ActualCanUserResize || (oldWidth.IsStar && !userInitiated))
983993
{
984994
newDisplayValue = limitedDisplayValue;
985995
}
@@ -1002,9 +1012,10 @@ internal void Resize(double value, DataGridLengthUnitType unitType, double desir
10021012
}
10031013
}
10041014

1005-
DataGridLength oldWidth = Width;
1006-
SetWidthInternalNoCallback(new DataGridLength(Math.Min(double.MaxValue, newValue), newUnitType, newDesiredValue, newDisplayValue));
1007-
if (Width != oldWidth)
1015+
newDisplayValue = Math.Min(double.MaxValue, newValue);
1016+
newWidth = new DataGridLength(newDisplayValue, newUnitType, newDesiredValue, newDisplayValue);
1017+
SetWidthInternalNoCallback(newWidth);
1018+
if (newWidth != oldWidth)
10081019
{
10091020
OwningGrid.OnColumnWidthChanged(this);
10101021
}
@@ -1052,7 +1063,17 @@ internal void SetWidthInternal(DataGridLength width)
10521063
/// <param name="width">The new Width.</param>
10531064
internal void SetWidthInternalNoCallback(DataGridLength width)
10541065
{
1055-
_width = width;
1066+
var originalValue = _setWidthInternalNoCallback;
1067+
_setWidthInternalNoCallback = true;
1068+
try
1069+
{
1070+
Width = width;
1071+
}
1072+
finally
1073+
{
1074+
_setWidthInternalNoCallback = originalValue;
1075+
}
1076+
10561077
}
10571078

10581079
/// <summary>
@@ -1122,7 +1143,7 @@ internal DataGridSortDescription GetSortDescription()
11221143
&& OwningGrid.DataConnection != null
11231144
&& OwningGrid.DataConnection.SortDescriptions != null)
11241145
{
1125-
if(CustomSortComparer != null)
1146+
if (CustomSortComparer != null)
11261147
{
11271148
return
11281149
OwningGrid.DataConnection.SortDescriptions

0 commit comments

Comments
 (0)