有没有办法自动占据WrapPanel中的空白区域?

2023-11-26

WrapPanel 的子级按顺序填充,如所附屏幕截图所示。

因此,Panel根据每个孩子的身长,制作了较长的空白。

如何利用空白空间重新排列孩子们?

到目前为止,似乎只有很少的人使用 WrapPanel,并且没有足够的示例。

有一些自动的方法吗? 或者我只需要制定自己的算法?

WrapPanel 在显示事物方面起着非常重要的作用,但显示空间有限。

谢谢 !

enter image description here


这是一个 WrapPanel,可以选择使用重新排列元素FFDH算法,以及可选地拉伸它们以删除空白区域(example).

public class StretchyWrapPanel : Panel {
    public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register(nameof(ItemWidth), typeof(double),
            typeof(StretchyWrapPanel), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => {
                ((StretchyWrapPanel)o)._itemWidth = (double)e.NewValue;
            }));

    private double _itemWidth = double.NaN;

    [TypeConverter(typeof(LengthConverter))]
    public double ItemWidth {
        get => _itemWidth;
        set => SetValue(ItemWidthProperty, value);
    }

    public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register(nameof(ItemHeight), typeof(double),
            typeof(StretchyWrapPanel), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => {
                ((StretchyWrapPanel)o)._itemHeight = (double)e.NewValue;
            }));

    private double _itemHeight = double.NaN;

    [TypeConverter(typeof(LengthConverter))]
    public double ItemHeight {
        get => _itemHeight;
        set => SetValue(ItemHeightProperty, value);
    }

    public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(nameof(Orientation), typeof(Orientation),
            typeof(StretchyWrapPanel), new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsArrange, (o, e) => {
                ((StretchyWrapPanel)o)._orientation = (Orientation)e.NewValue;
            }));

    private Orientation _orientation = Orientation.Horizontal;

    public Orientation Orientation {
        get => _orientation;
        set => SetValue(OrientationProperty, value);
    }

    public static readonly DependencyProperty StretchToFillProperty = DependencyProperty.Register(nameof(StretchToFill), typeof(bool),
            typeof(StretchyWrapPanel), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsArrange, (o, e) => {
                ((StretchyWrapPanel)o)._stretchToFill = (bool)e.NewValue;
            }));

    private bool _stretchToFill = true;

    public bool StretchToFill {
        get => _stretchToFill;
        set => SetValue(StretchToFillProperty, value);
    }

    public static readonly DependencyProperty StretchProportionallyProperty = DependencyProperty.Register(nameof(StretchProportionally), typeof(bool),
            typeof(StretchyWrapPanel), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsArrange, (o, e) => {
                ((StretchyWrapPanel)o)._stretchProportionally = (bool)e.NewValue;
            }));

    private bool _stretchProportionally = true;

    public bool StretchProportionally {
        get => _stretchProportionally;
        set => SetValue(StretchProportionallyProperty, value);
    }

    public static readonly DependencyProperty RearrangeForBestFitProperty = DependencyProperty.Register(nameof(RearrangeForBestFit), typeof(bool),
            typeof(StretchyWrapPanel), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => {
                ((StretchyWrapPanel)o)._rearrangeForBestFit = (bool)e.NewValue;
            }));

    private bool _rearrangeForBestFit;

    public bool RearrangeForBestFit {
        get => _rearrangeForBestFit;
        set => SetValue(RearrangeForBestFitProperty, value);
    }

    private struct UVSize {
        internal UVSize(Orientation orientation, Size size) {
            U = V = 0d;
            _isHorizontal = orientation == Orientation.Horizontal;
            Width = size.Width;
            Height = size.Height;
        }

        internal UVSize(Orientation orientation, double width, double height) {
            U = V = 0d;
            _isHorizontal = orientation == Orientation.Horizontal;
            Width = width;
            Height = height;
        }

        internal UVSize(Orientation orientation) {
            U = V = 0d;
            _isHorizontal = orientation == Orientation.Horizontal;
        }

        internal double U;
        internal double V;
        private bool _isHorizontal;

        internal double Width {
            get => _isHorizontal ? U : V;
            set {
                if (_isHorizontal) {
                    U = value;
                } else {
                    V = value;
                }
            }
        }

        internal double Height {
            get => _isHorizontal ? V : U;
            set {
                if (_isHorizontal) {
                    V = value;
                } else {
                    U = value;
                }
            }
        }
    }

    protected override Size MeasureOverride(Size constraint) {
        return RearrangeForBestFit ? MeasureBestFit(constraint) : MeasureKeepInOrder(constraint);
    }

    private Size MeasureKeepInOrder(Size constraint) {
        var orientation = Orientation;
        var uLimit = new UVSize(orientation, constraint.Width, constraint.Height).U;
        var curLineSize = new UVSize(orientation);
        var panelSize = new UVSize(orientation);
        var itemWidth = ItemWidth;
        var itemHeight = ItemHeight;
        var itemWidthSet = !double.IsNaN(itemWidth);
        var itemHeightSet = !double.IsNaN(itemHeight);

        var childConstraint = new Size(
                itemWidthSet ? itemWidth : constraint.Width,
                itemHeightSet ? itemHeight : constraint.Height);

        var children = InternalChildren;

        for (int i = 0, count = children.Count; i < count; i++) {
            var child = children[i];
            if (child == null) continue;

            // Flow passes its own constrint to children
            child.Measure(childConstraint);

            // This is the size of the child in UV space
            var sz = new UVSize(orientation,
                    itemWidthSet ? itemWidth : child.DesiredSize.Width,
                    itemHeightSet ? itemHeight : child.DesiredSize.Height);

            if (curLineSize.U + sz.U > uLimit) {
                // Need to switch to another line
                panelSize.U = Math.Max(curLineSize.U, panelSize.U);
                panelSize.V += curLineSize.V;
                curLineSize = sz;

                if (sz.U > uLimit) {
                    // The element is wider then the constrint - give it a separate line
                    panelSize.U = Math.Max(sz.U, panelSize.U);
                    panelSize.V += sz.V;
                    curLineSize = new UVSize(orientation);
                }
            } else {
                // Continue to accumulate a line
                curLineSize.U += sz.U;
                curLineSize.V = Math.Max(sz.V, curLineSize.V);
            }
        }

        // The last line size, if any should be added
        panelSize.U = Math.Max(curLineSize.U, panelSize.U);
        panelSize.V += curLineSize.V;

        // Go from UV space to W/H space
        return new Size(panelSize.Width, panelSize.Height);
    }

    private Size MeasureBestFit(Size constraint) {
        var orientation = Orientation;
        var uLimit = new UVSize(orientation, constraint.Width, constraint.Height).U;
        var itemWidth = ItemWidth;
        var itemHeight = ItemHeight;
        var itemWidthSet = !double.IsNaN(itemWidth);
        var itemHeightSet = !double.IsNaN(itemHeight);

        var childConstraint = new Size(
                itemWidthSet ? itemWidth : constraint.Width,
                itemHeightSet ? itemHeight : constraint.Height);

        var children = InternalChildren;

        // First-Fit Decreasing Height (FFDH) algorithm
        var lines = new List<UVSize>();

        for (int i = 0, count = children.Count; i < count; i++) {
            var child = children[i];
            if (child == null) continue;

            // Flow passes its own constrint to children
            child.Measure(childConstraint);

            // This is the size of the child in UV space
            var childSize = new UVSize(orientation,
                    itemWidthSet ? itemWidth : child.DesiredSize.Width,
                    itemHeightSet ? itemHeight : child.DesiredSize.Height);

            for (var j = 0; j < lines.Count; j++) {
                var line = lines[j];
                if (line.U + childSize.U <= uLimit) {
                    lines[j] = new UVSize(orientation) { U = childSize.U, V = Math.Max(childSize.V, line.V) };
                    goto Next;
                }
            }

            lines.Add(childSize);

            Next:
            { }
        }

        var panelSize = new UVSize(orientation);
        for (var i = 0; i < lines.Count; i++) {
            var line = lines[i];
            panelSize.U = Math.Max(line.U, panelSize.U);
            panelSize.V += line.V;
        }

        // Go from UV space to W/H space
        return new Size(panelSize.Width, panelSize.Height);
    }

    protected override Size ArrangeOverride(Size finalSize) {
        return RearrangeForBestFit ? ArrangeBestFit(finalSize) : ArrangeKeepInOrder(finalSize);
    }

    private static UVSize GetChildSize(Orientation orientation, UIElement child, UVSize fixedChildSize) {
        var childSize = new UVSize(orientation, child.DesiredSize);
        if (!double.IsNaN(fixedChildSize.U)) childSize.U = fixedChildSize.U;
        if (!double.IsNaN(fixedChildSize.V)) childSize.V = fixedChildSize.V;
        return childSize;
    }

    private Size ArrangeKeepInOrder(Size finalSize) {
        var orientation = Orientation;
        var fixedChildSize = new UVSize(orientation, ItemWidth, ItemHeight);
        var children = InternalChildren;
        var firstInLine = 0;
        var uLimit = new UVSize(orientation, finalSize).U;
        var currentLineSize = new UVSize(orientation);
        var accumulatedV = 0d;

        for (int i = 0, count = children.Count; i < count; i++) {
            var child = children[i];
            if (child == null) continue;

            var childSize = GetChildSize(orientation, child, fixedChildSize);
            if (currentLineSize.U + childSize.U > uLimit) {
                // Need to switch to another line
                if (!double.IsNaN(fixedChildSize.U)) {
                    ArrangeLineFixedSize(orientation, children, accumulatedV, currentLineSize.V, firstInLine, i, fixedChildSize.U);
                } else if (!StretchToFill) {
                    ArrangeLineDefault(orientation, children, accumulatedV, currentLineSize.V, firstInLine, i);
                } else {
                    ArrangeLineStretch(orientation, children, accumulatedV, currentLineSize.V, firstInLine, i, uLimit, StretchProportionally);
                }

                accumulatedV += currentLineSize.V;
                currentLineSize = childSize;
                firstInLine = i;
            } else {
                // Continue to accumulate a line
                currentLineSize.U += childSize.U;
                currentLineSize.V = Math.Max(childSize.V, currentLineSize.V);
            }
        }

        // Arrange the last line, if any
        if (!double.IsNaN(fixedChildSize.U)) {
            ArrangeLineFixedSize(orientation, children, accumulatedV, currentLineSize.V, firstInLine, children.Count, fixedChildSize.U);
        } else if (!StretchToFill) {
            ArrangeLineDefault(orientation, children, accumulatedV, currentLineSize.V, firstInLine, children.Count);
        } else {
            ArrangeLineStretch(orientation, children, accumulatedV, currentLineSize.V, firstInLine, children.Count, uLimit, StretchProportionally);
        }

        return finalSize;
    }

    private static void ArrangeLineDefault(Orientation orientation, UIElementCollection children, double v, double lineV, int start, int end) {
        var position = new UVSize(orientation){ U = 0d, V = v };
        for (var i = start; i < end; i++) {
            var child = children[i];
            if (child != null) {
                var childSize = new UVSize(orientation, child.DesiredSize) { V = lineV };
                child.Arrange(new Rect(position.Width, position.Height, childSize.Width, childSize.Height));
                position.U += childSize.U;
            }
        }
    }

    private static void ArrangeLineStretch(Orientation orientation, UIElementCollection children, double v, double lineV, int start, int end,
            double limitU, bool stretchProportionally) {
        var totalU = 0d;
        for (var i = start; i < end; i++) {
            totalU += new UVSize(orientation, children[i].DesiredSize).U;
        }

        var position = new UVSize(orientation) { U = 0d, V = v };
        var uExtra = stretchProportionally ? limitU / totalU : (limitU - totalU) / (end - start);
        for (var i = start; i < end; i++) {
            var child = children[i];
            if (child != null) {
                var childSize = new UVSize(orientation, child.DesiredSize) { V = lineV };
                childSize.U = stretchProportionally ? childSize.U * uExtra : Math.Max(childSize.U + uExtra, 1d);
                child.Arrange(new Rect(position.Width, position.Height, childSize.Width, childSize.Height));
                position.U += childSize.U;
            }
        }
    }

    private static void ArrangeLineFixedSize(Orientation orientation, UIElementCollection children, double v, double lineV, int start, int end, double itemU) {
        var position = new UVSize(orientation) { U = 0d, V = v };
        var childSize = new UVSize(orientation){ U = itemU, V = lineV };
        for (var i = start; i < end; i++) {
            var child = children[i];
            if (child != null) {
                child.Arrange(new Rect(position.Width, position.Height, childSize.Width, childSize.Height));
                position.U += childSize.U;
            }
        }
    }

    private class ArrangeBestFitLine {
        public UVSize Size;
        public readonly List<int> ItemIndices = new List<int>();

        public void ArrangeDefault(Orientation orientation, UIElementCollection children, double v) {
            var position = new UVSize(orientation){ U = 0d, V = v };
            for (var i = 0; i < ItemIndices.Count; i++) {
                var child = children[ItemIndices[i]];
                if (child != null) {
                    var childSize = new UVSize(orientation, child.DesiredSize) { V = Size.V };
                    child.Arrange(new Rect(position.Width, position.Height, childSize.Width, childSize.Height));
                    position.U += childSize.U;
                }
            }
        }

        public void ArrangeStretch(Orientation orientation, UIElementCollection children, double v, double limitU, bool stretchProportionally) {
            var totalU = 0d;
            for (var i = 0; i < ItemIndices.Count; i++) {
                totalU += new UVSize(orientation, children[ItemIndices[i]].DesiredSize).U;
            }

            var position = new UVSize(orientation) { U = 0d, V = v };
            var uExtra = stretchProportionally ? limitU / totalU : (limitU - totalU) / ItemIndices.Count;
            for (var i = 0; i < ItemIndices.Count; i++) {
                var child = children[ItemIndices[i]];
                if (child != null) {
                    var childSize = new UVSize(orientation, child.DesiredSize) { V = Size.V };
                    childSize.U = stretchProportionally ? childSize.U * uExtra : Math.Max(childSize.U + uExtra, 1d);
                    child.Arrange(new Rect(position.Width, position.Height, childSize.Width, childSize.Height));
                    position.U += childSize.U;
                }
            }
        }

        public void ArrangeFixedSize(Orientation orientation, UIElementCollection children, double v, double itemU) {
            var position = new UVSize(orientation) { U = 0d, V = v };
            var childSize = new UVSize(orientation){ U = itemU, V = Size.V };
            for (var i = 0; i < ItemIndices.Count; i++) {
                var child = children[ItemIndices[i]];
                if (child != null) {
                    child.Arrange(new Rect(position.Width, position.Height, childSize.Width, childSize.Height));
                    position.U += childSize.U;
                }
            }
        }
    }

    private Size ArrangeBestFit(Size finalSize) {
        var orientation = Orientation;
        var fixedChildSize = new UVSize(orientation, ItemWidth, ItemHeight);
        var uLimit = new UVSize(orientation, finalSize).U;

        // First-Fit Decreasing Height (FFDH) algorithm
        var lines = new List<ArrangeBestFitLine>();
        var children = InternalChildren;
        for (int i = 0, count = children.Count; i < count; i++) {
            var child = children[i];
            if (child == null) continue;

            var childSize = GetChildSize(orientation, child, fixedChildSize);
            for (var j = 0; j < lines.Count; j++) {
                var line = lines[j];
                if (line.Size.U + childSize.U <= uLimit) {
                    line.Size.U += childSize.U;
                    line.Size.V = Math.Max(childSize.V, line.Size.V);
                    line.ItemIndices.Add(i);
                    goto Next;
                }
            }

            lines.Add(new ArrangeBestFitLine {
                Size = childSize,
                ItemIndices = { i }
            });

            Next:
            { }
        }

        var accumulatedV = 0d;
        for (var i = 0; i < lines.Count; i++) {
            var line = lines[i];

            if (!double.IsNaN(fixedChildSize.U)) {
                line.ArrangeFixedSize(orientation, children, accumulatedV, fixedChildSize.U);
            } else if (!StretchToFill) {
                line.ArrangeDefault(orientation, children, accumulatedV);
            } else {
                line.ArrangeStretch(orientation, children, accumulatedV, uLimit, StretchProportionally);
            }

            accumulatedV += line.Size.V;
        }

        return finalSize;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有没有办法自动占据WrapPanel中的空白区域? 的相关文章

随机推荐

  • Arrays.asList 的返回类型是什么?

    我读了这篇文章 java 中 Arrays asList array 与 new ArrayList Arrays asList ia 之间的区别 我对此有一个疑问 我看看这一行 List
  • 如何运行 Maven Surefire 而不将测试结果打印到命令行?

    我正在运行 Maven 3 1 0万火插件 already与 quiet选项 但是它仍然将单元测试的结果打印到命令行 即使它们全部通过 有没有办法让它只打印失败 如果一切正常 我想抑制的输出如下所示 T E S T S Running ne
  • R:如何按列删除重复行[重复]

    这个问题在这里已经有答案了 df lt data frame id c 1 1 1 2 2 gender c Female Female Male Female Male variant c a b c d e gt df id gende
  • 即使应用程序未打开,后台进程也会定期扫描用户的位置并更新本地数据库

    我正在创建一个应用程序 该应用程序每半小时检查一次用户位置并更新本地数据库中的用户位置 然后基于用户的位置运行 CRUD 查询 即使该应用程序未运行也是如此 我该怎么做 我已经提到过这个http techtej blogspot com e
  • FastMM:分配的总内存

    我怎样才能获得FastMM分配的内存总量 我已经尝试过 function GetTotalAllocatedMemory Cardinal var MMState TMemoryManagerState begin GetMemoryMan
  • 内联 JavaScript 正则表达式更快吗?

    使用 RegExp 对象还是内联样式更好 为什么 根据 ES3 规范 它们略有不同 因为文字语法 regex 将创建一个RegExp初始扫描时的对象 正则表达式文字是 输入元素被转换为 RegExp 对象 第 15 10 节 当它 被扫描
  • 循环遍历 DataGridView 单元格

    我正在创建一个生成条形码然后打印运输标签的程序 我有一个功能 允许用户将电子表格上传到数据网格视图中 列名称之一是 跟踪号码 我希望能够循环遍历具有跟踪号码的每个单元格 然后在名为 条形码 的列中的新单元格中生成条形码 我知道有一个循环函数
  • 如何从 Capybara 和 ChromeDriver 中拖动 jQuery 滑块手柄?

    我可以执行以下代码来移动滑块手柄 但浏览器中触发的事件没有发生 page execute script Q slider handicap slider values 1 30 这正确地将右侧手柄设置为 30 但我需要它的行为就像我实际上拿
  • JQuery Mobile textarea:如何使用“rows”属性?

    我想使用 JQuery Mobile 动态生成具有不同行数的文本区域 我打算为此使用淘汰赛 将数据绑定到rows属性 例如 这里 http jsfiddle net j7b9A 2
  • 删除 .NET RichTextBox 中的特定行

    如何删除 RichTextBox 中的特定文本行 另一个解决方案 private void DeleteLine int a line int start index richTextBox GetFirstCharIndexFromLin
  • 如何使用 RLMArray 保存数组

    注意 我对 Realm 和 Swift 相当陌生 所以请原谅我不理解的任何明显的事情 我有一个正在工作的 UITableView 我计划用任务填充它 我希望用户能够根据需要添加和删除任务 因此我无法对任务进行硬编码 并且我希望在应用程序启动
  • 返回 JSONP 的 Restful api 的节点 HTTP 请求

    我正在尝试使用node js 对restful API 进行服务器端调用 JSONP JS 函数内的 JSON 容器 的返回返回错误 这似乎是节点 http get options callback API 的核心 节点或任何模块可以从 J
  • Android 音频编程噩梦 - soundpool、audiotrack arrghh?

    我构建了一个简单的音乐音序器 Android 应用程序 可以播放多个音频文件 最初我使用 SoundPool 来播放 mp3 文件 它在 2 3 4 上与旧的 HTC Droid Incredible 完美配合 然后我在运行 4 3 的 G
  • JPA多对多关系导致无限递归和堆栈溢出错误

    我正在开发一个 EclipseLink 项目 其中一个用户可以 关注 另一个用户 就像在社交媒体网站上一样 我有这个设置User实体 引用一个名为users 其中有一个 关注者 列表 关注该用户的用户 和另一个 关注 列表 该用户正在关注的
  • ASP.NET WSAT(网站管理工具)和自定义会员资格提供程序

    我正在构建一个 ASP NET MVC 应用程序 它将具有自定义角色和成员资格提供程序 我一直在研究管理工具来节省我们一些时间 WSAT已经横穿了我的道路 一眼看上去不错 都是开源的 而且很简单 如果不行我可以自己修复 第一个问题是 你们中
  • 为什么 MobX v6.x 在 React with Typescript 中不能按预期工作?

    我目前正在编写一个 React 应用程序 它应该能够在任何可观察到的值发生变化时重新渲染组件 问题是我无法得到email如果发生变化则重新渲染 store ts export class ExampleStore observable em
  • RewriteRule - 两个参数,但最后一个参数是可选的

    我无法重写以包含两个参数 但最后一个参数是可选的 例如 http www mywebsite com friends jamie 正斜杠也应该是可选的 这应该与此相同 http www mywebsite com friends php n
  • 隐藏奇怪的不需要的 Xcode 日志

    使用 Xcode 8 并创建新的空白项目时 运行应用程序时会出现以下日志 2016 06 13 16 33 34 406093 TestiOS10 8209 100611 bundleid com appc TestiOS10 enable
  • 在数据库中的任意位置查找值

    给定一个数字 我如何发现它可以在哪个表和列中找到 我不在乎它是否快 它只需要工作 这可能对你有帮助 来自纳拉亚纳 维亚斯 它搜索给定数据库中所有表的所有列 我以前用过它并且有效 这是上面链接中的存储过程 我所做的唯一更改是用临时表替换表变量
  • 有没有办法自动占据WrapPanel中的空白区域?

    WrapPanel 的子级按顺序填充 如所附屏幕截图所示 因此 Panel根据每个孩子的身长 制作了较长的空白 如何利用空白空间重新排列孩子们 到目前为止 似乎只有很少的人使用 WrapPanel 并且没有足够的示例 有一些自动的方法吗 或