From dd5cf143b24e7658faff45c71c076d3b62d8e6a9 Mon Sep 17 00:00:00 2001 From: koal44 Date: Thu, 29 Feb 2024 17:03:54 -0800 Subject: [PATCH 01/19] 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 02/19] 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 22:29:24 +0100 Subject: [PATCH 03/19] Update labeler.yml --- .github/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index cb16e8682..f1ce31c7d 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -46,7 +46,7 @@ controls: navigation: - changed-files: - - any-glob-to-any-file: 'src/Wpf.Ui/Controls/NavigationView/' + - any-glob-to-any-file: 'src/Wpf.Ui/Controls/NavigationView/**' gallery: - changed-files: From 7c70b151fca8b8050e7793d69808b82a194f495c Mon Sep 17 00:00:00 2001 From: koal44 Date: Tue, 19 Mar 2024 16:02:02 -0700 Subject: [PATCH 04/19] 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 05/19] 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"> + Date: Sun, 24 Mar 2024 21:13:31 +0100 Subject: [PATCH 18/19] Format code --- src/Wpf.Ui.Tray/Interop/User32.cs | 10 ++-- src/Wpf.Ui/Controls/GridView/GridView.cs | 16 +++++-- .../Controls/GridView/GridViewColumn.cs | 46 ++++++++++++++----- .../GridView/GridViewHeaderRowPresenter.cs | 17 +++++-- src/Wpf.Ui/Controls/ListView/ListViewItem.cs | 4 +- src/Wpf.Ui/Interop/User32.cs | 10 ++-- 6 files changed, 68 insertions(+), 35 deletions(-) diff --git a/src/Wpf.Ui.Tray/Interop/User32.cs b/src/Wpf.Ui.Tray/Interop/User32.cs index 021ffe01e..86397e655 100644 --- a/src/Wpf.Ui.Tray/Interop/User32.cs +++ b/src/Wpf.Ui.Tray/Interop/User32.cs @@ -860,7 +860,7 @@ public struct WNDCLASSEX public static extern bool AdjustWindowRectEx( [In] ref Rect lpRect, [In] WS dwStyle, - [In][MarshalAs(UnmanagedType.Bool)] bool bMenu, + [In] [MarshalAs(UnmanagedType.Bool)] bool bMenu, [In] WS_EX dwExStyle ); @@ -982,8 +982,8 @@ [In] IntPtr lParam [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr CreateWindowExW( [In] WS_EX dwExStyle, - [In, Optional][MarshalAs(UnmanagedType.LPWStr)] string lpClassName, - [In, Optional][MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, + [In, Optional] [MarshalAs(UnmanagedType.LPWStr)] string lpClassName, + [In, Optional] [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, [In] WS dwStyle, [In] int x, [In] int y, @@ -1423,7 +1423,7 @@ [In] IntPtr lParam [DllImport(Libraries.User32)] public static extern IntPtr GetSystemMenu( [In] IntPtr hWnd, - [In][MarshalAs(UnmanagedType.Bool)] bool bRevert + [In] [MarshalAs(UnmanagedType.Bool)] bool bRevert ); [DllImport(Libraries.User32, EntryPoint = "EnableMenuItem")] @@ -1447,7 +1447,7 @@ public static MF EnableMenuItem([In] IntPtr hMenu, [In] SC uIDEnableItem, [In] M private static extern int _SetWindowRgn( [In] IntPtr hWnd, [In] IntPtr hRgn, - [In][MarshalAs(UnmanagedType.Bool)] bool bRedraw + [In] [MarshalAs(UnmanagedType.Bool)] bool bRedraw ); /// diff --git a/src/Wpf.Ui/Controls/GridView/GridView.cs b/src/Wpf.Ui/Controls/GridView/GridView.cs index 70cf99c91..974c54b13 100644 --- a/src/Wpf.Ui/Controls/GridView/GridView.cs +++ b/src/Wpf.Ui/Controls/GridView/GridView.cs @@ -25,13 +25,19 @@ public class GridView : System.Windows.Controls.GridView { static GridView() { - ResourceDictionary resourceDict = new() - { - Source = new Uri("pack://application:,,,/Wpf.Ui;component/Controls/GridView/GridViewColumnHeader.xaml") - }; + ResourceDictionary resourceDict = + new() + { + Source = new Uri( + "pack://application:,,,/Wpf.Ui;component/Controls/GridView/GridViewColumnHeader.xaml" + ) + }; Style defaultStyle = (Style)resourceDict["UiGridViewColumnHeaderStyle"]; - ColumnHeaderContainerStyleProperty.OverrideMetadata(typeof(GridView), new FrameworkPropertyMetadata(defaultStyle)); + ColumnHeaderContainerStyleProperty.OverrideMetadata( + typeof(GridView), + new FrameworkPropertyMetadata(defaultStyle) + ); } } diff --git a/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs b/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs index e34049630..7a29fabdd 100644 --- a/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewColumn.cs @@ -29,19 +29,28 @@ namespace Wpf.Ui.Controls; public class GridViewColumn : System.Windows.Controls.GridViewColumn { // use reflection to get the `_desiredWidth` private field. - private static readonly Lazy _desiredWidthField = new(() => - typeof(System.Windows.Controls.GridViewColumn).GetField("_desiredWidth", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `_desiredWidth` field was not found.")); + private static readonly Lazy _desiredWidthField = + new( + () => + typeof(System.Windows.Controls.GridViewColumn).GetField( + "_desiredWidth", + BindingFlags.NonPublic | BindingFlags.Instance + ) ?? throw new InvalidOperationException("The `_desiredWidth` field was not found.") + ); private static FieldInfo DesiredWidthField => _desiredWidthField.Value; // use reflection to get the `UpdateActualWidth` private method. - private static readonly Lazy _updateActualWidthMethod = new(() => - { - MethodInfo methodInfo = typeof(System.Windows.Controls.GridViewColumn).GetMethod("UpdateActualWidth", BindingFlags.NonPublic | BindingFlags.Instance) - ?? throw new InvalidOperationException("The `UpdateActualWidth` method was not found."); - return methodInfo; - }); + private static readonly Lazy _updateActualWidthMethod = + new(() => + { + MethodInfo methodInfo = + typeof(System.Windows.Controls.GridViewColumn).GetMethod( + "UpdateActualWidth", + BindingFlags.NonPublic | BindingFlags.Instance + ) ?? throw new InvalidOperationException("The `UpdateActualWidth` method was not found."); + return methodInfo; + }); private static MethodInfo UpdateActualWidthMethod => _updateActualWidthMethod.Value; @@ -56,7 +65,10 @@ public class GridViewColumn : System.Windows.Controls.GridViewColumn /// internal void UpdateDesiredWidth() { - double currentWidth = (double)(DesiredWidthField.GetValue(this) ?? throw new InvalidOperationException("Failed to get the current `_desiredWidth`.")); + double currentWidth = (double)( + DesiredWidthField.GetValue(this) + ?? throw new InvalidOperationException("Failed to get the current `_desiredWidth`.") + ); double clampedWidth = Math.Max(MinWidth, Math.Min(currentWidth, MaxWidth)); DesiredWidthField.SetValue(this, clampedWidth); _ = UpdateActualWidthMethod.Invoke(this, null); @@ -72,7 +84,12 @@ public double MinWidth } /// Identifies the dependency property. - public static readonly DependencyProperty MinWidthProperty = DependencyProperty.Register(nameof(MinWidth), typeof(double), typeof(GridViewColumn), new FrameworkPropertyMetadata(0.0, OnMinWidthChanged)); + 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) { @@ -99,7 +116,12 @@ public double MaxWidth } /// Identifies the dependency property. - public static readonly DependencyProperty MaxWidthProperty = DependencyProperty.Register(nameof(MaxWidth), typeof(double), typeof(GridViewColumn), new FrameworkPropertyMetadata(double.PositiveInfinity, OnMaxWidthChanged)); + 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) { diff --git a/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs index d16d22226..2e99fad90 100644 --- a/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs +++ b/src/Wpf.Ui/Controls/GridView/GridViewHeaderRowPresenter.cs @@ -53,7 +53,10 @@ private void OnLoaded(object sender, RoutedEventArgs e) private void UpdateIndicatorStyle() { - FieldInfo? indicatorField = typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField("_indicator", BindingFlags.NonPublic | BindingFlags.Instance); + FieldInfo? indicatorField = typeof(System.Windows.Controls.GridViewHeaderRowPresenter).GetField( + "_indicator", + BindingFlags.NonPublic | BindingFlags.Instance + ); if (indicatorField == null) { @@ -66,10 +69,14 @@ private void UpdateIndicatorStyle() 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) - }; + ResourceDictionary resourceDictionary = + new() + { + Source = new Uri( + "pack://application:,,,/Wpf.Ui;component/Controls/GridView/GridViewHeaderRowIndicator.xaml", + UriKind.Absolute + ) + }; if (resourceDictionary["GridViewHeaderRowIndicatorTemplate"] is ControlTemplate template) { diff --git a/src/Wpf.Ui/Controls/ListView/ListViewItem.cs b/src/Wpf.Ui/Controls/ListView/ListViewItem.cs index 5c5b7f622..e3cfd9e5b 100644 --- a/src/Wpf.Ui/Controls/ListView/ListViewItem.cs +++ b/src/Wpf.Ui/Controls/ListView/ListViewItem.cs @@ -5,6 +5,4 @@ namespace Wpf.Ui.Controls; -public class ListViewItem : System.Windows.Controls.ListViewItem -{ -} +public class ListViewItem : System.Windows.Controls.ListViewItem { } diff --git a/src/Wpf.Ui/Interop/User32.cs b/src/Wpf.Ui/Interop/User32.cs index b887636eb..41b7ad651 100644 --- a/src/Wpf.Ui/Interop/User32.cs +++ b/src/Wpf.Ui/Interop/User32.cs @@ -872,7 +872,7 @@ public struct WNDCLASSEX public static extern bool AdjustWindowRectEx( [In] ref Rect lpRect, [In] WS dwStyle, - [In][MarshalAs(UnmanagedType.Bool)] bool bMenu, + [In] [MarshalAs(UnmanagedType.Bool)] bool bMenu, [In] WS_EX dwExStyle ); @@ -994,8 +994,8 @@ [In] IntPtr lParam [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr CreateWindowExW( [In] WS_EX dwExStyle, - [In, Optional][MarshalAs(UnmanagedType.LPWStr)] string lpClassName, - [In, Optional][MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, + [In, Optional] [MarshalAs(UnmanagedType.LPWStr)] string lpClassName, + [In, Optional] [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, [In] WS dwStyle, [In] int x, [In] int y, @@ -1466,7 +1466,7 @@ [In] IntPtr lParam [DllImport(Libraries.User32)] public static extern IntPtr GetSystemMenu( [In] IntPtr hWnd, - [In][MarshalAs(UnmanagedType.Bool)] bool bRevert + [In] [MarshalAs(UnmanagedType.Bool)] bool bRevert ); [DllImport(Libraries.User32, EntryPoint = "EnableMenuItem")] @@ -1490,7 +1490,7 @@ public static MF EnableMenuItem([In] IntPtr hMenu, [In] SC uIDEnableItem, [In] M private static extern int _SetWindowRgn( [In] IntPtr hWnd, [In] IntPtr hRgn, - [In][MarshalAs(UnmanagedType.Bool)] bool bRedraw + [In] [MarshalAs(UnmanagedType.Bool)] bool bRedraw ); /// From 11026411972c16d2154168d7065d3b2879a79af9 Mon Sep 17 00:00:00 2001 From: pomianowski <13592821+pomianowski@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:26:20 +0100 Subject: [PATCH 19/19] Format code --- .editorconfig | 5 +- Directory.Build.props | 2 +- .../Wpf.Ui.Demo.Console.csproj | 1 + src/Wpf.Ui.Demo.Mvvm/Wpf.Ui.Demo.Mvvm.csproj | 1 + .../Wpf.Ui.Demo.Simple.csproj | 1 + .../Wpf.Ui.Blank.csproj | 2 +- .../Wpf.Ui.Compact.csproj | 2 +- .../Wpf.Ui.Fluent.csproj | 2 +- .../source.extension.vsixmanifest | 2 +- .../Windows/EditorWindowViewModel.cs | 8 +-- src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj | 1 + .../Controls/AutoSuggestBox/AutoSuggestBox.cs | 2 +- .../DynamicScrollViewer.cs | 59 ++++++++++--------- .../Controls/FluentWindow/FluentWindow.cs | 47 +++++++++++---- src/Wpf.Ui/Controls/IconElement/FontIcon.cs | 4 +- .../Controls/IconElement/IconElement.cs | 15 ++--- .../Controls/IconElement/IconSourceElement.cs | 4 +- src/Wpf.Ui/Controls/IconElement/ImageIcon.cs | 7 ++- src/Wpf.Ui/Controls/Menu/Menu.xaml.cs | 41 +++++++------ 19 files changed, 125 insertions(+), 81 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5b981191d..38c62724d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -51,7 +51,7 @@ file_header_template = This Source Code Form is subject to the terms of the MIT # this. and Me. preferences dotnet_style_qualification_for_event = false:warning -dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_field = false:warning dotnet_style_qualification_for_method = false:warning dotnet_style_qualification_for_property = false:warning @@ -388,9 +388,12 @@ dotnet_diagnostic.SA1633.severity = none dotnet_diagnostic.SA1634.severity = none dotnet_diagnostic.SA1652.severity = none +dotnet_diagnostic.IDE0009.severity = none + # Additional Stylecop Analyzers dotnet_diagnostic.SA1111.severity = none dotnet_diagnostic.SA1121.severity = none +dotnet_diagnostic.SA1204.severity = none dotnet_diagnostic.SA1208.severity = none dotnet_diagnostic.SA1518.severity = none dotnet_diagnostic.SA1615.severity = none diff --git a/Directory.Build.props b/Directory.Build.props index d44472ea3..a46c1bf6c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.0.2 + 3.0.3 12.0 true diff --git a/src/Wpf.Ui.Demo.Console/Wpf.Ui.Demo.Console.csproj b/src/Wpf.Ui.Demo.Console/Wpf.Ui.Demo.Console.csproj index 8112c10c0..fb5496e36 100644 --- a/src/Wpf.Ui.Demo.Console/Wpf.Ui.Demo.Console.csproj +++ b/src/Wpf.Ui.Demo.Console/Wpf.Ui.Demo.Console.csproj @@ -7,6 +7,7 @@ enable true wpfui.ico + $(NoWarn);SA1601 diff --git a/src/Wpf.Ui.Demo.Mvvm/Wpf.Ui.Demo.Mvvm.csproj b/src/Wpf.Ui.Demo.Mvvm/Wpf.Ui.Demo.Mvvm.csproj index e61101205..1f3462e02 100644 --- a/src/Wpf.Ui.Demo.Mvvm/Wpf.Ui.Demo.Mvvm.csproj +++ b/src/Wpf.Ui.Demo.Mvvm/Wpf.Ui.Demo.Mvvm.csproj @@ -10,6 +10,7 @@ app.manifest applicationIcon.ico AnyCPU;x64 + $(NoWarn);SA1601 diff --git a/src/Wpf.Ui.Demo.Simple/Wpf.Ui.Demo.Simple.csproj b/src/Wpf.Ui.Demo.Simple/Wpf.Ui.Demo.Simple.csproj index 964b9e2d9..1d6697725 100644 --- a/src/Wpf.Ui.Demo.Simple/Wpf.Ui.Demo.Simple.csproj +++ b/src/Wpf.Ui.Demo.Simple/Wpf.Ui.Demo.Simple.csproj @@ -9,6 +9,7 @@ app.manifest applicationIcon.ico AnyCPU;x64 + $(NoWarn);SA1601 diff --git a/src/Wpf.Ui.Extension.Template.Blank/Wpf.Ui.Blank.csproj b/src/Wpf.Ui.Extension.Template.Blank/Wpf.Ui.Blank.csproj index acda0a933..1eeee3598 100644 --- a/src/Wpf.Ui.Extension.Template.Blank/Wpf.Ui.Blank.csproj +++ b/src/Wpf.Ui.Extension.Template.Blank/Wpf.Ui.Blank.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Compact.csproj b/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Compact.csproj index acda0a933..1eeee3598 100644 --- a/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Compact.csproj +++ b/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Compact.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Fluent.csproj b/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Fluent.csproj index 2bbe477dd..833c8384d 100644 --- a/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Fluent.csproj +++ b/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Fluent.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/Wpf.Ui.Extension/source.extension.vsixmanifest b/src/Wpf.Ui.Extension/source.extension.vsixmanifest index bed72a96b..b1a287dd1 100644 --- a/src/Wpf.Ui.Extension/source.extension.vsixmanifest +++ b/src/Wpf.Ui.Extension/source.extension.vsixmanifest @@ -1,7 +1,7 @@ - + WPF UI WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly. https://github.com/lepoco/wpfui diff --git a/src/Wpf.Ui.Gallery/ViewModels/Windows/EditorWindowViewModel.cs b/src/Wpf.Ui.Gallery/ViewModels/Windows/EditorWindowViewModel.cs index 97c42d856..c7051b616 100644 --- a/src/Wpf.Ui.Gallery/ViewModels/Windows/EditorWindowViewModel.cs +++ b/src/Wpf.Ui.Gallery/ViewModels/Windows/EditorWindowViewModel.cs @@ -17,7 +17,7 @@ public partial class EditorWindowViewModel : ObservableObject private int _progress = 70; [ObservableProperty] - private string _currentlyOpenedFile = String.Empty; + private string _currentlyOpenedFile = string.Empty; [ObservableProperty] private Visibility _statusBarVisibility = Visibility.Visible; @@ -25,9 +25,9 @@ public partial class EditorWindowViewModel : ObservableObject [RelayCommand] public void OnStatusBarAction(string value) { - if (String.IsNullOrEmpty(value)) + if (string.IsNullOrEmpty(value)) + { return; - - switch (value) { } + } } } diff --git a/src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj b/src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj index b8e46f8ab..24c428ff6 100644 --- a/src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj +++ b/src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj @@ -11,6 +11,7 @@ Wpf.Ui.Gallery AnyCPU;x64;x86 10.0.18362.0 + $(NoWarn);SA1601 diff --git a/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.cs b/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.cs index f12c69043..5c8ae6acb 100644 --- a/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.cs +++ b/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.cs @@ -451,7 +451,7 @@ private void TextBoxOnPreviewKeyDown(object sender, KeyEventArgs e) return; } - _ = SuggestionsList.Focus(); + _ = SuggestionsList?.Focus(); } private void TextBoxOnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) diff --git a/src/Wpf.Ui/Controls/DynamicScrollViewer/DynamicScrollViewer.cs b/src/Wpf.Ui/Controls/DynamicScrollViewer/DynamicScrollViewer.cs index 2c7cc292e..fd7759e10 100644 --- a/src/Wpf.Ui/Controls/DynamicScrollViewer/DynamicScrollViewer.cs +++ b/src/Wpf.Ui/Controls/DynamicScrollViewer/DynamicScrollViewer.cs @@ -11,8 +11,8 @@ namespace Wpf.Ui.Controls; /// /// Custom with events depending on actions taken by the user. /// -//[ToolboxItem(true)] -//[ToolboxBitmap(typeof(DynamicScrollViewer), "DynamicScrollViewer.bmp")] +// [ToolboxItem(true)] +// [ToolboxBitmap(typeof(DynamicScrollViewer), "DynamicScrollViewer.bmp")] [DefaultEvent("ScrollChangedEvent")] public class DynamicScrollViewer : PassiveScrollViewer { @@ -70,7 +70,7 @@ public class DynamicScrollViewer : PassiveScrollViewer ); /// - /// Gets or sets information whether the user was scrolling vertically for the last few seconds. + /// Gets or sets a value indicating whether the user was scrolling vertically for the last few seconds. /// public bool IsScrollingVertically { @@ -79,7 +79,7 @@ public bool IsScrollingVertically } /// - /// Gets or sets information whether the user was scrolling horizontally for the last few seconds. + /// Gets or sets a value indicating whether the user was scrolling horizontally for the last few seconds. /// public bool IsScrollingHorizontally { @@ -105,16 +105,6 @@ public int Timeout set => SetValue(TimeoutProperty, value); } - //protected override void OnPreviewMouseWheel(MouseWheelEventArgs e) - //{ - // base.OnPreviewMouseWheel(e); - //} - - //protected override void OnKeyDown(KeyEventArgs e) - //{ - // base.OnKeyDown(e); - //} - /// /// OnScrollChanged is an override called whenever scrolling state changes on this . /// @@ -127,16 +117,15 @@ protected override void OnScrollChanged(ScrollChangedEventArgs e) { base.OnScrollChanged(e); - //#if DEBUG - // System.Diagnostics.Debug.WriteLine($"DEBUG | {typeof(DynamicScrollBar)}.{nameof(e.VerticalChange)} - {e.VerticalChange}", "WPFUI"); - // System.Diagnostics.Debug.WriteLine($"DEBUG | {typeof(DynamicScrollBar)}.{nameof(e.HorizontalChange)} - {e.HorizontalChange}", "WPFUI"); - //#endif - if (e.HorizontalChange > _minimalChange || e.HorizontalChange < -_minimalChange) + { UpdateHorizontalScrollingState(); + } if (e.VerticalChange > _minimalChange || e.VerticalChange < -_minimalChange) + { UpdateVerticalScrollingState(); + } } private async void UpdateVerticalScrollingState() @@ -146,17 +135,22 @@ private async void UpdateVerticalScrollingState() // If more than Timeout has passed since the last event, there is no interaction. // We pass this value to the ScrollBar and link it to IsMouseOver. // This way we have a dynamic scrollbar that responds to scroll / mouse over. - - var currentEvent = _verticalIdentifier.GetNext(); + long currentEvent = _verticalIdentifier.GetNext(); if (!_scrollingVertically) - IsScrollingVertically = true; + { + SetCurrentValue(IsScrollingVerticallyProperty, true); + } if (_timeout > -1) + { await Task.Delay(_timeout < 10000 ? _timeout : 1000); + } if (_verticalIdentifier.IsEqual(currentEvent) && _scrollingVertically) - IsScrollingVertically = false; + { + SetCurrentValue(IsScrollingVerticallyProperty, false); + } } private async void UpdateHorizontalScrollingState() @@ -166,16 +160,19 @@ private async void UpdateHorizontalScrollingState() // If more than Timeout has passed since the last event, there is no interaction. // We pass this value to the ScrollBar and link it to IsMouseOver. // This way we have a dynamic scrollbar that responds to scroll / mouse over. - - var currentEvent = _horizontalIdentifier.GetNext(); + long currentEvent = _horizontalIdentifier.GetNext(); if (!_scrollingHorizontally) - IsScrollingHorizontally = true; + { + SetCurrentValue(IsScrollingHorizontallyProperty, true); + } await Task.Delay(Timeout < 10000 ? Timeout : 1000); if (_horizontalIdentifier.IsEqual(currentEvent) && _scrollingHorizontally) - IsScrollingHorizontally = false; + { + SetCurrentValue(IsScrollingHorizontallyProperty, false); + } } private static void IsScrollingVerticallyProperty_OnChanged( @@ -184,7 +181,9 @@ DependencyPropertyChangedEventArgs e ) { if (d is not DynamicScrollViewer scroll) + { return; + } scroll._scrollingVertically = scroll.IsScrollingVertically; } @@ -195,7 +194,9 @@ DependencyPropertyChangedEventArgs e ) { if (d is not DynamicScrollViewer scroll) + { return; + } scroll._scrollingHorizontally = scroll.IsScrollingHorizontally; } @@ -206,7 +207,9 @@ DependencyPropertyChangedEventArgs e ) { if (d is not DynamicScrollViewer scroll) + { return; + } scroll._minimalChange = scroll.MinimalChange; } @@ -214,7 +217,9 @@ DependencyPropertyChangedEventArgs e private static void TimeoutProperty_OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not DynamicScrollViewer scroll) + { return; + } scroll._timeout = scroll.Timeout; } diff --git a/src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs b/src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs index c76bb4f41..75c4231c3 100644 --- a/src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs +++ b/src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs @@ -12,8 +12,8 @@ namespace Wpf.Ui.Controls; /// /// A custom WinUI Window with more convenience methods. /// -//[ToolboxItem(true)] -//[ToolboxBitmap(typeof(FluentWindow), "FluentWindow.bmp")] +// [ToolboxItem(true)] +// [ToolboxBitmap(typeof(FluentWindow), "FluentWindow.bmp")] public class FluentWindow : System.Windows.Window { private WindowInteropHelper? _interopHelper = null; @@ -85,7 +85,7 @@ public bool ExtendsContentIntoTitleBar } /// - /// Creates new instance and sets default style. + /// Initializes static members of the class. /// public FluentWindow() { @@ -93,8 +93,12 @@ public FluentWindow() } /// + /// Initializes static members of the class. /// Overrides default properties. /// + /// + /// Overrides default properties. + /// static FluentWindow() { DefaultStyleKeyProperty.OverrideMetadata( @@ -119,10 +123,14 @@ protected override void OnSourceInitialized(EventArgs e) private static void OnCornerPreferenceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not FluentWindow window) + { return; + } if (e.OldValue == e.NewValue) + { return; + } window.OnCornerPreferenceChanged( (WindowCornerPreference)e.OldValue, @@ -139,9 +147,11 @@ WindowCornerPreference newValue ) { if (InteropHelper.Handle == IntPtr.Zero) + { return; + } - UnsafeNativeMethods.ApplyWindowCornerPreference(InteropHelper.Handle, newValue); + _ = UnsafeNativeMethods.ApplyWindowCornerPreference(InteropHelper.Handle, newValue); } /// @@ -150,10 +160,14 @@ WindowCornerPreference newValue private static void OnBackdropTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not FluentWindow window) + { return; + } if (e.OldValue == e.NewValue) + { return; + } window.OnBackdropTypeChanged((WindowBackdropType)e.OldValue, (WindowBackdropType)e.NewValue); } @@ -169,21 +183,28 @@ protected virtual void OnBackdropTypeChanged(WindowBackdropType oldValue, Window } if (InteropHelper.Handle == IntPtr.Zero) + { return; + } if (newValue == WindowBackdropType.None) { - WindowBackdrop.RemoveBackdrop(this); + _ = WindowBackdrop.RemoveBackdrop(this); + return; } if (!ExtendsContentIntoTitleBar) + { throw new InvalidOperationException( $"Cannot apply backdrop effect if {nameof(ExtendsContentIntoTitleBar)} is false." ); + } if (WindowBackdrop.IsSupported(newValue) && WindowBackdrop.RemoveBackground(this)) - WindowBackdrop.ApplyBackdrop(this, newValue); + { + _ = WindowBackdrop.ApplyBackdrop(this, newValue); + } } /// @@ -195,10 +216,14 @@ DependencyPropertyChangedEventArgs e ) { if (d is not FluentWindow window) + { return; + } if (e.OldValue == e.NewValue) + { return; + } window.OnExtendsContentIntoTitleBarChanged((bool)e.OldValue, (bool)e.NewValue); } @@ -208,8 +233,8 @@ DependencyPropertyChangedEventArgs e /// protected virtual void OnExtendsContentIntoTitleBarChanged(bool oldValue, bool newValue) { - WindowStyle = WindowStyle.SingleBorderWindow; - //AllowsTransparency = true; + // AllowsTransparency = true; + SetCurrentValue(WindowStyleProperty, WindowStyle.SingleBorderWindow); WindowChrome.SetWindowChrome( this, @@ -223,8 +248,8 @@ protected virtual void OnExtendsContentIntoTitleBarChanged(bool oldValue, bool n } ); - UnsafeNativeMethods.RemoveWindowTitlebarContents(this); - ////WindowStyleProperty.OverrideMetadata(typeof(FluentWindow), new FrameworkPropertyMetadata(WindowStyle.SingleBorderWindow)); - ////AllowsTransparencyProperty.OverrideMetadata(typeof(FluentWindow), new FrameworkPropertyMetadata(false)); + // WindowStyleProperty.OverrideMetadata(typeof(FluentWindow), new FrameworkPropertyMetadata(WindowStyle.SingleBorderWindow)); + // AllowsTransparencyProperty.OverrideMetadata(typeof(FluentWindow), new FrameworkPropertyMetadata(false)); + _ = UnsafeNativeMethods.RemoveWindowTitlebarContents(this); } } diff --git a/src/Wpf.Ui/Controls/IconElement/FontIcon.cs b/src/Wpf.Ui/Controls/IconElement/FontIcon.cs index 4336df477..bc3e7ee3c 100644 --- a/src/Wpf.Ui/Controls/IconElement/FontIcon.cs +++ b/src/Wpf.Ui/Controls/IconElement/FontIcon.cs @@ -15,8 +15,8 @@ namespace Wpf.Ui.Controls; /// /// Represents an icon that uses a glyph from the specified font. /// -//[ToolboxItem(true)] -//[ToolboxBitmap(typeof(FontIcon), "FontIcon.bmp")] +// [ToolboxItem(true)] +// [ToolboxBitmap(typeof(FontIcon), "FontIcon.bmp")] public class FontIcon : IconElement { #region Static properties diff --git a/src/Wpf.Ui/Controls/IconElement/IconElement.cs b/src/Wpf.Ui/Controls/IconElement/IconElement.cs index 1f97a7d4b..978d703f5 100644 --- a/src/Wpf.Ui/Controls/IconElement/IconElement.cs +++ b/src/Wpf.Ui/Controls/IconElement/IconElement.cs @@ -49,31 +49,30 @@ public Brush Foreground private Grid? _layoutRoot; - #region Protected methods - protected abstract UIElement InitializeChildren(); protected virtual void OnForegroundPropertyChanged(DependencyPropertyChangedEventArgs args) { } - #endregion - - #region Layout methods - private void EnsureLayoutRoot() { if (_layoutRoot != null) + { return; + } _layoutRoot = new Grid { Background = Brushes.Transparent, SnapsToDevicePixels = true, }; - _layoutRoot.Children.Add(InitializeChildren()); + _ = _layoutRoot.Children.Add(InitializeChildren()); + AddVisualChild(_layoutRoot); } protected override Visual GetVisualChild(int index) { if (index != 0) + { throw new ArgumentOutOfRangeException(nameof(index)); + } EnsureLayoutRoot(); return _layoutRoot!; @@ -94,6 +93,4 @@ protected override Size ArrangeOverride(Size finalSize) _layoutRoot!.Arrange(new Rect(new Point(), finalSize)); return finalSize; } - - #endregion } diff --git a/src/Wpf.Ui/Controls/IconElement/IconSourceElement.cs b/src/Wpf.Ui/Controls/IconElement/IconSourceElement.cs index 425a92c1a..906ef1f19 100644 --- a/src/Wpf.Ui/Controls/IconElement/IconSourceElement.cs +++ b/src/Wpf.Ui/Controls/IconElement/IconSourceElement.cs @@ -30,13 +30,13 @@ public class IconSourceElement : IconElement /// public IconSource? IconSource { - get => (IconSource)GetValue(IconSourceProperty); + get => (IconSource?)GetValue(IconSourceProperty); set => SetValue(IconSourceProperty, value); } protected override UIElement InitializeChildren() { - //TODO come up with an elegant solution + // TODO: Come up with an elegant solution throw new InvalidOperationException($"Use {nameof(IconSourceElementConverter)} class."); } } diff --git a/src/Wpf.Ui/Controls/IconElement/ImageIcon.cs b/src/Wpf.Ui/Controls/IconElement/ImageIcon.cs index d6e0ab9ba..6a2a3968e 100644 --- a/src/Wpf.Ui/Controls/IconElement/ImageIcon.cs +++ b/src/Wpf.Ui/Controls/IconElement/ImageIcon.cs @@ -45,10 +45,13 @@ protected override UIElement InitializeChildren() private static void OnSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - var self = (ImageIcon)d; + ImageIcon self = (ImageIcon)d; + if (self.Image is null) + { return; + } - self.Image.Source = (ImageSource)e.NewValue; + self.Image.SetCurrentValue(System.Windows.Controls.Image.SourceProperty, (ImageSource?)e.NewValue); } } diff --git a/src/Wpf.Ui/Controls/Menu/Menu.xaml.cs b/src/Wpf.Ui/Controls/Menu/Menu.xaml.cs index 1d915e4a5..3562c176e 100644 --- a/src/Wpf.Ui/Controls/Menu/Menu.xaml.cs +++ b/src/Wpf.Ui/Controls/Menu/Menu.xaml.cs @@ -4,30 +4,37 @@ // All Rights Reserved. using System.Reflection; -using System.Windows; -namespace Wpf.Ui.Styles.Controls +namespace Wpf.Ui.Styles.Controls; + +/// +/// Extension to the menu. +/// +public partial class Menu : ResourceDictionary { /// - /// Extension to the menu. + /// Initializes a new instance of the class. /// - partial class Menu : ResourceDictionary + /// + /// Sets menu alignment on initialization. + /// + public Menu() { - /// - /// Sets menu alignment on initialization. - /// - public Menu() => Initialize(); + Initialize(); + } - private void Initialize() + private void Initialize() + { + if (!SystemParameters.MenuDropAlignment) { - if (!SystemParameters.MenuDropAlignment) - return; - - var fieldInfo = typeof(SystemParameters).GetField( - "_menuDropAlignment", - BindingFlags.NonPublic | BindingFlags.Static - ); - fieldInfo?.SetValue(null, false); + return; } + + FieldInfo? fieldInfo = typeof(SystemParameters).GetField( + "_menuDropAlignment", + BindingFlags.NonPublic | BindingFlags.Static + ); + + fieldInfo?.SetValue(null, false); } }