之前看了很多的WPF瀑布流布局,无奈自己太笨,多次实践都失败了,最后再cnblog中看到一篇关于WinPhone中的瀑布流布局的文章(http://www.cnblogs.com/Smallcode/archive/2012/10/19/2730810.html),受到启发,放到自己的项目中,却还是发现不少问题,没办法,实在是再找不到了,只能硬着头皮试试改了,没想到还成功了。
如题,这个修复了一些BUG,包括最大化,最小化,及快速拖动窗口时,布局界面不能及时刷新。
先看一下效果图:
自定义瀑布流控件:
// ***********************************************************************
// Assembly : ISmart
// Author : Snail
// Created : 08-13-2014
//
// Last Modified By : Snail
// Last Modified On : 08-18-2014
// ***********************************************************************
//
// Copyright (c) . All rights reserved.
//
// 自定义瀑布流布局面板,在所有子项宽度都统一的情况的下,实现瀑布流布局
// ***********************************************************************
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace ISmart.CustomControl
{
///
/// 自定义瀑布流布局面板
///
public class CustomPanel : Panel
{
#region 构造函数
///
/// Initializes a new instance of the class.
///
public CustomPanel()
{
//根据列数,实例化用来存放每列高度的数组
ColumnHeight = new double[ColumnCount];
}
#endregion 构造函数
///
/// 每列的高度
///
public static double[] ColumnHeight;
///
/// 列数
///
/// The column count.
public int ColumnCount
{
get { return (int)this.GetValue(ColumnCountProperty); }
set { this.SetValue(ColumnCountProperty, value); }
}
///
/// The column count property
///
public static readonly DependencyProperty ColumnCountProperty = DependencyProperty.Register("ColumnCount", typeof(int), typeof(CustomPanel), new PropertyMetadata(PropertyChanged));
///
/// Properties the changed.
///
/// The sender.
/// The instance containing the event data.
public static void PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ColumnHeight = new double[(int)e.NewValue];
if (sender == null || e.NewValue == e.OldValue)
return;
sender.SetValue(ColumnCountProperty, e.NewValue);
}
///
/// 当在派生类中重写时,请测量子元素在布局中所需的大小,然后确定 派生类的大小。
/// 更新当前元素与其子元素的布局,以下处理都属于 测量 处理,并非实际布局
///
/// 此元素可以赋给子元素的可用大小。可以指定无穷大值,这表示元素的大小将调整为内容的可用大小。
/// 此元素在布局过程中所需的大小,这是由此元素根据对其子元素大小的计算而确定的。
protected override Size MeasureOverride(Size availableSize)
{
Console.WriteLine(ColumnCount);
//清空所有列的高度
for (int i = 0; i < ColumnHeight.Count(); i++)
{
ColumnHeight[i] = 0;
}
//计算行数
int indexY = this.Children.Count / ColumnCount;
//计算行数
if (this.Children.Count % ColumnCount > 0) indexY++;
//输出
//Console.WriteLine("总个数:" + Children.Count);
//Console.WriteLine("行数:"+indexY);
//第几行
int flagY = 0;
//声明一个尺寸,用来存放测量后面板的尺寸
Size resultSize = new Size(0, 0);
#region 测量值
//循环所有行
for (int i = 0; i < indexY; i++)//行
{
//计算面板要呈现的宽度
resultSize.Width = Children[i].DesiredSize.Width * ColumnCount;
//处理最后一行
if (i == indexY - 1)
{
//剩余内容项个数
int residual = Children.Count - i * ColumnCount;
//如果集合总数小于列数,那么剩余内容项就是集合总数
if (Children.Count <= ColumnCount)
{
residual = Children.Count;
}
//循环剩余元素,设置元素呈现大小,计算当前列需要的高度
for (int h = 0; h < residual; h++)
{
//更新当前循环元素的布局
Children[ColumnCount * flagY + h].Measure(availableSize);
//累加每一列元素的高度
ColumnHeight[h] += Children[ColumnCount * flagY + h].DesiredSize.Height;
//Console.WriteLine(string.Format("测量高度{1}:{0}", Children[ColumnCount * flagY + h].DesiredSize.Height, ColumnCount * flagY + h));
}
}
else
{
for (int y = 0; y < ColumnCount; y++)
{
Children[ColumnCount * flagY + y].Measure(availableSize);
ColumnHeight[y] += Children[ColumnCount * flagY + y].DesiredSize.Height;
//Console.WriteLine(string.Format("测量高度{1}:{0}", Children[ColumnCount * flagY + y].DesiredSize.Height, ColumnCount * flagY + y));
}
flagY++;
}
}
#endregion 测量值
//面板的高度等于所有列中最高的值
resultSize.Height = ColumnHeight.Max();
Console.WriteLine(resultSize.Height);
//设置面板呈现的高度
//如果父元素给子元素提供的是一个无穷的宽,则使用计算的宽度,否则使用父元素的宽
resultSize.Width =
double.IsPositiveInfinity(availableSize.Width) ?
resultSize.Width : availableSize.Width;
//设置面板呈现的高度
//如果父元素给子元素提供的是一个无穷的高,则使用计算的宽度,否则使用父元素的高
resultSize.Height =
double.IsPositiveInfinity(availableSize.Height) ?
resultSize.Height : availableSize.Height;
//输出
//Console.WriteLine(string.Format("Width:{0},Height:{1}", resultSize.Width, resultSize.Height));
//返回测量尺寸
return resultSize;
}
///
/// 在派生类中重写时,请为 派生类定位子元素并确定大小。
/// 更新当前元素与其子元素的布局,以下处理都属于 实际 处理,元素布局都将基于此
///
/// 父级中此元素应用来排列自身及其子元素的最终区域。
/// 所用的实际大小。
protected override Size ArrangeOverride(Size finalSize)
{
//清空所有列的高度
for (int i = 0; i < ColumnHeight.Count(); i++)
{
ColumnHeight[i] = 0;
}
//计算行数
int indexY = this.Children.Count / ColumnCount;
if (this.Children.Count % ColumnCount > 0) indexY++;
//当前行
int flagY = 0;
//当前行高
double flagX = 0;
#region 实际值
//循环所有行
for (int i = 0; i < indexY; i++)
{
//元素最终的宽度
finalSize.Width = Children[i].DesiredSize.Width * ColumnCount;
//处理最后一行
if (i == indexY - 1)
{
//列宽
flagX = 0;
//剩余项个数
int residual = Children.Count - i * ColumnCount;
if (Children.Count <= ColumnCount)
{
residual = Children.Count;
}
for (int h = 0; h < residual; h++)
{
//Console.WriteLine(string.Format("实际坐标{4}:{0},{1},{2},{3}", flagX, ColumnHeight[h], Children[ColumnCount * i + h].DesiredSize.Width, Children[ColumnCount * i + h].DesiredSize.Height, ColumnCount * flagY + h));
Children[ColumnCount * i + h].Arrange(new Rect(new Point(flagX, ColumnHeight[h]), Children[ColumnCount * i + h].DesiredSize));
ColumnHeight[h] += Children[ColumnCount * i + h].DesiredSize.Height;
flagX += Children[ColumnCount * i + h].DesiredSize.Width;
}
}
else
{
flagX = 0;
for (int y = 0; y < ColumnCount; y++)
{
//Console.WriteLine(string.Format("实际坐标{4}:{0},{1},{2},{3}", flagX, i * ColumnHeight[y], Children[ColumnCount * i + y].DesiredSize.Width, Children[ColumnCount * i + y].DesiredSize.Height, ColumnCount * flagY + y));
Children[ColumnCount * flagY + y].Arrange(new Rect(new Point(flagX, ColumnHeight[y]), Children[ColumnCount * i + y].DesiredSize));
ColumnHeight[y] += Children[ColumnCount * flagY + y].DesiredSize.Height;
flagX += Children[ColumnCount * flagY + y].DesiredSize.Width;
}
flagY++;
}
}
//finalSize.Height = ColumnHeight.Max();
#endregion 测量值
return finalSize;
}
}
}
XAML代码:
后台代码:
//窗体尺寸改变时,改变瀑布流容器的列
///
/// Windows the size changed.
///
/// The sender.
/// The instance containing the event data.
public void WindowSizeChanged(object sender, System.Windows.RoutedEventArgs e)
{
if (LstSearch.Items.Count <= 0)
{
return;
}
Window currentWindow = Window.GetWindow(this);
var width = currentWindow.ActualWidth;
int columnNum = (int)width / 240;
//只在列数改变的情况下赋值,有效减少CustomPanel重绘次数
if (columnNum != _columnCount)
{
_columnCount = columnNum;
_imgsPanel.ColumnCount = columnNum;
//手动触发CustomPanel的Measure方法
//让窗体在最大化,最小化的情况下重绘
_imgsPanel.Measure(_imgsPanel.DesiredSize);
}
}