Skip to content

Commit

Permalink
Merge pull request #19 from column-auto-sizing
Browse files Browse the repository at this point in the history
implemented column auto sizing
  • Loading branch information
w-ahmad authored Jun 5, 2024
2 parents b01820f + 5f25104 commit 5bd8eb0
Show file tree
Hide file tree
Showing 12 changed files with 342 additions and 90 deletions.
39 changes: 39 additions & 0 deletions src/WinUI.TableView/TableView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
namespace WinUI.TableView;
public class TableView : ListView
{
private TableViewHeaderRow? _headerRow;

public TableView()
{
DefaultStyleKey = typeof(TableView);
Expand All @@ -36,6 +38,13 @@ public TableView()
Loaded += OnLoaded;
}

protected override void OnApplyTemplate()
{
base.OnApplyTemplate();

_headerRow = GetTemplateChild("HeaderRow") as TableViewHeaderRow;
}

private void OnLoaded(object sender, RoutedEventArgs e)
{
if (GetTemplateChild("ScrollViewer") is not ScrollViewer scrollViewer)
Expand Down Expand Up @@ -382,6 +391,22 @@ private static void OnCanFilterColumnsChanged(DependencyObject d, DependencyProp
}
}

private static void OnMinColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableView table && table._headerRow is not null)
{
table._headerRow.CalculateHeaderWidths();
}
}

private static void OnMaxColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableView table && table._headerRow is not null)
{
table._headerRow.CalculateHeaderWidths();
}
}

private void UpdateVerticalScrollBarMargin()
{
if (GetTemplateChild("ScrollViewer") is ScrollViewer scrollViewer)
Expand Down Expand Up @@ -493,6 +518,18 @@ public bool CanFilterColumns
set => SetValue(CanFilterColumnsProperty, value);
}

public double MinColumnWidth
{
get => (double)GetValue(MinColumnWidthProperty);
set => SetValue(MinColumnWidthProperty, value);
}

public double MaxColumnWidth
{
get => (double)GetValue(MaxColumnWidthProperty);
set => SetValue(MaxColumnWidthProperty, value);
}

public static readonly new DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(IList), typeof(TableView), new PropertyMetadata(null, OnItemsSourceChanged));
public static readonly DependencyProperty HeaderRowHeightProperty = DependencyProperty.Register(nameof(HeaderRowHeight), typeof(double), typeof(TableView), new PropertyMetadata(32d, OnHeaderRowHeightChanged));
public static readonly DependencyProperty RowHeightProperty = DependencyProperty.Register(nameof(RowHeight), typeof(double), typeof(TableView), new PropertyMetadata(40d));
Expand All @@ -504,6 +541,8 @@ public bool CanFilterColumns
public static readonly DependencyProperty CanResizeColumnsProperty = DependencyProperty.Register(nameof(CanResizeColumns), typeof(bool), typeof(TableView), new PropertyMetadata(true));
public static readonly DependencyProperty CanSortColumnsProperty = DependencyProperty.Register(nameof(CanSortColumns), typeof(bool), typeof(TableView), new PropertyMetadata(true, OnCanSortColumnsChanged));
public static readonly DependencyProperty CanFilterColumnsProperty = DependencyProperty.Register(nameof(CanFilterColumns), typeof(bool), typeof(TableView), new PropertyMetadata(true, OnCanFilterColumnsChanged));
public static readonly DependencyProperty MinColumnWidthProperty = DependencyProperty.Register(nameof(MinColumnWidth), typeof(double), typeof(TableView), new PropertyMetadata(50d, OnMinColumnWidthChanged));
public static readonly DependencyProperty MaxColumnWidthProperty = DependencyProperty.Register(nameof(MaxColumnWidth), typeof(double), typeof(TableView), new PropertyMetadata(double.PositiveInfinity, OnMaxColumnWidthChanged));

public event EventHandler<TableViewAutoGeneratingColumnEventArgs>? AutoGeneratingColumn;
public event EventHandler<TableViewExportRowsContentEventArgs>? ExportAllRowsContent;
Expand Down
7 changes: 6 additions & 1 deletion src/WinUI.TableView/TableViewCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ public TableViewCell()
protected override Size MeasureOverride(Size availableSize)
{
var size = base.MeasureOverride(availableSize);
Column.DesiredWidth = Math.Max(Column.DesiredWidth, size.Width);

if (Column is not null)
{
Column.DesiredWidth = Math.Max(Column.DesiredWidth, size.Width);
}

return size;
}

Expand Down
89 changes: 78 additions & 11 deletions src/WinUI.TableView/TableViewColumn.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,53 @@
using Microsoft.UI.Xaml;
using System;

namespace WinUI.TableView;

[StyleTypedProperty(Property = nameof(HeaderStyle), StyleTargetType = typeof(TableViewColumnHeader))]
public abstract class TableViewColumn : DependencyObject
{
private TableViewColumnsCollection? _owningCollection;
private TableViewColumnHeader? _headerControl;
private double _desiredWidth;

public abstract FrameworkElement GenerateElement();
public abstract FrameworkElement GenerateEditingElement();

internal void SetOwingCollection(TableViewColumnsCollection collection)
{
_owningCollection = collection;
}

public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}

public double Width
public GridLength Width
{
get => (double)GetValue(WidthProperty);
get => (GridLength)GetValue(WidthProperty);
set => SetValue(WidthProperty, value);
}

public double MinWidth
public double? MinWidth
{
get => (double)GetValue(MinWidthProperty);
get => (double?)GetValue(MinWidthProperty);
set => SetValue(MinWidthProperty, value);
}

public double MaxWidth
public double? MaxWidth
{
get => (double)GetValue(MaxWidthProperty);
get => (double?)GetValue(MaxWidthProperty);
set => SetValue(MaxWidthProperty, value);
}

public double ActualWidth
{
get => (double)GetValue(ActualWidthProperty);
set => SetValue(ActualWidthProperty, value);
}

public bool CanResize
{
get => (bool)GetValue(CanResizeProperty);
Expand Down Expand Up @@ -68,7 +82,18 @@ public Visibility Visibility
set => SetValue(VisibilityProperty, value);
}

internal double DesiredWidth { get; set; }
internal double DesiredWidth
{
get => _desiredWidth;
set
{
if (_desiredWidth != value)
{
_desiredWidth = value;
_owningCollection?.HandleColumnPropertyChanged(this, nameof(DesiredWidth));
}
}
}

public bool IsAutoGenerated { get; internal set; }

Expand All @@ -80,12 +105,54 @@ private void EnsureHeaderStyle()
}
}


private static void OnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableViewColumn column && column._owningCollection is { })
{
column._owningCollection.HandleColumnPropertyChanged(column, nameof(Width));
}
}

private static void OnMinWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableViewColumn column && column._owningCollection is { })
{
column._owningCollection.HandleColumnPropertyChanged(column, nameof(MinWidth));
}
}

private static void OnMaxWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableViewColumn column && column._owningCollection is { })
{
column._owningCollection.HandleColumnPropertyChanged(column, nameof(MaxWidth));
}
}

private static void OnActualWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableViewColumn column && column._owningCollection is { })
{
column._owningCollection.HandleColumnPropertyChanged(column, nameof(ActualWidth));
}
}

private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TableViewColumn column && column._owningCollection is { })
{
column._owningCollection.HandleColumnPropertyChanged(column, nameof(Visibility));
}
}

public static readonly DependencyProperty HeaderStyleProperty = DependencyProperty.Register(nameof(HeaderStyle), typeof(Style), typeof(TableViewColumn), new PropertyMetadata(null, (d, _) => ((TableViewColumn)d).EnsureHeaderStyle()));
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(nameof(Header), typeof(object), typeof(TableViewColumn), new PropertyMetadata(null));
public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(nameof(Width), typeof(double), typeof(TableViewColumn), new PropertyMetadata(200d));
public static readonly DependencyProperty MinWidthProperty = DependencyProperty.Register(nameof(MinWidth), typeof(double), typeof(TableViewColumn), new PropertyMetadata(50d));
public static readonly DependencyProperty MaxWidthProperty = DependencyProperty.Register(nameof(MaxWidth), typeof(double), typeof(TableViewColumn), new PropertyMetadata(double.PositiveInfinity));
public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(nameof(Width), typeof(GridLength), typeof(TableViewColumn), new PropertyMetadata(GridLength.Auto, OnWidthChanged));
public static readonly DependencyProperty MinWidthProperty = DependencyProperty.Register(nameof(MinWidth), typeof(double?), typeof(TableViewColumn), new PropertyMetadata(default, OnMinWidthChanged));
public static readonly DependencyProperty MaxWidthProperty = DependencyProperty.Register(nameof(MaxWidth), typeof(double?), typeof(TableViewColumn), new PropertyMetadata(default, OnMaxWidthChanged));
public static readonly DependencyProperty ActualWidthProperty = DependencyProperty.Register(nameof(ActualWidth), typeof(double), typeof(TableViewColumn), new PropertyMetadata(0d, OnActualWidthChanged));
public static readonly DependencyProperty CanResizeProperty = DependencyProperty.Register(nameof(CanResize), typeof(bool), typeof(TableViewColumn), new PropertyMetadata(true));
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(nameof(IsReadOnly), typeof(bool), typeof(TableViewColumn), new PropertyMetadata(false));
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.Register(nameof(Visibility), typeof(Visibility), typeof(TableViewColumn), new PropertyMetadata(Visibility.Visible));
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.Register(nameof(Visibility), typeof(Visibility), typeof(TableViewColumn), new PropertyMetadata(Visibility.Visible, OnVisibilityChanged));
}
59 changes: 40 additions & 19 deletions src/WinUI.TableView/TableViewColumnHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public partial class TableViewColumnHeader : ContentControl
private TableViewHeaderRow? _headerRow;
private Button? _optionsButton;
private MenuFlyout? _optionsFlyout;
private ContentPresenter? _contentPresenter;
private CheckBox? _selectAllCheckBox;
private OptionsFlyoutViewModel _optionsFlyoutViewModel = default!;
private string _propertyPath = default!;
Expand All @@ -45,6 +46,15 @@ public TableViewColumnHeader()
{
DefaultStyleKey = typeof(TableViewColumnHeader);
ManipulationMode = ManipulationModes.TranslateX;
RegisterPropertyChangedCallback(WidthProperty, OnWidthChanged);
}

private void OnWidthChanged(DependencyObject sender, DependencyProperty dp)
{
if (Column is not null)
{
Column.ActualWidth = Width;
}
}

private void DoSort(SD direction, bool singleSorting = true)
Expand Down Expand Up @@ -145,6 +155,7 @@ protected override void OnApplyTemplate()
_headerRow = this.FindAscendant<TableViewHeaderRow>();
_optionsButton = GetTemplateChild("OptionsButton") as Button;
_optionsFlyout = GetTemplateChild("OptionsFlyout") as MenuFlyout;
_contentPresenter = GetTemplateChild("ContentPresenter") as ContentPresenter;

if (_tableView is null || _optionsButton is null || _optionsFlyout is null)
{
Expand Down Expand Up @@ -290,6 +301,11 @@ private void SetFilterButtonVisibility()
{
_optionsButton.Visibility = CanFilter ? Visibility.Visible : Visibility.Collapsed;
}

if (_contentPresenter is not null)
{
_contentPresenter.Margin = CanFilter ? new Thickness(0, 0, 8, 0) : new Thickness(0);
}
}

private bool IsCursorInRightResizeArea(PointerRoutedEventArgs args)
Expand All @@ -311,22 +327,22 @@ protected override void OnDoubleTapped(DoubleTappedRoutedEventArgs e)
{
base.OnDoubleTapped(e);

if (IsSizingCursor && Column is not null)
if (!IsSizingCursor || _tableView is null)
{
var position = e.GetPosition(this);
return;
}

if (position.X <= 8 && _headerRow?.GetPreviousHeader(this) is { Column: { } } header)
{
var width = Math.Clamp(header.Column.DesiredWidth, header.MinWidth, header.MaxWidth);
header.Width = width;
header.Measure(new Size(Width, ActualHeight));
}
else
{
var width = Math.Clamp(Column.DesiredWidth, MinWidth, MaxWidth);
Width = width;
Measure(new Size(Width, ActualHeight));
}
var position = e.GetPosition(this);

if (position.X <= 8 && _headerRow?.GetPreviousHeader(this) is { Column: { } } header)
{
var width = Math.Clamp(header.Column.DesiredWidth, header.Column.MinWidth ?? _tableView.MinColumnWidth, header.Column.MaxWidth ?? _tableView.MaxColumnWidth);
header.Column.Width = new GridLength(width, GridUnitType.Pixel);
}
else if (Column is not null)
{
var width = Math.Clamp(Column.DesiredWidth, Column.MinWidth ?? _tableView.MinColumnWidth, Column.MaxWidth ?? _tableView.MaxColumnWidth);
Column.Width = new GridLength(width, GridUnitType.Pixel);
}
}

Expand Down Expand Up @@ -368,15 +384,20 @@ protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e)
{
base.OnManipulationDelta(e);

if (Column is null || _tableView is null)
{
return;
}

if (_resizeStarted)
{
Width = Math.Clamp(e.Position.X, MinWidth, MaxWidth);
Measure(new Size(Width, ActualHeight));
var width = Math.Clamp(e.Position.X, Column.MinWidth ?? _tableView.MinColumnWidth, Column.MaxWidth ?? _tableView.MaxColumnWidth);
Column.Width = new GridLength(width, GridUnitType.Pixel);
}
else if (_resizePreviousStarted && _headerRow?.GetPreviousHeader(this) is { } header)
else if (_resizePreviousStarted && _headerRow?.GetPreviousHeader(this) is { Column: { } } header)
{
header.Width = Math.Clamp(header.ActualWidth + e.Position.X, header.MinWidth, header.MaxWidth);
header.Measure(new Size(header.Width, ActualHeight));
var width = Math.Clamp(header.Column.ActualWidth + e.Position.X, header.Column.MinWidth ?? _tableView.MinColumnWidth, header.Column.MaxWidth ?? _tableView.MaxColumnWidth);
header.Column.Width = new GridLength(width, GridUnitType.Pixel);
}
}

Expand Down
24 changes: 10 additions & 14 deletions src/WinUI.TableView/TableViewColumnsColection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ namespace WinUI.TableView;

public class TableViewColumnsCollection : ObservableCollection<TableViewColumn>
{
private readonly Dictionary<TableViewColumn, long> _callbackMap = new();
internal event EventHandler<TableViewColumnVisibilityChanged>? ColumnVisibilityChanged;
internal event EventHandler<TableViewColumnPropertyChanged>? ColumnPropertyChanged;
internal IList<TableViewColumn> VisibleColumns => Items.Where(x => x.Visibility == Visibility.Visible).ToList();

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
Expand All @@ -19,44 +18,41 @@ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)

if (e.NewItems != null)
{
var index = e.NewStartingIndex;

foreach (var column in e.NewItems.OfType<TableViewColumn>())
{
var token = column.RegisterPropertyChangedCallback(TableViewColumn.VisibilityProperty, OnColumnVisibilityChanged);
_callbackMap.Remove(column);
_callbackMap.Add(column, token);
column.SetOwingCollection(this);
}
}

if (e.OldItems != null)
{
foreach (var column in e.OldItems.OfType<TableViewColumn>())
{
var token = _callbackMap[column];
column.UnregisterPropertyChangedCallback(TableViewColumn.VisibilityProperty, token);
column.SetOwingCollection(null!);
}
}
}

private void OnColumnVisibilityChanged(DependencyObject sender, DependencyProperty dp)
internal void HandleColumnPropertyChanged(TableViewColumn column, string propertyName)
{
if (sender is TableViewColumn column)
if (Items.Contains(column))
{
var index = IndexOf(column);
ColumnVisibilityChanged?.Invoke(this, new TableViewColumnVisibilityChanged(column, index));
ColumnPropertyChanged?.Invoke(this, new TableViewColumnPropertyChanged(column, propertyName, index));
}
}
}

internal class TableViewColumnVisibilityChanged : EventArgs
internal class TableViewColumnPropertyChanged : EventArgs
{
public TableViewColumnVisibilityChanged(TableViewColumn column, int index)
public TableViewColumnPropertyChanged(TableViewColumn column, string propertyName, int index)
{
Column = column;
PropertyName = propertyName;
Index = index;
}

public TableViewColumn Column { get; }
public string PropertyName { get; }
public int Index { get; }
}
Loading

0 comments on commit 5bd8eb0

Please sign in to comment.