Files
FSI.BT.IR.Tools/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ListViewLayoutManager.cs
Maier Stephan SI 0b0508b042 Sicherung
2023-01-16 16:04:47 +01:00

623 lines
22 KiB
C#

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
namespace DJ.Helper.ListViewLayoutManager
{
public class ListViewLayoutManager
{
// ##############################################################################################################################
// Properties
// ##############################################################################################################################
#region Properties
// ##########################################################################################
// Public Properties
// ##########################################################################################
public ListView ListView => _ListView;
public ScrollBarVisibility VerticalScrollBarVisibility
{
get => _VerticalScrollBarVisibility;
set => _VerticalScrollBarVisibility = value;
}
// ##########################################################################################
// Private Properties
// ##########################################################################################
private readonly ListView _ListView;
private ScrollViewer _ScrollViewer;
private bool _Loaded;
private bool _Resizing;
private Cursor _ResizeCursor;
private ScrollBarVisibility _VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
private GridViewColumn _AutoSizedColumn;
private const double _ZERO_WIDTH_RANGE = 0.1;
#endregion
// ##############################################################################################################################
// AttachedProperties
// ##############################################################################################################################
#region AttachedProperties
public static void SetEnabled(DependencyObject dependencyObject, bool enabled)
{
dependencyObject.SetValue(EnabledProperty, enabled);
}
public static readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached("Enabled",
typeof(bool), typeof(ListViewLayoutManager), new FrameworkPropertyMetadata(_OnLayoutManagerEnabledChanged));
#endregion
// ##############################################################################################################################
// Constructor
// ##############################################################################################################################
#region Constructor
public ListViewLayoutManager(ListView listView)
{
_ListView = listView ?? throw new ArgumentNullException(nameof(listView));
_ListView.Loaded += ListView_Loaded;
_ListView.Unloaded += ListView_Unloaded;
}
private void ListView_Loaded(object sender, RoutedEventArgs e)
{
_RegisterEvents(_ListView);
_InitColumns();
_DoResizeColumns();
_Loaded = true;
}
private void ListView_Unloaded(object sender, RoutedEventArgs e)
{
if (!_Loaded)
{
return;
}
_UnRegisterEvents(_ListView);
_Loaded = false;
}
#endregion
// ##############################################################################################################################
// public methods
// ##############################################################################################################################
#region public methods
public void Refresh()
{
_InitColumns();
_DoResizeColumns();
}
protected virtual void ResizeColumns()
{
GridView view = _ListView.View as GridView;
if (view == null || view.Columns.Count == 0)
{
return;
}
// listview width
double actualWidth = double.PositiveInfinity;
if (_ScrollViewer != null)
{
actualWidth = _ScrollViewer.ViewportWidth;
}
if (double.IsInfinity(actualWidth))
{
actualWidth = _ListView.ActualWidth;
}
if (double.IsInfinity(actualWidth) || actualWidth <= 0)
{
return;
}
double resizeableRegionCount = 0;
double otherColumnsWidth = 0;
// determine column sizes
foreach (GridViewColumn gridViewColumn in view.Columns)
{
if (ProportionalColumn.IsProportionalColumn(gridViewColumn))
{
double? proportionalWidth = ProportionalColumn.GetProportionalWidth(gridViewColumn);
if (proportionalWidth != null)
{
resizeableRegionCount += proportionalWidth.Value;
}
}
else
{
otherColumnsWidth += gridViewColumn.ActualWidth;
}
}
if (resizeableRegionCount <= 0)
{
// no proportional columns present: commit the regulation to the scroll viewer
if (_ScrollViewer != null)
{
_ScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
}
// search the first fill column
GridViewColumn fillColumn = null;
for (int i = 0; i < view.Columns.Count; i++)
{
GridViewColumn gridViewColumn = view.Columns[i];
if (_IsFillColumn(gridViewColumn))
{
fillColumn = gridViewColumn;
break;
}
}
if (fillColumn != null)
{
double otherColumnsWithoutFillWidth = otherColumnsWidth - fillColumn.ActualWidth;
double fillWidth = actualWidth - otherColumnsWithoutFillWidth;
if (fillWidth > 0)
{
double? minWidth = RangeColumn.GetRangeMinWidth(fillColumn);
double? maxWidth = RangeColumn.GetRangeMaxWidth(fillColumn);
bool setWidth = !(minWidth.HasValue && fillWidth < minWidth.Value);
if (maxWidth.HasValue && fillWidth > maxWidth.Value)
{
setWidth = false;
}
if (setWidth)
{
if (_ScrollViewer != null)
{
_ScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
}
fillColumn.Width = fillWidth;
}
}
}
return;
}
double resizeableColumnsWidth = actualWidth - otherColumnsWidth;
if (resizeableColumnsWidth <= 0)
{
return; // missing space
}
// resize columns
double resizeableRegionWidth = resizeableColumnsWidth / resizeableRegionCount;
foreach (GridViewColumn gridViewColumn in view.Columns)
{
if (ProportionalColumn.IsProportionalColumn(gridViewColumn))
{
double? proportionalWidth = ProportionalColumn.GetProportionalWidth(gridViewColumn);
if (proportionalWidth != null)
{
gridViewColumn.Width = proportionalWidth.Value * resizeableRegionWidth;
}
}
}
}
#endregion
// ##############################################################################################################################
// private methods
// ##############################################################################################################################
#region private methods
private void _DoResizeColumns()
{
if (_Resizing)
{
return;
}
_Resizing = true;
try
{
ResizeColumns();
}
finally
{
_Resizing = false;
}
}
private void _RegisterEvents(DependencyObject start)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++)
{
Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual;
if (childVisual is Thumb)
{
GridViewColumn gridViewColumn = _FindParentColumn(childVisual);
if (gridViewColumn != null)
{
Thumb thumb = childVisual as Thumb;
if (ProportionalColumn.IsProportionalColumn(gridViewColumn) ||
FixedColumn.IsFixedColumn(gridViewColumn) || _IsFillColumn(gridViewColumn))
{
thumb.IsHitTestVisible = false;
}
else
{
thumb.PreviewMouseMove += _ThumbPreviewMouseMove;
thumb.PreviewMouseLeftButtonDown +=
_ThumbPreviewMouseLeftButtonDown;
DependencyPropertyDescriptor.FromProperty(
GridViewColumn.WidthProperty,
typeof(GridViewColumn)).AddValueChanged(gridViewColumn, _GridColumnWidthChanged);
}
}
}
else if (childVisual is GridViewColumnHeader)
{
GridViewColumnHeader columnHeader = childVisual as GridViewColumnHeader;
columnHeader.SizeChanged += _GridColumnHeaderSizeChanged;
}
else if (_ScrollViewer == null && childVisual is ScrollViewer)
{
_ScrollViewer = childVisual as ScrollViewer;
_ScrollViewer.ScrollChanged += _ScrollViewerScrollChanged;
// assume we do the regulation of the horizontal scrollbar
_ScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
_ScrollViewer.VerticalScrollBarVisibility = _VerticalScrollBarVisibility;
}
_RegisterEvents(childVisual);
}
}
private void _UnRegisterEvents(DependencyObject start)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++)
{
Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual;
if (childVisual is Thumb)
{
GridViewColumn gridViewColumn = _FindParentColumn(childVisual);
if (gridViewColumn != null)
{
Thumb thumb = childVisual as Thumb;
if (ProportionalColumn.IsProportionalColumn(gridViewColumn) ||
FixedColumn.IsFixedColumn(gridViewColumn) || _IsFillColumn(gridViewColumn))
{
thumb.IsHitTestVisible = true;
}
else
{
thumb.PreviewMouseMove -= _ThumbPreviewMouseMove;
thumb.PreviewMouseLeftButtonDown -=
_ThumbPreviewMouseLeftButtonDown;
DependencyPropertyDescriptor.FromProperty(
GridViewColumn.WidthProperty,
typeof(GridViewColumn)).RemoveValueChanged(gridViewColumn, _GridColumnWidthChanged);
}
}
}
else if (childVisual is GridViewColumnHeader)
{
GridViewColumnHeader columnHeader = childVisual as GridViewColumnHeader;
columnHeader.SizeChanged -= _GridColumnHeaderSizeChanged;
}
else if (_ScrollViewer == null && childVisual is ScrollViewer)
{
_ScrollViewer = childVisual as ScrollViewer;
_ScrollViewer.ScrollChanged -= _ScrollViewerScrollChanged;
}
_UnRegisterEvents(childVisual);
}
}
private GridViewColumn _FindParentColumn(DependencyObject element)
{
if (element == null)
{
return null;
}
while (element != null)
{
GridViewColumnHeader gridViewColumnHeader = element as GridViewColumnHeader;
if (gridViewColumnHeader != null)
{
return (gridViewColumnHeader).Column;
}
element = VisualTreeHelper.GetParent(element);
}
return null;
}
private GridViewColumnHeader _FindColumnHeader(DependencyObject start, GridViewColumn gridViewColumn)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++)
{
Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual;
if (childVisual is GridViewColumnHeader)
{
GridViewColumnHeader gridViewHeader = childVisual as GridViewColumnHeader;
if (gridViewHeader.Column == gridViewColumn)
{
return gridViewHeader;
}
}
GridViewColumnHeader childGridViewHeader = _FindColumnHeader(childVisual, gridViewColumn);
if (childGridViewHeader != null)
{
return childGridViewHeader;
}
}
return null;
}
private void _InitColumns()
{
GridView view = _ListView.View as GridView;
if (view == null)
{
return;
}
foreach (GridViewColumn gridViewColumn in view.Columns)
{
if (!RangeColumn.IsRangeColumn(gridViewColumn))
{
continue;
}
double? minWidth = RangeColumn.GetRangeMinWidth(gridViewColumn);
double? maxWidth = RangeColumn.GetRangeMaxWidth(gridViewColumn);
if (!minWidth.HasValue && !maxWidth.HasValue)
{
continue;
}
GridViewColumnHeader columnHeader = _FindColumnHeader(_ListView, gridViewColumn);
if (columnHeader == null)
{
continue;
}
double actualWidth = columnHeader.ActualWidth;
if (minWidth.HasValue)
{
columnHeader.MinWidth = minWidth.Value;
if (!double.IsInfinity(actualWidth) && actualWidth < columnHeader.MinWidth)
{
gridViewColumn.Width = columnHeader.MinWidth;
}
}
if (maxWidth.HasValue)
{
columnHeader.MaxWidth = maxWidth.Value;
if (!double.IsInfinity(actualWidth) && actualWidth > columnHeader.MaxWidth)
{
gridViewColumn.Width = columnHeader.MaxWidth;
}
}
}
}
// returns the delta
private double _SetRangeColumnToBounds(GridViewColumn gridViewColumn)
{
double startWidth = gridViewColumn.Width;
double? minWidth = RangeColumn.GetRangeMinWidth(gridViewColumn);
double? maxWidth = RangeColumn.GetRangeMaxWidth(gridViewColumn);
if ((minWidth.HasValue && maxWidth.HasValue) && (minWidth > maxWidth))
{
return 0; // invalid case
}
if (minWidth.HasValue && gridViewColumn.Width < minWidth.Value)
{
gridViewColumn.Width = minWidth.Value;
}
else if (maxWidth.HasValue && gridViewColumn.Width > maxWidth.Value)
{
gridViewColumn.Width = maxWidth.Value;
}
return gridViewColumn.Width - startWidth;
}
private bool _IsFillColumn(GridViewColumn gridViewColumn)
{
if (gridViewColumn == null)
{
return false;
}
GridView view = _ListView.View as GridView;
if (view == null || view.Columns.Count == 0)
{
return false;
}
bool? isFillColumn = RangeColumn.GetRangeIsFillColumn(gridViewColumn);
return isFillColumn.HasValue && isFillColumn.Value;
}
private void _ThumbPreviewMouseMove(object sender, MouseEventArgs e)
{
Thumb thumb = sender as Thumb;
if (thumb == null)
{
return;
}
GridViewColumn gridViewColumn = _FindParentColumn(thumb);
if (gridViewColumn == null)
{
return;
}
// suppress column resizing for proportional, fixed and range fill columns
if (ProportionalColumn.IsProportionalColumn(gridViewColumn) ||
FixedColumn.IsFixedColumn(gridViewColumn) ||
_IsFillColumn(gridViewColumn))
{
thumb.Cursor = null;
return;
}
// check range column bounds
if (thumb.IsMouseCaptured && RangeColumn.IsRangeColumn(gridViewColumn))
{
double? minWidth = RangeColumn.GetRangeMinWidth(gridViewColumn);
double? maxWidth = RangeColumn.GetRangeMaxWidth(gridViewColumn);
if ((minWidth.HasValue && maxWidth.HasValue) && (minWidth > maxWidth))
{
return; // invalid case
}
if (_ResizeCursor == null)
{
_ResizeCursor = thumb.Cursor; // save the resize cursor
}
if (minWidth.HasValue && gridViewColumn.Width <= minWidth.Value)
{
thumb.Cursor = Cursors.No;
}
else if (maxWidth.HasValue && gridViewColumn.Width >= maxWidth.Value)
{
thumb.Cursor = Cursors.No;
}
else
{
thumb.Cursor = _ResizeCursor; // between valid min/max
}
}
}
private void _ThumbPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Thumb thumb = sender as Thumb;
GridViewColumn gridViewColumn = _FindParentColumn(thumb);
// suppress column resizing for proportional, fixed and range fill columns
if (ProportionalColumn.IsProportionalColumn(gridViewColumn) ||
FixedColumn.IsFixedColumn(gridViewColumn) ||
_IsFillColumn(gridViewColumn))
{
e.Handled = true;
}
}
private void _GridColumnWidthChanged(object sender, EventArgs e)
{
if (!_Loaded)
{
return;
}
GridViewColumn gridViewColumn = sender as GridViewColumn;
// suppress column resizing for proportional and fixed columns
if (ProportionalColumn.IsProportionalColumn(gridViewColumn) || FixedColumn.IsFixedColumn(gridViewColumn))
{
return;
}
// ensure range column within the bounds
if (RangeColumn.IsRangeColumn(gridViewColumn))
{
// special case: auto column width - maybe conflicts with min/max range
if (gridViewColumn != null && gridViewColumn.Width.Equals(double.NaN))
{
_AutoSizedColumn = gridViewColumn;
return; // handled by the change header size event
}
// ensure column bounds
if (Math.Abs(_SetRangeColumnToBounds(gridViewColumn) - 0) > _ZERO_WIDTH_RANGE)
{
return;
}
}
_DoResizeColumns();
}
// handle autosized column
private void _GridColumnHeaderSizeChanged(object sender, SizeChangedEventArgs e)
{
if (_AutoSizedColumn == null)
{
return;
}
GridViewColumnHeader gridViewColumnHeader = sender as GridViewColumnHeader;
if (gridViewColumnHeader != null && gridViewColumnHeader.Column == _AutoSizedColumn)
{
if (gridViewColumnHeader.Width.Equals(double.NaN))
{
// sync column with
gridViewColumnHeader.Column.Width = gridViewColumnHeader.ActualWidth;
_DoResizeColumns();
}
_AutoSizedColumn = null;
}
}
private void _ScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (_Loaded && Math.Abs(e.ViewportWidthChange - 0) > _ZERO_WIDTH_RANGE)
{
_DoResizeColumns();
}
}
private static void _OnLayoutManagerEnabledChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
ListView listView = dependencyObject as ListView;
if (listView != null)
{
bool enabled = (bool) e.NewValue;
if (enabled)
{
new ListViewLayoutManager(listView);
}
}
}
#endregion
}
}