From dd5cf143b24e7658faff45c71c076d3b62d8e6a9 Mon Sep 17 00:00:00 2001 From: koal44 Date: Thu, 29 Feb 2024 17:03:54 -0800 Subject: [PATCH 1/5] Add properties MinWidth and MaxWidth to GridViewColumn A fair amount of reflection was needed to support columnheader dragging --- .../Views/Pages/Collections/ListViewPage.xaml | 17 +- .../Controls/ListView/GridViewColumn.cs | 67 ++++++++ .../ListView/GridViewHeaderRowPresenter.cs | 158 ++++++++++++++++++ .../Controls/ListView/GridViewRowPresenter.cs | 50 ++++++ src/Wpf.Ui/Controls/ListView/ListView.xaml | 2 +- .../Controls/ListView/ListViewItem.xaml | 2 +- 6 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 src/Wpf.Ui/Controls/ListView/GridViewColumn.cs create mode 100644 src/Wpf.Ui/Controls/ListView/GridViewHeaderRowPresenter.cs create mode 100644 src/Wpf.Ui/Controls/ListView/GridViewRowPresenter.cs diff --git a/src/Wpf.Ui.Gallery/Views/Pages/Collections/ListViewPage.xaml b/src/Wpf.Ui.Gallery/Views/Pages/Collections/ListViewPage.xaml index 7ce461c3d..86632c0c2 100644 --- a/src/Wpf.Ui.Gallery/Views/Pages/Collections/ListViewPage.xaml +++ b/src/Wpf.Ui.Gallery/Views/Pages/Collections/ListViewPage.xaml @@ -122,7 +122,7 @@ <ListView ItemsSource="{Binding ViewModel.BasicListViewItems}">\n \t<ListView.View>\n \t\t<GridView>\n - \t\t\t<GridViewColumn DisplayMemberBinding="{Binding FirstName}" Header="First Name"/>\n + \t\t\t<GridViewColumn DisplayMemberBinding="{Binding FirstName}" Header="First Name" MinWidth=&quot;100&quot; MaxWidth=&quot;200&quot;/>\n \t\t\t<GridViewColumn DisplayMemberBinding="{Binding LastName}" Header="Last Name"/>\n \t\t\t<GridViewColumn DisplayMemberBinding="{Binding Company}" Header="Company"/>\n \t\t</GridView>\n @@ -131,20 +131,23 @@ - - - + diff --git a/src/Wpf.Ui/Controls/ListView/GridViewColumn.cs b/src/Wpf.Ui/Controls/ListView/GridViewColumn.cs new file mode 100644 index 000000000..7183df577 --- /dev/null +++ b/src/Wpf.Ui/Controls/ListView/GridViewColumn.cs @@ -0,0 +1,67 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Reflection; + +namespace Wpf.Ui.Controls; + +public class GridViewColumn : System.Windows.Controls.GridViewColumn +{ + // use reflection to get the `DesiredWidth` internal property. cache the `PropertyInfo` for performance + private static readonly PropertyInfo _desiredWidthProperty = typeof(System.Windows.Controls.GridViewColumn).GetProperty("DesiredWidth", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("The `DesiredWidth` property was not found."); + + public double DesiredWidth => (double)(_desiredWidthProperty.GetValue(this) ?? throw new InvalidOperationException("The `DesiredWidth` property was not found.")); + + // use reflection to get the `ActualIndex` internal property. cache the `PropertyInfo` for performance + private static readonly PropertyInfo _actualIndexProperty = typeof(System.Windows.Controls.GridViewColumn).GetProperty("ActualIndex", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("The `ActualIndex` property was not found."); + + public int ActualIndex => (int)(_actualIndexProperty.GetValue(this) ?? throw new InvalidOperationException("The `ActualIndex` property was not found.")); + + public double MinWidth + { + get => (double)GetValue(MinWidthProperty); + set => SetValue(MinWidthProperty, value); + } + + /// Identifies the dependency property. + public static readonly DependencyProperty MinWidthProperty = DependencyProperty.Register(nameof(MinWidth), typeof(double), typeof(GridViewColumn), new FrameworkPropertyMetadata(0.0, OnMinWidthChanged)); + + private static void OnMinWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not GridViewColumn self) + { + return; + } + + self.OnMinWidthChanged(e); + } + + protected virtual void OnMinWidthChanged(DependencyPropertyChangedEventArgs e) + { + } + + public double MaxWidth + { + get => (double)GetValue(MaxWidthProperty); + set => SetValue(MaxWidthProperty, value); + } + + /// Identifies the dependency property. + public static readonly DependencyProperty MaxWidthProperty = DependencyProperty.Register(nameof(MaxWidth), typeof(double), typeof(GridViewColumn), new FrameworkPropertyMetadata(Double.PositiveInfinity, OnMaxWidthChanged)); + + private static void OnMaxWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not GridViewColumn self) + { + return; + } + + self.OnMaxWidthChanged(e); + } + + protected virtual void OnMaxWidthChanged(DependencyPropertyChangedEventArgs e) + { + } +} diff --git a/src/Wpf.Ui/Controls/ListView/GridViewHeaderRowPresenter.cs b/src/Wpf.Ui/Controls/ListView/GridViewHeaderRowPresenter.cs new file mode 100644 index 000000000..e56b4d4a4 --- /dev/null +++ b/src/Wpf.Ui/Controls/ListView/GridViewHeaderRowPresenter.cs @@ -0,0 +1,158 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Reflection; +using System.Windows.Controls; + +namespace Wpf.Ui.Controls; + +public class GridViewHeaderRowPresenter : System.Windows.Controls.GridViewHeaderRowPresenter +{ + // use reflection to get the `HeadersPositionList` internal property. cache the `PropertyInfo` for performance + private static readonly PropertyInfo _headersPositionListPropertyInfo = + typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetProperty("HeadersPositionList", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("The `HeadersPositionList` property was not found."); + + private List GetHeadersPositionList() => _headersPositionListPropertyInfo.GetValue(this) as List + ?? throw new InvalidOperationException("HeadersPositionList is null"); + + // use reflection to get the `_isHeaderDragging` private property. cache the `FieldInfo` for performance + private static readonly FieldInfo _isHeaderDraggingFieldInfo = + typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_isHeaderDragging", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("The `_isHeaderDragging` field was not found."); + + private bool IsHeaderDragging => (bool)(_isHeaderDraggingFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_isHeaderDragging` field was not found.")); + + private static readonly FieldInfo _startPosFieldInfo = + typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_startPos", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("The `_startPos` field was not found."); + + // start position when dragging (position relative to GridViewHeaderRowPresenter) + private Point StartPos => (Point)(_startPosFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_startPos` field was not found.")); + + private static readonly FieldInfo _relativeStartPosFieldInfo = + typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_relativeStartPos", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("The `_relativeStartPos` field was not found."); + + // relative start position when dragging (position relative to Header) + private Point RelativeStartPos => (Point)(_relativeStartPosFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_relativeStartPos` field was not found.")); + + private static readonly FieldInfo _currentPosFieldInfo = + typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_currentPos", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("The `_currentPos` field was not found."); + + // current mouse position (position relative to GridViewHeaderRowPresenter) + private Point CurrentPos => (Point)(_currentPosFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_currentPos` field was not found.")); + + private static readonly FieldInfo _startColumnIndexFieldInfo = + typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_startColumnIndex", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("The `_startColumnIndex` field was not found."); + + // start column index when begin dragging + private int StartColumnIndex => (int)(_startColumnIndexFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_startColumnIndex` field was not found.")); + + private static readonly FieldInfo _desColumnIndexFieldInfo = + typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_desColumnIndex", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("The `_desColumnIndex` field was not found."); + + // destination column index when finish dragging + private int DesColumnIndex => (int)(_desColumnIndexFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_desColumnIndex` field was not found.")); + + private static readonly MethodInfo _findPositionByIndexMethodInfo = + typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetMethod("FindPositionByIndex", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("The `FindPositionByIndex` method was not found."); + + // Find position by logic column index + private Point FindPositionByIndex(int index) + { + return (Point)(_findPositionByIndexMethodInfo.Invoke(this, new object[] { index }) + ?? throw new InvalidOperationException("`FindPositionByIndex` method invocation resulted in null.")); + } + + /// + /// computes the position of its children inside each child's Margin and calls Arrange + /// on each child. + /// + /// Size the GridViewRowPresenter will assume. + /// The actual size used. + protected override Size ArrangeOverride(Size arrangeSize) + { + _ = base.ArrangeOverride(arrangeSize); + + double accumulatedWidth = 0; + GridViewColumnCollection columns = Columns; + var remainingWidth = arrangeSize.Width; + Rect rect; + List headersPositionList = GetHeadersPositionList(); + headersPositionList.Clear(); + + if (columns != null) + { + for (var i = 0; i < columns.Count; ++i) + { + var visualIndex = GetVisualIndex(i); + if (VisualTreeHelper.GetChild(this, visualIndex) is not UIElement child || columns[i] is not GridViewColumn col) + { + continue; + } + + var clampedWidth = Math.Min(Math.Max(col.DesiredWidth, col.MinWidth), col.MaxWidth); + clampedWidth = Math.Max(0, Math.Min(clampedWidth, remainingWidth)); + + rect = new Rect(accumulatedWidth, 0, clampedWidth, arrangeSize.Height); + child.Arrange(rect); + + headersPositionList.Add(rect); + remainingWidth -= clampedWidth; + accumulatedWidth += clampedWidth; + } + } + + // Arrange padding header + UIElement paddingHeader = VisualTreeHelper.GetChild(this, 0) as UIElement + ?? throw new InvalidOperationException("padding header is null"); + rect = new Rect(accumulatedWidth, 0.0, Math.Max(remainingWidth, 0.0), arrangeSize.Height); + paddingHeader.Arrange(rect); + headersPositionList.Add(rect); + + // if re-order started, arrange floating header & indicator + if (IsHeaderDragging) + { + UIElement floatingHeader = VisualTreeHelper.GetChild(this, VisualTreeHelper.GetChildrenCount(this) - 1) as UIElement + ?? throw new InvalidOperationException("floating header is null"); // last child + UIElement separator = VisualTreeHelper.GetChild(this, VisualTreeHelper.GetChildrenCount(this) - 2) as UIElement + ?? throw new InvalidOperationException("indicator is null"); // second to last child + + floatingHeader.Arrange(new Rect(new Point(CurrentPos.X - RelativeStartPos.X, 0), headersPositionList[StartColumnIndex].Size)); + + Point pos = FindPositionByIndex(DesColumnIndex); + separator.Arrange(new Rect(pos, new Size(separator.DesiredSize.Width, arrangeSize.Height))); + } + + return arrangeSize; + } + + private int GetVisualIndex(int columnIndex) + { + // VisualTree: [PaddingHeader, ColumnHeaders (in reverse order), Separator, FloatingHeader] + var index = VisualTreeHelper.GetChildrenCount(this) - 3 - columnIndex; // -1 for index counting & -2 for Seperator and FloatingHeader + return index; + } + + public static List GetVisualChildren(DependencyObject parent) + { + var list = new List(); + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + { + DependencyObject child = VisualTreeHelper.GetChild(parent, i); + if (child is UIElement element) + { + list.Add(element); + } + } + + return list; + } +} diff --git a/src/Wpf.Ui/Controls/ListView/GridViewRowPresenter.cs b/src/Wpf.Ui/Controls/ListView/GridViewRowPresenter.cs new file mode 100644 index 000000000..acb88a914 --- /dev/null +++ b/src/Wpf.Ui/Controls/ListView/GridViewRowPresenter.cs @@ -0,0 +1,50 @@ +using System.Windows.Controls; + +namespace Wpf.Ui.Controls; + +public class GridViewRowPresenter : System.Windows.Controls.GridViewRowPresenter +{ + /// + /// GridViewRowPresenter computes the position of its children inside each child's Margin and calls Arrange + /// on each child. + /// + /// Size the GridViewRowPresenter will assume. + /// The actual size used. + protected override Size ArrangeOverride(Size arrangeSize) + { + _ = base.ArrangeOverride(arrangeSize); + + GridViewColumnCollection columns = Columns; + double accumulatedWidth = 0; + var remainingWidth = arrangeSize.Width; + + if (columns != null) + { + for (var i = 0; i < columns.Count; ++i) + { + if (columns[i] is not GridViewColumn col) + { + continue; + } + + // use ActualIndex to track reordering when columns were dragged around + var visualIndex = col.ActualIndex; + if (VisualTreeHelper.GetChild(this, visualIndex) is not UIElement child) + { + continue; + } + + var clampedWidth = Math.Min(Math.Max(col.DesiredWidth, col.MinWidth), col.MaxWidth); + clampedWidth = Math.Max(0, Math.Min(clampedWidth, remainingWidth)); + + var rect = new Rect(accumulatedWidth, 0, clampedWidth, arrangeSize.Height); + child.Arrange(rect); + + remainingWidth -= clampedWidth; + accumulatedWidth += clampedWidth; + } + } + + return arrangeSize; + } +} diff --git a/src/Wpf.Ui/Controls/ListView/ListView.xaml b/src/Wpf.Ui/Controls/ListView/ListView.xaml index 90c506ac9..c5c09d3ac 100644 --- a/src/Wpf.Ui/Controls/ListView/ListView.xaml +++ b/src/Wpf.Ui/Controls/ListView/ListView.xaml @@ -59,7 +59,7 @@ Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"> - - Date: Tue, 19 Mar 2024 14:03:38 -0700 Subject: [PATCH 2/5] Add documentation and move GridView related components to Wpf.Ui/Controls/ --- .../{ListView => GridView}/GridViewColumn.cs | 31 ++++++++++- .../GridViewHeaderRowPresenter.cs | 41 ++++++++------ .../Controls/GridView/GridViewRowPresenter.cs | 55 +++++++++++++++++++ .../Controls/ListView/GridViewRowPresenter.cs | 50 ----------------- .../Controls/ListView/ListViewItem.xaml | 5 +- 5 files changed, 112 insertions(+), 70 deletions(-) rename src/Wpf.Ui/Controls/{ListView => GridView}/GridViewColumn.cs (67%) rename src/Wpf.Ui/Controls/{ListView => GridView}/GridViewHeaderRowPresenter.cs (86%) create mode 100644 src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs delete mode 100644 src/Wpf.Ui/Controls/ListView/GridViewRowPresenter.cs diff --git a/src/Wpf.Ui/Controls/ListView/GridViewColumn.cs b/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs similarity index 67% rename from src/Wpf.Ui/Controls/ListView/GridViewColumn.cs rename to src/Wpf.Ui/Controls/GridView/GridViewColumn.cs index 7183df577..4a99e95dd 100644 --- a/src/Wpf.Ui/Controls/ListView/GridViewColumn.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs @@ -7,18 +7,40 @@ namespace Wpf.Ui.Controls; +/// +/// Extends with MinWidth and MaxWidth properties. +/// It can be used with when in GridView mode. +/// +/// +/// +/// <ui:ListView> +/// <ui:ListView.View> +/// <GridView> +/// <ui:GridViewColumn +/// MinWidth="100" +/// MaxWidth="200" +/// DisplayMemberBinding="{Binding FirstName}" +/// Header="First Name" /> +/// </GridView> +/// </ui:ListView.View> +/// </ui:ListView> +/// +/// public class GridViewColumn : System.Windows.Controls.GridViewColumn { // use reflection to get the `DesiredWidth` internal property. cache the `PropertyInfo` for performance private static readonly PropertyInfo _desiredWidthProperty = typeof(System.Windows.Controls.GridViewColumn).GetProperty("DesiredWidth", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("The `DesiredWidth` property was not found."); - public double DesiredWidth => (double)(_desiredWidthProperty.GetValue(this) ?? throw new InvalidOperationException("The `DesiredWidth` property was not found.")); + internal double DesiredWidth => (double)(_desiredWidthProperty.GetValue(this) ?? throw new InvalidOperationException("The `DesiredWidth` property was not found.")); // use reflection to get the `ActualIndex` internal property. cache the `PropertyInfo` for performance private static readonly PropertyInfo _actualIndexProperty = typeof(System.Windows.Controls.GridViewColumn).GetProperty("ActualIndex", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("The `ActualIndex` property was not found."); - public int ActualIndex => (int)(_actualIndexProperty.GetValue(this) ?? throw new InvalidOperationException("The `ActualIndex` property was not found.")); + internal int ActualIndex => (int)(_actualIndexProperty.GetValue(this) ?? throw new InvalidOperationException("The `ActualIndex` property was not found.")); + /// + /// Gets or sets the minimum width of the column. + /// public double MinWidth { get => (double)GetValue(MinWidthProperty); @@ -40,8 +62,12 @@ private static void OnMinWidthChanged(DependencyObject d, DependencyPropertyChan protected virtual void OnMinWidthChanged(DependencyPropertyChangedEventArgs e) { + // Hook for derived classes to react to MinWidth property changes } + /// + /// gets or sets the maximum width of the column. + /// public double MaxWidth { get => (double)GetValue(MaxWidthProperty); @@ -63,5 +89,6 @@ private static void OnMaxWidthChanged(DependencyObject d, DependencyPropertyChan protected virtual void OnMaxWidthChanged(DependencyPropertyChangedEventArgs e) { + // Hook for derived classes to react to MaxWidth property changes } } diff --git a/src/Wpf.Ui/Controls/ListView/GridViewHeaderRowPresenter.cs b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs similarity index 86% rename from src/Wpf.Ui/Controls/ListView/GridViewHeaderRowPresenter.cs rename to src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs index e56b4d4a4..49a5bd531 100644 --- a/src/Wpf.Ui/Controls/ListView/GridViewHeaderRowPresenter.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs @@ -4,10 +4,12 @@ // All Rights Reserved. using System.Reflection; -using System.Windows.Controls; namespace Wpf.Ui.Controls; +/// +/// Extends , and adds layout support for , which can have and . +/// public class GridViewHeaderRowPresenter : System.Windows.Controls.GridViewHeaderRowPresenter { // use reflection to get the `HeadersPositionList` internal property. cache the `PropertyInfo` for performance @@ -81,33 +83,35 @@ protected override Size ArrangeOverride(Size arrangeSize) { _ = base.ArrangeOverride(arrangeSize); + // exit early if columns are not Wpf.Ui.Controls.GridViewColumn + if (Columns == null || Columns.Count == 0 || Columns[0] is not GridViewColumn) + { + return arrangeSize; + } + double accumulatedWidth = 0; - GridViewColumnCollection columns = Columns; var remainingWidth = arrangeSize.Width; Rect rect; List headersPositionList = GetHeadersPositionList(); headersPositionList.Clear(); - if (columns != null) + for (var i = 0; i < Columns.Count; ++i) { - for (var i = 0; i < columns.Count; ++i) + var visualIndex = GetVisualIndex(i); + if (VisualTreeHelper.GetChild(this, visualIndex) is not UIElement child || Columns[i] is not GridViewColumn col) { - var visualIndex = GetVisualIndex(i); - if (VisualTreeHelper.GetChild(this, visualIndex) is not UIElement child || columns[i] is not GridViewColumn col) - { - continue; - } + continue; + } - var clampedWidth = Math.Min(Math.Max(col.DesiredWidth, col.MinWidth), col.MaxWidth); - clampedWidth = Math.Max(0, Math.Min(clampedWidth, remainingWidth)); + var clampedWidth = Math.Min(Math.Max(col.DesiredWidth, col.MinWidth), col.MaxWidth); + clampedWidth = Math.Max(0, Math.Min(clampedWidth, remainingWidth)); - rect = new Rect(accumulatedWidth, 0, clampedWidth, arrangeSize.Height); - child.Arrange(rect); + rect = new Rect(accumulatedWidth, 0, clampedWidth, arrangeSize.Height); + child.Arrange(rect); - headersPositionList.Add(rect); - remainingWidth -= clampedWidth; - accumulatedWidth += clampedWidth; - } + headersPositionList.Add(rect); + remainingWidth -= clampedWidth; + accumulatedWidth += clampedWidth; } // Arrange padding header @@ -141,6 +145,8 @@ private int GetVisualIndex(int columnIndex) return index; } + // helper method to get all visual children, useful for debugging + /* public static List GetVisualChildren(DependencyObject parent) { var list = new List(); @@ -155,4 +161,5 @@ public static List GetVisualChildren(DependencyObject parent) return list; } + */ } diff --git a/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs b/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs new file mode 100644 index 000000000..e997d7574 --- /dev/null +++ b/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs @@ -0,0 +1,55 @@ +using System.Windows.Controls; + +namespace Wpf.Ui.Controls; + +/// +/// Extends , and adds header row layout support for , which can have and . +/// +public class GridViewRowPresenter : System.Windows.Controls.GridViewRowPresenter +{ + /// + /// GridViewRowPresenter computes the position of its children inside each child's Margin and calls Arrange + /// on each child. + /// + /// Size the GridViewRowPresenter will assume. + /// The actual size used. + protected override Size ArrangeOverride(Size arrangeSize) + { + _ = base.ArrangeOverride(arrangeSize); + + // exit early if columns are not Wpf.Ui.Controls.GridViewColumn + if (Columns == null || Columns.Count == 0 || Columns[0] is not GridViewColumn) + { + return arrangeSize; + } + + double accumulatedWidth = 0; + var remainingWidth = arrangeSize.Width; + + for (var i = 0; i < Columns.Count; ++i) + { + if (Columns[i] is not GridViewColumn col) + { + continue; + } + + // use ActualIndex to track reordering when columns are dragged around + var visualIndex = col.ActualIndex; + if (VisualTreeHelper.GetChild(this, visualIndex) is not UIElement child) + { + continue; + } + + var clampedWidth = Math.Min(Math.Max(col.DesiredWidth, col.MinWidth), col.MaxWidth); + clampedWidth = Math.Max(0, Math.Min(clampedWidth, remainingWidth)); + + var rect = new Rect(accumulatedWidth, 0, clampedWidth, arrangeSize.Height); + child.Arrange(rect); + + remainingWidth -= clampedWidth; + accumulatedWidth += clampedWidth; + } + + return arrangeSize; + } +} diff --git a/src/Wpf.Ui/Controls/ListView/GridViewRowPresenter.cs b/src/Wpf.Ui/Controls/ListView/GridViewRowPresenter.cs deleted file mode 100644 index acb88a914..000000000 --- a/src/Wpf.Ui/Controls/ListView/GridViewRowPresenter.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Windows.Controls; - -namespace Wpf.Ui.Controls; - -public class GridViewRowPresenter : System.Windows.Controls.GridViewRowPresenter -{ - /// - /// GridViewRowPresenter computes the position of its children inside each child's Margin and calls Arrange - /// on each child. - /// - /// Size the GridViewRowPresenter will assume. - /// The actual size used. - protected override Size ArrangeOverride(Size arrangeSize) - { - _ = base.ArrangeOverride(arrangeSize); - - GridViewColumnCollection columns = Columns; - double accumulatedWidth = 0; - var remainingWidth = arrangeSize.Width; - - if (columns != null) - { - for (var i = 0; i < columns.Count; ++i) - { - if (columns[i] is not GridViewColumn col) - { - continue; - } - - // use ActualIndex to track reordering when columns were dragged around - var visualIndex = col.ActualIndex; - if (VisualTreeHelper.GetChild(this, visualIndex) is not UIElement child) - { - continue; - } - - var clampedWidth = Math.Min(Math.Max(col.DesiredWidth, col.MinWidth), col.MaxWidth); - clampedWidth = Math.Max(0, Math.Min(clampedWidth, remainingWidth)); - - var rect = new Rect(accumulatedWidth, 0, clampedWidth, arrangeSize.Height); - child.Arrange(rect); - - remainingWidth -= clampedWidth; - accumulatedWidth += clampedWidth; - } - } - - return arrangeSize; - } -} diff --git a/src/Wpf.Ui/Controls/ListView/ListViewItem.xaml b/src/Wpf.Ui/Controls/ListView/ListViewItem.xaml index ee7b30a8f..708c578ae 100644 --- a/src/Wpf.Ui/Controls/ListView/ListViewItem.xaml +++ b/src/Wpf.Ui/Controls/ListView/ListViewItem.xaml @@ -5,7 +5,10 @@ All Rights Reserved. --> - + Date: Tue, 19 Mar 2024 16:02:02 -0700 Subject: [PATCH 3/5] Greatly simplify ArrangeOverride() logic by adjusting reflected field _desiredWidth It turns out that the base methods for GridViewRow presenters can handle all the logic if _desiredWidth is preemptively adjusted --- .../Controls/GridView/GridViewColumn.cs | 25 ++- .../GridView/GridViewHeaderRowPresenter.cs | 149 +----------------- .../Controls/GridView/GridViewRowPresenter.cs | 44 +----- 3 files changed, 27 insertions(+), 191 deletions(-) diff --git a/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs b/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs index 4a99e95dd..df9bffa8c 100644 --- a/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs @@ -28,15 +28,24 @@ namespace Wpf.Ui.Controls; /// public class GridViewColumn : System.Windows.Controls.GridViewColumn { - // use reflection to get the `DesiredWidth` internal property. cache the `PropertyInfo` for performance - private static readonly PropertyInfo _desiredWidthProperty = typeof(System.Windows.Controls.GridViewColumn).GetProperty("DesiredWidth", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("The `DesiredWidth` property was not found."); + // use reflection to the `_desiredWidth` private field. + private static readonly FieldInfo _desiredWidthField = typeof(System.Windows.Controls.GridViewColumn).GetField("_desiredWidth", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException("The `_desiredWidth` field was not found."); - internal double DesiredWidth => (double)(_desiredWidthProperty.GetValue(this) ?? throw new InvalidOperationException("The `DesiredWidth` property was not found.")); - - // use reflection to get the `ActualIndex` internal property. cache the `PropertyInfo` for performance - private static readonly PropertyInfo _actualIndexProperty = typeof(System.Windows.Controls.GridViewColumn).GetProperty("ActualIndex", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("The `ActualIndex` property was not found."); - - internal int ActualIndex => (int)(_actualIndexProperty.GetValue(this) ?? throw new InvalidOperationException("The `ActualIndex` property was not found.")); + /// + /// Updates the desired width of the column to be clamped between MinWidth and MaxWidth). + /// + /// + /// Uses reflection to directly set the private `_desiredWidth` field on the `System.Windows.Controls.GridViewColumn`. + /// + /// + /// Thrown if reflection fails to access the `_desiredWidth` field + /// + internal void UpdateDesiredWidth() + { + var currentWidth = (double)(_desiredWidthField.GetValue(this) ?? throw new InvalidOperationException("Failed to get the current `_desiredWidth`.")); + var clampedWidth = Math.Max(MinWidth, Math.Min(currentWidth, MaxWidth)); + _desiredWidthField.SetValue(this, clampedWidth); + } /// /// Gets or sets the minimum width of the column. diff --git a/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs index 49a5bd531..04816364c 100644 --- a/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs @@ -3,8 +3,6 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. -using System.Reflection; - namespace Wpf.Ui.Controls; /// @@ -12,154 +10,17 @@ namespace Wpf.Ui.Controls; /// public class GridViewHeaderRowPresenter : System.Windows.Controls.GridViewHeaderRowPresenter { - // use reflection to get the `HeadersPositionList` internal property. cache the `PropertyInfo` for performance - private static readonly PropertyInfo _headersPositionListPropertyInfo = - typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetProperty("HeadersPositionList", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `HeadersPositionList` property was not found."); - - private List GetHeadersPositionList() => _headersPositionListPropertyInfo.GetValue(this) as List - ?? throw new InvalidOperationException("HeadersPositionList is null"); - - // use reflection to get the `_isHeaderDragging` private property. cache the `FieldInfo` for performance - private static readonly FieldInfo _isHeaderDraggingFieldInfo = - typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_isHeaderDragging", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `_isHeaderDragging` field was not found."); - - private bool IsHeaderDragging => (bool)(_isHeaderDraggingFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_isHeaderDragging` field was not found.")); - - private static readonly FieldInfo _startPosFieldInfo = - typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_startPos", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `_startPos` field was not found."); - - // start position when dragging (position relative to GridViewHeaderRowPresenter) - private Point StartPos => (Point)(_startPosFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_startPos` field was not found.")); - - private static readonly FieldInfo _relativeStartPosFieldInfo = - typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_relativeStartPos", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `_relativeStartPos` field was not found."); - - // relative start position when dragging (position relative to Header) - private Point RelativeStartPos => (Point)(_relativeStartPosFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_relativeStartPos` field was not found.")); - - private static readonly FieldInfo _currentPosFieldInfo = - typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_currentPos", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `_currentPos` field was not found."); - - // current mouse position (position relative to GridViewHeaderRowPresenter) - private Point CurrentPos => (Point)(_currentPosFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_currentPos` field was not found.")); - - private static readonly FieldInfo _startColumnIndexFieldInfo = - typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_startColumnIndex", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `_startColumnIndex` field was not found."); - - // start column index when begin dragging - private int StartColumnIndex => (int)(_startColumnIndexFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_startColumnIndex` field was not found.")); - - private static readonly FieldInfo _desColumnIndexFieldInfo = - typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_desColumnIndex", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `_desColumnIndex` field was not found."); - - // destination column index when finish dragging - private int DesColumnIndex => (int)(_desColumnIndexFieldInfo.GetValue(this) ?? throw new InvalidOperationException("The `_desColumnIndex` field was not found.")); - - private static readonly MethodInfo _findPositionByIndexMethodInfo = - typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetMethod("FindPositionByIndex", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `FindPositionByIndex` method was not found."); - - // Find position by logic column index - private Point FindPositionByIndex(int index) - { - return (Point)(_findPositionByIndexMethodInfo.Invoke(this, new object[] { index }) - ?? throw new InvalidOperationException("`FindPositionByIndex` method invocation resulted in null.")); - } - - /// - /// computes the position of its children inside each child's Margin and calls Arrange - /// on each child. - /// - /// Size the GridViewRowPresenter will assume. - /// The actual size used. protected override Size ArrangeOverride(Size arrangeSize) { - _ = base.ArrangeOverride(arrangeSize); - - // exit early if columns are not Wpf.Ui.Controls.GridViewColumn - if (Columns == null || Columns.Count == 0 || Columns[0] is not GridViewColumn) - { - return arrangeSize; - } - - double accumulatedWidth = 0; - var remainingWidth = arrangeSize.Width; - Rect rect; - List headersPositionList = GetHeadersPositionList(); - headersPositionList.Clear(); - - for (var i = 0; i < Columns.Count; ++i) - { - var visualIndex = GetVisualIndex(i); - if (VisualTreeHelper.GetChild(this, visualIndex) is not UIElement child || Columns[i] is not GridViewColumn col) - { - continue; - } - - var clampedWidth = Math.Min(Math.Max(col.DesiredWidth, col.MinWidth), col.MaxWidth); - clampedWidth = Math.Max(0, Math.Min(clampedWidth, remainingWidth)); - - rect = new Rect(accumulatedWidth, 0, clampedWidth, arrangeSize.Height); - child.Arrange(rect); - - headersPositionList.Add(rect); - remainingWidth -= clampedWidth; - accumulatedWidth += clampedWidth; - } - - // Arrange padding header - UIElement paddingHeader = VisualTreeHelper.GetChild(this, 0) as UIElement - ?? throw new InvalidOperationException("padding header is null"); - rect = new Rect(accumulatedWidth, 0.0, Math.Max(remainingWidth, 0.0), arrangeSize.Height); - paddingHeader.Arrange(rect); - headersPositionList.Add(rect); - - // if re-order started, arrange floating header & indicator - if (IsHeaderDragging) - { - UIElement floatingHeader = VisualTreeHelper.GetChild(this, VisualTreeHelper.GetChildrenCount(this) - 1) as UIElement - ?? throw new InvalidOperationException("floating header is null"); // last child - UIElement separator = VisualTreeHelper.GetChild(this, VisualTreeHelper.GetChildrenCount(this) - 2) as UIElement - ?? throw new InvalidOperationException("indicator is null"); // second to last child - - floatingHeader.Arrange(new Rect(new Point(CurrentPos.X - RelativeStartPos.X, 0), headersPositionList[StartColumnIndex].Size)); - - Point pos = FindPositionByIndex(DesColumnIndex); - separator.Arrange(new Rect(pos, new Size(separator.DesiredSize.Width, arrangeSize.Height))); - } - - return arrangeSize; - } - - private int GetVisualIndex(int columnIndex) - { - // VisualTree: [PaddingHeader, ColumnHeaders (in reverse order), Separator, FloatingHeader] - var index = VisualTreeHelper.GetChildrenCount(this) - 3 - columnIndex; // -1 for index counting & -2 for Seperator and FloatingHeader - return index; - } - - // helper method to get all visual children, useful for debugging - /* - public static List GetVisualChildren(DependencyObject parent) - { - var list = new List(); - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + // update the desired width of each column (clamps desiredwidth to MinWidth and MaxWidth) + if (Columns != null) { - DependencyObject child = VisualTreeHelper.GetChild(parent, i); - if (child is UIElement element) + foreach (GridViewColumn column in Columns.OfType()) { - list.Add(element); + column.UpdateDesiredWidth(); } } - return list; + return base.ArrangeOverride(arrangeSize); } - */ } diff --git a/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs b/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs index e997d7574..4f7006531 100644 --- a/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs @@ -1,5 +1,3 @@ -using System.Windows.Controls; - namespace Wpf.Ui.Controls; /// @@ -7,49 +5,17 @@ namespace Wpf.Ui.Controls; /// public class GridViewRowPresenter : System.Windows.Controls.GridViewRowPresenter { - /// - /// GridViewRowPresenter computes the position of its children inside each child's Margin and calls Arrange - /// on each child. - /// - /// Size the GridViewRowPresenter will assume. - /// The actual size used. protected override Size ArrangeOverride(Size arrangeSize) { - _ = base.ArrangeOverride(arrangeSize); - - // exit early if columns are not Wpf.Ui.Controls.GridViewColumn - if (Columns == null || Columns.Count == 0 || Columns[0] is not GridViewColumn) - { - return arrangeSize; - } - - double accumulatedWidth = 0; - var remainingWidth = arrangeSize.Width; - - for (var i = 0; i < Columns.Count; ++i) + // update the desired width of each column (clamps desiredwidth to MinWidth and MaxWidth) + if (Columns != null) { - if (Columns[i] is not GridViewColumn col) + foreach (GridViewColumn column in Columns.OfType()) { - continue; + column.UpdateDesiredWidth(); } - - // use ActualIndex to track reordering when columns are dragged around - var visualIndex = col.ActualIndex; - if (VisualTreeHelper.GetChild(this, visualIndex) is not UIElement child) - { - continue; - } - - var clampedWidth = Math.Min(Math.Max(col.DesiredWidth, col.MinWidth), col.MaxWidth); - clampedWidth = Math.Max(0, Math.Min(clampedWidth, remainingWidth)); - - var rect = new Rect(accumulatedWidth, 0, clampedWidth, arrangeSize.Height); - child.Arrange(rect); - - remainingWidth -= clampedWidth; - accumulatedWidth += clampedWidth; } - return arrangeSize; + return base.ArrangeOverride(arrangeSize); } } From e68dc636e00c56b86ab5763583840b27071af596 Mon Sep 17 00:00:00 2001 From: koal44 Date: Tue, 19 Mar 2024 19:12:02 -0700 Subject: [PATCH 4/5] Isolate ui:ListView from vanilla framework ListView completely style ListViewItem to a custom one semove global styling of GridViewColumnHeader In this way, a user using ListView will see the vanilla version and when using ui:ListiView, ui:GridView and ui:GridViewColumn - they will see the ui library version --- .../Views/Pages/Collections/ListViewPage.xaml | 8 ++--- .../AutoSuggestBox/AutoSuggestBox.xaml | 2 +- src/Wpf.Ui/Controls/GridView/GridView.cs | 21 +++++++++++++ .../Controls/GridView/GridViewColumn.cs | 4 +-- .../GridView/GridViewColumnHeader.xaml | 31 +++++++++++++++++++ src/Wpf.Ui/Controls/ListView/ListView.cs | 14 +++++++-- src/Wpf.Ui/Controls/ListView/ListView.xaml | 19 ------------ src/Wpf.Ui/Controls/ListView/ListViewItem.cs | 10 ++++++ .../Controls/ListView/ListViewItem.xaml | 8 ++--- src/Wpf.Ui/Resources/Wpf.Ui.xaml | 1 + 10 files changed, 86 insertions(+), 32 deletions(-) create mode 100644 src/Wpf.Ui/Controls/GridView/GridView.cs create mode 100644 src/Wpf.Ui/Controls/GridView/GridViewColumnHeader.xaml create mode 100644 src/Wpf.Ui/Controls/ListView/ListViewItem.cs diff --git a/src/Wpf.Ui.Gallery/Views/Pages/Collections/ListViewPage.xaml b/src/Wpf.Ui.Gallery/Views/Pages/Collections/ListViewPage.xaml index d5d5a2acc..e03078edc 100644 --- a/src/Wpf.Ui.Gallery/Views/Pages/Collections/ListViewPage.xaml +++ b/src/Wpf.Ui.Gallery/Views/Pages/Collections/ListViewPage.xaml @@ -121,11 +121,11 @@ <ListView ItemsSource="{Binding ViewModel.BasicListViewItems}">\n \t<ListView.View>\n - \t\t<GridView>\n + \t\t<ui:GridView>\n \t\t\t<GridViewColumn DisplayMemberBinding="{Binding FirstName}" Header="First Name" MinWidth=&quot;100&quot; MaxWidth=&quot;200&quot;/>\n \t\t\t<GridViewColumn DisplayMemberBinding="{Binding LastName}" Header="Last Name"/>\n \t\t\t<GridViewColumn DisplayMemberBinding="{Binding Company}" Header="Company"/>\n - \t\t</GridView>\n + \t\t</ui:GridView>\n \t</ListView.View>\n </ListView> @@ -135,7 +135,7 @@ BorderThickness="0" ItemsSource="{Binding ViewModel.BasicListViewItems, Mode=TwoWay}"> - + - + diff --git a/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml b/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml index 389daa086..5465b4a70 100644 --- a/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml +++ b/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml @@ -13,7 +13,7 @@ 24 14 - + + \ No newline at end of file diff --git a/src/Wpf.Ui/Controls/ListView/ListView.cs b/src/Wpf.Ui/Controls/ListView/ListView.cs index 8520cd916..abc83f1d1 100644 --- a/src/Wpf.Ui/Controls/ListView/ListView.cs +++ b/src/Wpf.Ui/Controls/ListView/ListView.cs @@ -7,14 +7,14 @@ namespace Wpf.Ui.Controls; /// /// <ui:ListView ItemsSource="{Binding ...}" > /// <ui:ListView.View> -/// <GridView> +/// <ui:GridView> /// <GridViewColumn /// DisplayMemberBinding="{Binding FirstName}" /// Header="First Name" /> /// <GridViewColumn /// DisplayMemberBinding="{Binding LastName}" /// Header="Last Name" /> -/// </GridView> +/// </ui:GridView> /// </ui:ListView.View> /// </ui:ListView> /// @@ -85,4 +85,14 @@ static ListView() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ListView), new FrameworkPropertyMetadata(typeof(ListView))); } + + protected override DependencyObject GetContainerForItemOverride() + { + return new ListViewItem(); + } + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is ListViewItem; + } } diff --git a/src/Wpf.Ui/Controls/ListView/ListView.xaml b/src/Wpf.Ui/Controls/ListView/ListView.xaml index 818c3e8d6..4f53ba78b 100644 --- a/src/Wpf.Ui/Controls/ListView/ListView.xaml +++ b/src/Wpf.Ui/Controls/ListView/ListView.xaml @@ -115,25 +115,6 @@ - - - - + - + - - + + + + + + + + + + + + + + + + + diff --git a/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowIndicator.xaml b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowIndicator.xaml new file mode 100644 index 000000000..77537437e --- /dev/null +++ b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowIndicator.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs index 04816364c..d16d22226 100644 --- a/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs @@ -3,6 +3,10 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. +using System.Diagnostics; +using System.Reflection; +using System.Windows.Controls; + namespace Wpf.Ui.Controls; /// @@ -10,6 +14,11 @@ namespace Wpf.Ui.Controls; /// public class GridViewHeaderRowPresenter : System.Windows.Controls.GridViewHeaderRowPresenter { + public GridViewHeaderRowPresenter() + { + Loaded += OnLoaded; + } + protected override Size ArrangeOverride(Size arrangeSize) { // update the desired width of each column (clamps desiredwidth to MinWidth and MaxWidth) @@ -23,4 +32,53 @@ protected override Size ArrangeOverride(Size arrangeSize) return base.ArrangeOverride(arrangeSize); } + + protected override Size MeasureOverride(Size constraint) + { + if (Columns != null) + { + foreach (GridViewColumn column in Columns.OfType()) + { + column.UpdateDesiredWidth(); + } + } + + return base.MeasureOverride(constraint); + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + UpdateIndicatorStyle(); + } + + private void UpdateIndicatorStyle() + { + FieldInfo? indicatorField = typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_indicator", BindingFlags.NonPublic | BindingFlags.Instance); + + if (indicatorField == null) + { + Debug.WriteLine("Failed to get the _indicator field"); + return; + } + + if (indicatorField.GetValue(this) is Separator indicator) + { + indicator.Margin = new Thickness(0); + indicator.Width = 3.0; + + ResourceDictionary resourceDictionary = new() + { + Source = new Uri("pack://application:,,,/Wpf.Ui;component/Controls/GridView/GridViewHeaderRowIndicator.xaml", UriKind.Absolute) + }; + + if (resourceDictionary["GridViewHeaderRowIndicatorTemplate"] is ControlTemplate template) + { + indicator.Template = template; + } + else + { + Debug.WriteLine("Failed to get the GridViewHeaderRowIndicatorTemplate"); + } + } + } } diff --git a/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs b/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs index 4f7006531..0ba3e9334 100644 --- a/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewRowPresenter.cs @@ -1,3 +1,6 @@ +using System.Reflection; +using System.Windows.Controls; + namespace Wpf.Ui.Controls; /// @@ -18,4 +21,17 @@ protected override Size ArrangeOverride(Size arrangeSize) return base.ArrangeOverride(arrangeSize); } + + protected override Size MeasureOverride(Size constraint) + { + if (Columns != null) + { + foreach (GridViewColumn column in Columns.OfType()) + { + column.UpdateDesiredWidth(); + } + } + + return base.MeasureOverride(constraint); + } } diff --git a/src/Wpf.Ui/Controls/ListView/ListView.xaml b/src/Wpf.Ui/Controls/ListView/ListView.xaml index 4f53ba78b..6e6eb9711 100644 --- a/src/Wpf.Ui/Controls/ListView/ListView.xaml +++ b/src/Wpf.Ui/Controls/ListView/ListView.xaml @@ -59,8 +59,9 @@ Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"> +