Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 105 additions & 14 deletions src/Shared/HandyControl_Shared/Controls/Panel/Grid/Row.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
Expand All @@ -11,9 +12,7 @@ namespace HandyControl.Controls;
public class Row : Panel
{
private ColLayoutStatus _layoutStatus;

private double _maxChildDesiredHeight;

private double _fixedWidth;

public static readonly DependencyProperty GutterProperty = DependencyProperty.Register(
Expand All @@ -30,18 +29,92 @@ public double Gutter
set => SetValue(GutterProperty, value);
}

#region 父布局监控
// 当视觉父发生变化,订阅父的 SizeChanged / LayoutUpdated,确保在父确定尺寸后重新测量
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
UnsubscribeParentSizeChanged(oldParent as FrameworkElement);

if (VisualParent is FrameworkElement newParent)
{
// 如果父已经有具体宽度,可以立即 InvalidateMeasure
if (newParent.ActualWidth > 0)
{
InvalidateMeasure();
}
else
{
// 等待父首次布局完成
newParent.SizeChanged += Parent_SizeChanged;
newParent.LayoutUpdated += Parent_LayoutUpdated;
}
}
}

private void UnsubscribeParentSizeChanged(FrameworkElement parent)
{
if (parent == null) return;
parent.SizeChanged -= Parent_SizeChanged;
parent.LayoutUpdated -= Parent_LayoutUpdated;
}

private void Parent_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (sender is FrameworkElement parent && parent.ActualWidth > 0)
{
UnsubscribeParentSizeChanged(parent);
Dispatcher.BeginInvoke(new Action(InvalidateMeasure), DispatcherPriority.Render);
}
}

private void Parent_LayoutUpdated(object sender, EventArgs e)
{
if (sender is FrameworkElement parent && parent.ActualWidth > 0)
{
UnsubscribeParentSizeChanged(parent);
Dispatcher.BeginInvoke(new Action(InvalidateMeasure), DispatcherPriority.Render);
}
}

#endregion

protected override Size MeasureOverride(Size constraint)
{
var gutter = Gutter;

// --- 防止无限约束 ---
if (double.IsInfinity(constraint.Width) || double.IsNaN(constraint.Width) || constraint.Width <= 0)
{
if (Parent is FrameworkElement parent && parent.ActualWidth > 0)
{
constraint = new Size(parent.ActualWidth, constraint.Height);
}
else
{
// 给出一个合理的默认宽度,避免返回 Infinity
constraint = new Size(100, constraint.Height);
// 延迟重新测量,等待父尺寸稳定
Dispatcher.BeginInvoke(new Action(InvalidateMeasure), DispatcherPriority.Render);
}
}

// 高度也进行防御
if (double.IsInfinity(constraint.Height) || double.IsNaN(constraint.Height))
constraint = new Size(constraint.Width, 100);

var localLayoutStatus = ColLayout.GetLayoutStatus(constraint.Width);

var totalCellCount = 0;
var totalRowCount = 1;
_fixedWidth = 0;
_maxChildDesiredHeight = 0;
var cols = InternalChildren.OfType<Col>().ToList();

// 先测量固定或不参与栅格的子元素
foreach (var child in cols)
{
var cellCount = child.GetLayoutCellCount(_layoutStatus);
var cellCount = child.GetLayoutCellCount(localLayoutStatus);
if (cellCount == 0 || child.IsFixed)
{
child.Measure(constraint);
Expand All @@ -50,18 +123,18 @@ protected override Size MeasureOverride(Size constraint)
}
}

var itemWidth = (constraint.Width - _fixedWidth + gutter) / ColLayout.ColMaxCellCount;
itemWidth = Math.Max(0, itemWidth);
var availableWidth = Math.Max(0, constraint.Width - _fixedWidth + gutter);
var itemWidth = availableWidth / ColLayout.ColMaxCellCount;

foreach (var child in cols)
{
var cellCount = child.GetLayoutCellCount(_layoutStatus);
var cellCount = child.GetLayoutCellCount(localLayoutStatus);
if (cellCount > 0 && !child.IsFixed)
{
totalCellCount += cellCount;
var availableWidth = Math.Max(0, cellCount * itemWidth - gutter);
var availableChildWidth = Math.Max(0, cellCount * itemWidth - gutter);

child.Measure(new Size(availableWidth, constraint.Height));
child.Measure(new Size(availableChildWidth, constraint.Height));
_maxChildDesiredHeight = Math.Max(_maxChildDesiredHeight, child.DesiredSize.Height);

if (totalCellCount > ColLayout.ColMaxCellCount)
Expand All @@ -72,31 +145,49 @@ protected override Size MeasureOverride(Size constraint)
}
}

return new Size(0, _maxChildDesiredHeight * totalRowCount);
// --- 返回安全尺寸 ---
var returnWidth = constraint.Width;
var returnHeight = _maxChildDesiredHeight * totalRowCount;

if (double.IsInfinity(returnWidth) || double.IsNaN(returnWidth) || returnWidth <= 0)
{
if (Parent is FrameworkElement p && p.ActualWidth > 0)
returnWidth = p.ActualWidth;
else
returnWidth = 100; // fallback
}

if (double.IsInfinity(returnHeight) || double.IsNaN(returnHeight) || returnHeight < 0)
returnHeight = 0;

return new Size(returnWidth, returnHeight);
}

protected override Size ArrangeOverride(Size finalSize)
{
var gutter = Gutter;
var totalCellCount = 0;
var cols = InternalChildren.OfType<Col>().ToList();
// 再次设置布局状态,供 Arrange 使用
_layoutStatus = ColLayout.GetLayoutStatus(finalSize.Width);

var itemWidth = (finalSize.Width - _fixedWidth + gutter) / ColLayout.ColMaxCellCount;
itemWidth = Math.Max(0, itemWidth);

var childBounds = new Rect(0, 0, 0, _maxChildDesiredHeight);
_layoutStatus = ColLayout.GetLayoutStatus(finalSize.Width);

foreach (var child in cols)
{
if (!child.IsVisible)
{
continue;
}

var cellCount = child.GetLayoutCellCount(_layoutStatus);
totalCellCount += cellCount;

var childWidth = (cellCount > 0 && !child.IsFixed) ? Math.Max(0, cellCount * itemWidth - gutter) : child.DesiredSize.Width;
var childWidth = (cellCount > 0 && !child.IsFixed)
? Math.Max(0, cellCount * itemWidth - gutter)
: child.DesiredSize.Width;

childBounds.Width = childWidth;
childBounds.X += child.Offset * itemWidth;

Expand Down