
UI组件桌面应用【免费下载链接】HandyControlContains some simple and commonly used WPF controls项目地址https://gitcode.com/gh_mirrors/ha/HandyControl点击查看免费下载导读WaterfallPanel 是 HandyControl 中一个轻量级的瀑布流布局面板它使子元素能够按照等宽不等高或等高不等宽两种模式自动排列适合照片墙、商品卡片流、标签云等场景。本文将以 WaterfallPanel 官方文档 为主线结合 WPF 与 Avalonia 双平台的 核心源码 与 演示用例带你掌握全部属性含义、瀑布流最短列优先布局算法以及一套可直接复制运行的完整 XAML 案例。WaterfallPanel 是什么WaterfallPanel 继承自 WPF 的Panel基类是一个纯粹的布局容器不携带任何视觉外观public class WaterfallPanel : Panel它的职责只有一个让元素按照等宽不等高或者等高不等宽进行布局。等宽不等高所有列宽度相同每个元素保持自身高度新的元素总是放入当前高度最矮的那一列形成典型的瀑布流错落效果等高不等宽布局方向旋转 90°所有行高度相同元素保持自身宽度依次填入当前宽度最短的那一行。这种布局非常适合元素尺寸参差不齐、又希望空间利用率最大化的场景。核心属性与默认值文档给出的两个核心属性如下| 属性 | 描述 | 默认值 | 备注 | | - | - | - | - | |Groups| 组数 |2| 等宽不等高时即为列数等高不等宽即为行数 | |Orientation| 排列方向 |Orientation.Horizontal|Horizontal为等宽不等高Vertical为等高不等宽 |Groups组数即列数 / 行数Groups决定将可用空间划分为多少个分组。以默认的Horizontal方向为例Groups3就表示把面板宽度平均分成 3 列每个子元素占据其中一列列宽恒为面板宽度 / 组数因此所有元素等宽元素的高度则保持自身的DesiredSize因此整体呈现不等高。切换为Vertical后Groups则对应行数。从源码看该属性注册为依赖属性WPF 版位于 WaterfallPanel.cspublic static readonly DependencyProperty GroupsProperty DependencyProperty.Register( nameof(Groups), typeof(int), typeof(WaterfallPanel), new FrameworkPropertyMetadata( ValueBoxes.Int2Box, FrameworkPropertyMetadataOptions.AffectsMeasure), IsGroupsValid); public int Groups { get (int) GetValue(GroupsProperty); set SetValue(GroupsProperty, value); } private static bool IsGroupsValid(object value) (int) value 1;值得注意的两个细节元数据带有AffectsMeasure修改Groups会触发面板重新测量与排列这正是演示中通过NumericUpDown实时改列数能立刻生效的原因校验规则IsGroupsValidGroups必须 1传入 0 或负数会抛出异常设置组数时需注意下界。Orientation决定排列方向Orientation不是另起炉灶的新属性而是复用StackPanel.OrientationProperty.AddOwner(...)注册的见 WaterfallPanel.cs默认值Horizontal。AddOwner意味着它和StackPanel的Orientation共享同一依赖属性标识符在样式、绑定与动画层面行为一致这是 HandyControl 面板实现中常见且优雅的做法。从源码理解瀑布流布局算法WaterfallPanel 的布局核心是每次把元素放到当前累计长度最短的组。它借助内部结构PanelUvSize定义于 PanelUvSize.cs把二维尺寸抽象成 UV 坐标U 轴沿布局方向Horizontal时为宽度Vertical时为高度即元素被均分到各组的方向V 轴垂直布局方向Horizontal时为高度Vertical时为宽度即组内元素累积的长度。MeasureOverride计算面板总高度protected override Size MeasureOverride(Size constraint) { var orientation Orientation; var uvConstraint new PanelUvSize(orientation, constraint); var groups CalculateGroupCount(orientation, uvConstraint); ... var vArr new double[groups].ToList(); var itemU uvConstraint.U / groups; ... for (int i 0, count children.Count; i count; i) { ... child.Measure(constraint); var sz new PanelUvSize(orientation, child.DesiredSize); var minIndex vArr.IndexOf(vArr.Min()); var minV vArr[minIndex]; vArr[minIndex] minV sz.V; } uvConstraint new PanelUvSize(orientation, new Size(uvConstraint.ScreenSize.Width, vArr.Max())); return uvConstraint.ScreenSize; }测量阶段维护一个长度为Groups的数组vArr对每个子元素执行child.Measure(constraint)拿到其理想尺寸然后找到vArr.Min()所在组当前最短的列/行把该元素的 V 轴长度累加进去。遍历完成后面板的 V 轴尺寸取vArr.Max()——也就是最高的那一列或最宽的那一行保证所有元素都能被容纳。ArrangeOverride把元素摆到最短组var minIndex vArr.IndexOf(vArr.Min()); var minV vArr[minIndex]; var childUvSize new PanelUvSize(orientation, child.DesiredSize); var childSize new PanelUvSize(orientation, itemU, childUvSize.V); var childRectSize new PanelUvSize(orientation, minIndex * itemU, minV); child.Arrange(new Rect(new Point(childRectSize.U, childRectSize.V), childSize.ScreenSize)); vArr[minIndex] minV childUvSize.V;排列阶段使用同样的最短组优先策略每个子元素被放置在minIndex * itemU的 U 轴起点即所在列/行的起始位置V 轴起点是当前组已累积的长度元素的尺寸固定为itemU × 自身V轴长度。因此元素在组内是等宽或等高且顶部对齐、依次下排的这正是瀑布流的经典形态。整体算法是典型的贪心装箱思路复杂度 O(n·Groups)无需对子元素排序天然支持运行时增删子元素也支持通过Groups改变列数后平滑重排。完整案例一份可直接运行的 XAML文档给出了完整的演示页面完整源文件见 WaterfallPanelDemo.xamlWPF 演示中通过hc:TransitioningContentControl承载Avalonia 版见 WaterfallPanelDemo.axaml核心结构如下Grid Margin32 Grid.RowDefinitions RowDefinition HeightAuto/ RowDefinition/ /Grid.RowDefinitions hc:NumericUpDown x:NameUpDownGroups HorizontalAlignmentCenter Value2 Minimum1 Maximum3 Width200 Style{StaticResource NumericUpDownExtend} hc:TitleElement.Title{ex:Lang Key{x:Static langs:LangKeys.Groups}} hc:TitleElement.TitleWidth50 hc:TitleElement.TitlePlacementLeft/ StackPanel Grid.Row1 OrientationHorizontal Margin0,16,0,0 hc:ScrollViewer MaxHeight300 hc:WaterfallPanel VerticalAlignmentCenter Width300 Groups{Binding Value,ElementNameUpDownGroups} hc:PanelElement.FluidMoveBehavior{StaticResource BehaviorXY200} Border Height100 Background{DynamicResource PrimaryBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border Border Height140 Background{DynamicResource DangerBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border Border Height100 Background{DynamicResource SuccessBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border Border Height170 Background{DynamicResource InfoBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border Border Height100 Background{DynamicResource WarningBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border /hc:WaterfallPanel /hc:ScrollViewer hc:ScrollViewer Margin32,0,0,0 HorizontalScrollBarVisibilityAuto VerticalScrollBarVisibilityHidden OrientationHorizontal MaxWidth300 hc:WaterfallPanel VerticalAlignmentCenter Height300 OrientationVertical Groups{Binding Value,ElementNameUpDownGroups} hc:PanelElement.FluidMoveBehavior{StaticResource BehaviorXY200} Border Width100 Background{DynamicResource PrimaryBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border Border Width140 Background{DynamicResource DangerBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border Border Width100 Background{DynamicResource SuccessBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border Border Width170 Background{DynamicResource InfoBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border Border Width100 Background{DynamicResource WarningBrush} Effect{StaticResource EffectShadow1} Margin5 TextBlock Text{ex:Lang Key{x:Static langs:LangKeys.ContentDemoStr}} VerticalAlignmentCenter HorizontalAlignmentCenter ForegroundWhite/ /Border /hc:WaterfallPanel /hc:ScrollViewer /StackPanel /Grid这个案例同时演示了两种形态逐段拆解如下1. 等宽不等高Horizontal 瀑布流左侧面板设置了Width300、Groups绑定到UpDownGroups.Value内部放置了高度分别为 100 / 140 / 100 / 170 / 100 的五块Border。由于未显式设置Orientation默认Horizontal这五个高度不一的块会被均匀分到 2 列中且始终填到当前最矮的一列形成错落瀑布效果。面板外层套hc:ScrollViewer MaxHeight300当内容总高度超出 300 时出现垂直滚动条。2. 等高不等宽Vertical 瀑布流右侧面板设置了Height300与OrientationVertical子元素改为指定Width100 / 140 / 100 / 170 / 100。此时Groups表示行数面板把高度均分为行元素保持自身宽度、依次填入当前最短的行整体效果是垂直方向等分、水平方向参差。外层hc:ScrollViewer配置了HorizontalScrollBarVisibilityAuto与VerticalScrollBarVisibilityHidden配合OrientationHorizontal实现横向滚动查看。3. 用 NumericUpDown 实时调整组数页面顶部用hc:NumericUpDownValue2、Minimum1、Maximum3通过{Binding Value, ElementNameUpDownGroups}同时驱动左右两个面板的Groups。由于Groups注册了AffectsMeasure改变组数会触发重新测量与排列你可以直观看到瀑布流从 2 列变为 3 列时元素的重新分布。4. 结合 FluidMoveBehavior 实现平滑重排动画两个WaterfallPanel都附加了hc:PanelElement.FluidMoveBehavior{StaticResource BehaviorXY200}。FluidMoveBehavior是 HandyControl 基于Microsoft.Expression.Interactions提供的流体移动行为相关交互层源码见 FluidMoveBehavior.cs它会在面板布局变化子元素位置改变时为元素的位置变化补上过渡动画时长约 200ms。于是当组数改变、元素在列之间跳转时画面不会生硬跳动而是平滑滑动——这是让瀑布流交互高级感的关键一笔。案例中的资源约定案例中用到了多组 HandyControl 主题资源理解它们有助于直接复用| 资源 | 用途 | | - | - | |{DynamicResource PrimaryBrush}| 主题主色画刷 | |{DynamicResource DangerBrush}| 危险/警示色画刷 | |{DynamicResource SuccessBrush}| 成功色画刷 | |{DynamicResource InfoBrush}| 信息色画刷 | |{DynamicResource WarningBrush}| 警告色画刷 | |{StaticResource EffectShadow1}| 阴影特效让卡片更有层次感 | |{StaticResource BehaviorXY200}| 200ms 的 XY 方向流体移动行为 | |{StaticResource NumericUpDownExtend}| NumericUpDown 扩展样式 |Border上统一设置Margin5在瀑布流块之间留出 5 像素间隙每个卡片内的TextBlock使用{ex:Lang Key...}从多语言资源中取文案演示项目通过 Lang.resx 等资源文件实现国际化实际开发中可直接替换为Textxxx。在 Avalonia 分支中该演示WaterfallPanelDemo.axaml结构完全一致仅资源引用语法略有差异如Theme{StaticResource NumericUpDownExtend}、绑定写作{Binding #UpDownGroups.Value}并且官方已将该控件收录进 Avalonia 版核心库见 Avalonia 版 WaterfallPanel.cs。进阶AutoGroup 与 DesiredLength 自动分组除文档表格列出的两个属性外从 WPF 源码 可以看到 WaterfallPanel 还提供了两个用于自动分组的属性适合组数需要随容器尺寸自适应变化的场景| 属性 | 类型 | 默认值 | 说明 | | - | - | - | - | |AutoGroup|bool|false| 是否根据可用尺寸自动计算组数 | |DesiredLength|double|0| 每组期望的 U 轴长度即每列期望宽度 / 每行期望高度 |结合CalculateGroupCount的逻辑理解自动分组规则private int CalculateGroupCount(Orientation orientation, PanelUvSize size) { if (!AutoGroup) { return Groups; } var itemLength DesiredLength; if (MathHelper.IsVerySmall(itemLength)) { return Groups; } return (int) (size.U / itemLength); }当AutoGroupFalse时一律使用手动设置的Groups当AutoGroupTrue且DesiredLength为 0默认值被MathHelper.IsVerySmall判定为极小值时同样回退到手动Groups只有AutoGroupTrue且DesiredLength大于 0 时组数才按可用U轴长度 / DesiredLength向下取整自动计算例如容器宽 900、DesiredLength300时自动得到 3 列。这个组合非常适合需要响应窗口大小变化的自适应瀑布流如不同分辨率下自动增减列数。Avalonia 版通过CoerceLength将负值钳制为 0行为保持一致见 Avalonia 版 WaterfallPanel.cs。使用建议与注意事项务必给面板设置确定的宽/高或外部约束ArrangeOverride中itemU uvConstraint.U / groups若所在方向尺寸为NaN或InfinityMeasureOverride会直接返回原约束源码中有double.IsNaN(itemU) || double.IsInfinity(itemU)的防御判断。演示中水平形态固定Width300、垂直形态固定Height300正是为了给面板提供明确的 U 轴长度。容器尺寸变化时组内位置会重新计算因为瀑布流是全局最短组优先在ScrollViewer等可变尺寸容器中元素位置可能随滚动或尺寸变化而调整配合FluidMoveBehavior可让这种调整平滑过渡。Groups必须不小于 1注册元数据中的IsGroupsValid校验会拦截非法值绑定时若数据源可能越界建议像演示一样用NumericUpDown的Minimum1从输入侧兜底。子元素的高度或宽度决定视觉落差瀑布流观感取决于子元素 V 轴尺寸的差异度示例中刻意混用 100/140/170 等不同高度就是为了突出错落效果。该控件位于扩展控件目录从演示工程的 DemoInfo.json 可以看到它被归类为Layout布局类控件与 UniformSpacingPanel 等面板类控件同属布局体系。小结WaterfallPanel 通过等宽不等高 / 等高不等宽两种模式 最短组优先的贪心布局算法为 HandyControl 提供了一套轻量、无依赖的瀑布流布局方案。掌握Groups、Orientation两个核心属性理解AutoGroupDesiredLength的自动分组能力再配合FluidMoveBehavior的位置过渡动画即可在照片墙、卡片流、标签云等界面中快速落地一套动态、平滑、可交互的瀑布流布局。赞分享UI组件桌面应用【免费下载链接】HandyControlContains some simple and commonly used WPF controls项目地址https://gitcode.com/gh_mirrors/ha/HandyControl点击查看免费下载相关推荐HandyControl CirclePanel 圆形布局面板完全指南属性、排列算法与实战案例HandyControl CirclePanel 圆形布局面板完全指南属性、排列算法与实战案例 圆形布局CirclePanel是 HandyControlUI组件桌面应用HandyControl的布局与面板控件HandyControl的布局与面板控件 HandyControl 提供了多种强大的布局控件如 FlexPanel、RelativePanel、HoneycoUI组件桌面应用Quasar Flexbox 布局模式实战断行与瀑布流布局完全指南Quasar Flexbox 布局模式实战断行与瀑布流布局完全指南 本文基于 Quasar Framework 官方文档的 Flexbox Patterns前端UI组件跨平台上一篇Humanizer.IDateOnlyToOrdinalWordConverter 接口深度解析DateOnly 本地化序数日期转换下一篇OpCore-Simplify15分钟完成专业级黑苹果配置的智能工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考