gpt4 book ai didi

c# - 填充可见空间时收缩项目控制项目

转载 作者:行者123 更新时间:2023-11-30 12:24:59 45 4
gpt4 key购买 nike

我想创建一个数据绑定(bind)的水平布局 ItemsControl,其中每个项目都有一个 Button。当我向集合中添加新项目时,ItemsControl 应该相对于它所在的 Window 增长,直到达到它的 MaxWidth 属性。然后所有的按钮都应该同样收缩以适应 MaxWidth。类似于 Chrome 浏览器的标签页。

带空格的制表符: enter image description here

没有空格的标签: enter image description here

到目前为止,我已经做到了这一点:

    <ItemsControl Name="ButtonsControl" MaxWidth="400">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type dataclasses:TextNote}">
<Button Content="{Binding Title}" MinWidth="80"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

添加项目时,StackPanelWindow 的扩展很好,但是当达到 MaxWidth 时,项目才开始消失。

最佳答案

我认为使用标准 WPF 控件的任意组合都不可能产生这种行为,但这个自定义 StackPanel 控件应该可以完成这项工作:

public class SqueezeStackPanel : Panel
{
private const double Tolerance = 0.001;

public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register
("Orientation", typeof (Orientation), typeof (SqueezeStackPanel),
new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure,
OnOrientationChanged));

private readonly Dictionary<UIElement, Size> _childToConstraint = new Dictionary<UIElement, Size>();
private bool _isMeasureDirty;
private bool _isHorizontal = true;
private List<UIElement> _orderedSequence;
private Child[] _children;

static SqueezeStackPanel()
{
DefaultStyleKeyProperty.OverrideMetadata
(typeof (SqueezeStackPanel),
new FrameworkPropertyMetadata(typeof (SqueezeStackPanel)));
}

protected override bool HasLogicalOrientation
{
get { return true; }
}

protected override Orientation LogicalOrientation
{
get { return Orientation; }
}

public Orientation Orientation
{
get { return (Orientation) GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}

protected override Size ArrangeOverride(Size finalSize)
{
var size = new Size(_isHorizontal ? 0 : finalSize.Width, !_isHorizontal ? 0 : finalSize.Height);

var childrenCount = Children.Count;

var rc = new Rect();
for (var index = 0; index < childrenCount; index++)
{
var child = _orderedSequence[index];

var childVal = _children[index].Val;
if (_isHorizontal)
{
rc.Width = double.IsInfinity(childVal) ? child.DesiredSize.Width : childVal;
rc.Height = Math.Max(finalSize.Height, child.DesiredSize.Height);
size.Width += rc.Width;
size.Height = Math.Max(size.Height, rc.Height);
child.Arrange(rc);
rc.X += rc.Width;
}
else
{
rc.Width = Math.Max(finalSize.Width, child.DesiredSize.Width);
rc.Height = double.IsInfinity(childVal) ? child.DesiredSize.Height : childVal;
size.Width = Math.Max(size.Width, rc.Width);
size.Height += rc.Height;
child.Arrange(rc);
rc.Y += rc.Height;
}
}

return new Size(Math.Max(finalSize.Width, size.Width), Math.Max(finalSize.Height, size.Height));
}

protected override Size MeasureOverride(Size availableSize)
{
for (var i = 0; i < 3; i++)
{
_isMeasureDirty = false;

var childrenDesiredSize = new Size();

var childrenCount = Children.Count;

if (childrenCount == 0)
return childrenDesiredSize;

var childConstraint = GetChildrenConstraint(availableSize);

_children = new Child[childrenCount];

_orderedSequence = Children.Cast<UIElement>().ToList();

for (var index = 0; index < childrenCount; index++)
{
if (_isMeasureDirty)
break;

var child = _orderedSequence[index];

const double minLength = 0.0;
const double maxLength = double.PositiveInfinity;

MeasureChild(child, childConstraint);

if (_isHorizontal)
{
childrenDesiredSize.Width += child.DesiredSize.Width;
_children[index] = new Child(minLength, maxLength, child.DesiredSize.Width);
childrenDesiredSize.Height = Math.Max(childrenDesiredSize.Height, child.DesiredSize.Height);
}
else
{
childrenDesiredSize.Height += child.DesiredSize.Height;
_children[index] = new Child(minLength, maxLength, child.DesiredSize.Height);
childrenDesiredSize.Width = Math.Max(childrenDesiredSize.Width, child.DesiredSize.Width);
}
}

if (_isMeasureDirty)
continue;

var current = _children.Sum(s => s.Val);
var target = GetSizePart(availableSize);

var finalSize = new Size
(Math.Min(availableSize.Width, _isHorizontal ? current : childrenDesiredSize.Width),
Math.Min(availableSize.Height, _isHorizontal ? childrenDesiredSize.Height : current));

if (double.IsInfinity(target))
return finalSize;

RecalcChilds(current, target);

current = 0.0;
for (var index = 0; index < childrenCount; index++)
{
var child = _children[index];

if (IsGreater(current + child.Val, target, Tolerance) &&
IsGreater(target, current, Tolerance))
{
var rest = IsGreater(target, current, Tolerance) ? target - current : 0.0;
if (IsGreater(rest, child.Min, Tolerance))
child.Val = rest;
}

current += child.Val;
}

RemeasureChildren(finalSize);

finalSize = new Size
(Math.Min(availableSize.Width, _isHorizontal ? target : childrenDesiredSize.Width),
Math.Min(availableSize.Height, _isHorizontal ? childrenDesiredSize.Height : target));

if (_isMeasureDirty)
continue;

return finalSize;
}

return new Size();
}

public static double GetHeight(Thickness thickness)
{
return thickness.Top + thickness.Bottom;
}

public static double GetWidth(Thickness thickness)
{
return thickness.Left + thickness.Right;
}

protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);

var removedUiElement = visualRemoved as UIElement;

if (removedUiElement != null)
_childToConstraint.Remove(removedUiElement);
}

private Size GetChildrenConstraint(Size availableSize)
{
return new Size
(_isHorizontal ? double.PositiveInfinity : availableSize.Width,
!_isHorizontal ? double.PositiveInfinity : availableSize.Height);
}

private double GetSizePart(Size size)
{
return _isHorizontal ? size.Width : size.Height;
}

private static bool IsGreater(double a, double b, double tolerance)
{
return a - b > tolerance;
}

private void MeasureChild(UIElement child, Size childConstraint)
{
Size lastConstraint;
if ((child.IsMeasureValid && _childToConstraint.TryGetValue(child, out lastConstraint) &&
lastConstraint.Equals(childConstraint))) return;

child.Measure(childConstraint);
_childToConstraint[child] = childConstraint;
}

private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var panel = (SqueezeStackPanel) d;
panel._isHorizontal = panel.Orientation == Orientation.Horizontal;
}

private void RecalcChilds(double current, double target)
{
var shouldShrink = IsGreater(current, target, Tolerance);

if (shouldShrink)
ShrinkChildren(_children, target);
}

private void RemeasureChildren(Size availableSize)
{
var childrenCount = Children.Count;
if (childrenCount == 0)
return;

var childConstraint = GetChildrenConstraint(availableSize);
for (var index = 0; index < childrenCount; index++)
{
var child = _orderedSequence[index];
if (Math.Abs(GetSizePart(child.DesiredSize) - _children[index].Val) > Tolerance)
MeasureChild(child, new Size(_isHorizontal ? _children[index].Val : childConstraint.Width,
!_isHorizontal ? _children[index].Val : childConstraint.Height));
}
}

private static void ShrinkChildren(IEnumerable<Child> children, double target)
{
var sortedChilds = children.OrderBy(v => v.Val).ToList();
var minValidTarget = sortedChilds.Sum(s => s.Min);
if (minValidTarget > target)
{
foreach (var child in sortedChilds)
child.Val = child.Min;
return;
}
do
{
var tmpTarget = target;
for (var iChild = 0; iChild < sortedChilds.Count; iChild++)
{
var child = sortedChilds[iChild];
if (child.Val*(sortedChilds.Count - iChild) >= tmpTarget)
{
var avg = tmpTarget/(sortedChilds.Count - iChild);
var success = true;
for (var jChild = iChild; jChild < sortedChilds.Count; jChild++)
{
var tChild = sortedChilds[jChild];
tChild.Val = Math.Max(tChild.Min, avg);

// Min constraint skip success expand on this iteration
if (Math.Abs(avg - tChild.Val) <= Tolerance) continue;

target -= tChild.Val;
success = false;
sortedChilds.RemoveAt(jChild);
jChild--;
}
if (success)
return;

break;
}
tmpTarget -= child.Val;
}
} while (sortedChilds.Count > 0);
}

private class Child
{
public readonly double Min;
public double Val;

public Child(double min, double max, double val)
{
Min = min;
Val = val;

Val = Math.Max(min, val);
Val = Math.Min(max, Val);
}
}
}

尝试将其用作您的 ItemsPanelTemplate:

<ItemsControl Name="ButtonsControl" MaxWidth="400">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:SqueezeStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type dataclasses:TextNote}">
<Button Content="{Binding Title}" MinWidth="80"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

根据您提供的代码,我无法确定,但我认为通过删除 ItemsControl 上的 MaxWidth,您将获得更好的布局结果。

关于c# - 填充可见空间时收缩项目控制项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32511626/

45 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com