Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions samples/ControlCatalog/Pages/TableViewPage.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
IsChecked="True"
Margin="8" />

<CheckBox Name="ShowRegionColumn"
Content="Show region column"
IsChecked="True"
Margin="8" />

</StackPanel>

<TableView ItemsSource="{Binding Countries}"
Expand All @@ -48,6 +53,7 @@
Width="{Binding #UseStarSizes.IsChecked, Converter={StaticResource TableViewColumnWidthConverter}, ConverterParameter=3}" />
<TableViewColumn Header="Region"
Binding="{Binding Region}"
IsVisible="{Binding #ShowRegionColumn.IsChecked}"
Width="{Binding #UseStarSizes.IsChecked, Converter={StaticResource TableViewColumnWidthConverter}, ConverterParameter=2}" />
<TableViewColumn Header="Population"
Binding="{Binding Population, StringFormat=N0}"
Expand Down
27 changes: 25 additions & 2 deletions src/Avalonia.Controls/Presenters/TableViewLayoutHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public static bool UpdateActualWidths(

for (var i = 0; i < columns.Count; i++)
{
if (!columns[i].IsVisible)
continue;

var width = columns[i].Width;
if (width.IsAbsolute)
{
Expand Down Expand Up @@ -68,6 +71,9 @@ public static bool UpdateActualWidths(

for (var i = 0; i < columns.Count; i++)
{
if (!columns[i].IsVisible)
continue;

var width = columns[i].Width;
if (!width.IsAbsolute)
{
Expand Down Expand Up @@ -97,12 +103,23 @@ public static bool UpdateActualWidths(
}

public static bool NeedsActualWidths(AvaloniaList<TableViewColumn> columns)
=> columns.Count > 0 && double.IsNaN(columns[0].ActualWidth);
{
foreach (var column in columns)
{
if (column.IsVisible)
return double.IsNaN(column.ActualWidth);
}

return false;
}

public static void ResetActualWidths(AvaloniaList<TableViewColumn> columns)
{
foreach (var column in columns)
column.ActualWidth = double.NaN;
{
if (column.IsVisible)
column.ActualWidth = double.NaN;
}
}

public static Size MeasureRow(AvaloniaList<TableViewColumn> columns, Controls cells, Size availableSize)
Expand All @@ -115,6 +132,9 @@ public static Size MeasureRow(AvaloniaList<TableViewColumn> columns, Controls ce

for (var i = 0; i < cells.Count; i++)
{
if (!columns[i].IsVisible)
continue;

var child = cells[i];
var columnWidth = columns[i].ActualWidth;
child.Measure(new Size(columnWidth, availableSize.Height));
Expand All @@ -133,6 +153,9 @@ public static Size ArrangeRow(AvaloniaList<TableViewColumn> columns, Controls ce
var x = offset;
for (var i = 0; i < cells.Count; i++)
{
if (!columns[i].IsVisible)
continue;

var width = columns[i].ActualWidth;
cells[i].Arrange(new Rect(x, 0, width, finalSize.Height));
x += width;
Expand Down
4 changes: 3 additions & 1 deletion src/Avalonia.Controls/TableView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ private void AttachColumn(TableViewColumn column)
$"The column {column.DebugDisplay} is already attached to a {nameof(TableView)}.");
}

column.TableView = this;
// Resolve styles and bindings before enabling refresh notifications. The headers
// and cells are rebuilt after attachment to apply the column's current values.
((ISetLogicalParent)column).SetParent(this);
column.TableView = this;
}

private void DetachColumn(TableViewColumn column)
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Controls/TableViewCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal set

private void ClearProperties()
{
ClearValue(IsVisibleProperty);
ClearValue(ThemeProperty);
ClearValue(HorizontalContentAlignmentProperty);
ClearValue(ContentTemplateProperty);
Expand All @@ -48,6 +49,7 @@ private void SetProperties(TableViewColumn column)
// Second, we have additional logic depending on whether a cell template is specified.
// Instead, values are updated manually via Refresh().

SetValue(IsVisibleProperty, column.IsVisible);
SetValue(ThemeProperty, column.CellTheme);
SetValue(HorizontalContentAlignmentProperty, column.HorizontalContentAlignment);

Expand Down
26 changes: 22 additions & 4 deletions src/Avalonia.Controls/TableViewColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ namespace Avalonia.Controls;
/// </summary>
public class TableViewColumn : StyledElement, IHeadered
{
/// <summary>
/// Defines the <see cref="IsVisible"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsVisibleProperty =
Visual.IsVisibleProperty.AddOwner<TableViewColumn>();

/// <summary>
/// Defines the <see cref="HeaderTheme"/> property.
/// </summary>
Expand Down Expand Up @@ -87,6 +93,15 @@ public class TableViewColumn : StyledElement, IHeadered
public static readonly DirectProperty<TableViewColumn, bool> CanUserEffectivelyResizeProperty =
AvaloniaProperty.RegisterDirect<TableViewColumn, bool>(nameof(CanUserEffectivelyResize), o => o.CanUserEffectivelyResize);

/// <summary>
/// Gets or sets whether the column is visible. The default is true.
/// </summary>
public bool IsVisible
{
get => GetValue(IsVisibleProperty);
set => SetValue(IsVisibleProperty, value);
}

/// <summary>
/// Gets or sets the theme to apply to the header.
/// It must target <see cref="TableViewColumnHeader"/>.
Expand Down Expand Up @@ -217,22 +232,25 @@ private static bool IsCellProperty(AvaloniaProperty property)
=> property == CellThemeProperty ||
property == CellTemplateProperty ||
property == BindingProperty ||
property == HorizontalContentAlignmentProperty;
property == HorizontalContentAlignmentProperty ||
property == IsVisibleProperty;

private static bool IsHeaderProperty(AvaloniaProperty property)
=> property == HeaderThemeProperty ||
property == HeaderTemplateProperty ||
property == HeaderProperty ||
property == HorizontalContentAlignmentProperty;
property == HorizontalContentAlignmentProperty ||
property == IsVisibleProperty;

/// <inheritdoc />
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

if (change.Property == WidthProperty)
if (change.Property == WidthProperty || change.Property == IsVisibleProperty)
TableView?.OnColumnsSizeChanged();
else if (change.Property == CanUserResizeProperty)

if (change.Property == CanUserResizeProperty)
UpdateCanUserEffectivelyResize();
else
{
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Controls/TableViewColumnHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ internal override void BuildDebugDisplay(StringBuilder builder, bool includeCont

private void ClearProperties()
{
ClearValue(IsVisibleProperty);
ClearValue(ThemeProperty);
ClearValue(HorizontalContentAlignmentProperty);
ClearValue(ContentTemplateProperty);
Expand All @@ -86,6 +87,7 @@ private void ClearProperties()

private void SetProperties(TableViewColumn column)
{
SetValue(IsVisibleProperty, column.IsVisible);
SetOrClearValue(ThemeProperty, column.HeaderTheme);
SetValue(HorizontalContentAlignmentProperty, column.HorizontalContentAlignment);
SetOrClearValue(ContentTemplateProperty, column.HeaderTemplate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,34 @@ public void UpdateActualWidths_Treats_Auto_As_One_Star()
Assert.Equal(100, columns[1].ActualWidth);
}

[Theory]
[InlineData(GridUnitType.Pixel)]
[InlineData(GridUnitType.Star)]
[InlineData(GridUnitType.Auto)]
public void UpdateActualWidths_Excludes_Hidden_Columns_And_Preserves_Their_Widths(GridUnitType unit)
{
var hidden = new TableViewColumn { Width = new GridLength(2, unit) };
var columns = new AvaloniaList<TableViewColumn>
{
hidden,
new() { Width = new GridLength(1, GridUnitType.Star) },
};
TableViewLayoutHelper.UpdateActualWidths(columns, 200, false, 1);
var actualWidth = hidden.ActualWidth;

hidden.IsVisible = false;
TableViewLayoutHelper.UpdateActualWidths(columns, 200, false, 1);

Assert.Equal(new GridLength(2, unit), hidden.Width);
Assert.Equal(actualWidth, hidden.ActualWidth);
Assert.Equal(200, columns[1].ActualWidth);

hidden.IsVisible = true;
TableViewLayoutHelper.UpdateActualWidths(columns, 200, false, 1);

Assert.Equal(actualWidth, hidden.ActualWidth);
}

[Fact]
public void UpdateActualWidths_Falls_Back_To_1000_For_Infinite_Width()
{
Expand Down Expand Up @@ -210,6 +238,29 @@ public void UpdateActualWidths_Spreads_Rounding_At_Fractional_Layout_Scale()
Assert.Equal(100, columns[0].ActualWidth + columns[1].ActualWidth + columns[2].ActualWidth);
}

[Fact]
public void UpdateActualWidths_Ignores_Hidden_Columns()
{
var columns = new AvaloniaList<TableViewColumn>
{
new() { Width = new GridLength(3, GridUnitType.Star) },
new() { Width = new GridLength(100), IsVisible = false },
new() { Width = new GridLength(1, GridUnitType.Star) },
new() { Width = new GridLength(5, GridUnitType.Star), IsVisible = false },
new() { Width = new GridLength(3, GridUnitType.Star) },
};

TableViewLayoutHelper.UpdateActualWidths(columns, 100, useLayoutRounding: true, layoutScale: 2);

// Hidden columns are skipped, so their uncalculated widths remain NaN.
Assert.Equal(43, columns[0].ActualWidth);
Assert.Equal(double.NaN, columns[1].ActualWidth);
Assert.Equal(14, columns[2].ActualWidth);
Assert.Equal(double.NaN, columns[3].ActualWidth);
Assert.Equal(43, columns[4].ActualWidth);
Assert.Equal(100, columns[0].ActualWidth + columns[2].ActualWidth + columns[4].ActualWidth);
}

[Fact]
public void UpdateActualWidths_Rounds_Fixed_Column_Width_And_Star_Absorbs_Remainder()
{
Expand Down Expand Up @@ -259,6 +310,27 @@ public void NeedsActualWidths_Returns_False_After_Widths_Are_Computed()
Assert.False(TableViewLayoutHelper.NeedsActualWidths(columns));
}

[Fact]
public void NeedsActualWidths_Ignores_Hidden_Columns_After_Reset()
{
var columns = new AvaloniaList<TableViewColumn> { new(), new() };
TableViewLayoutHelper.UpdateActualWidths(columns, 200, false, 1);
columns[0].IsVisible = false;

TableViewLayoutHelper.ResetActualWidths(columns);

Assert.Equal(100, columns[0].ActualWidth);
Assert.True(TableViewLayoutHelper.NeedsActualWidths(columns));

TableViewLayoutHelper.UpdateActualWidths(columns, 200, false, 1);

Assert.Equal(200, columns[1].ActualWidth);
Assert.False(TableViewLayoutHelper.NeedsActualWidths(columns));

columns[1].IsVisible = false;
Assert.False(TableViewLayoutHelper.NeedsActualWidths(columns));
}

[Fact]
public void ResetActualWidths_Sets_All_Widths_To_NaN()
{
Expand Down
Loading