WPF中的瀑布流布局(TilePanel)控件
最近在用wpf做一个metro风格的程序,需要用到win8风格的布局容器,只能自己写一个了。效果如下

用法 : <local:TilePanel
TileMargin="1"
Orientation="Horizontal"
TileCount="4" >
//todo 放置内容
//local:TilePanel.WidthPix="1" 控制宽度倍率
//local:TilePanel.HeightPix="2"控制高度倍率
</local:TilePanel>
下面附上源码。
/// <summary>
/// TilePanel
/// 瀑布流布局
/// </summary>
public class TilePanel : Panel
{
#region 枚举 private enum OccupyType
{
NONE,
WIDTHHEIGHT,
OVERFLOW
} #endregion #region 属性 /// <summary>
/// 容器内元素的高度
/// </summary>
public int TileHeight
{
get { return (int)GetValue(TileHeightProperty); }
set { SetValue(TileHeightProperty, value); }
}
/// <summary>
/// 容器内元素的高度
/// </summary>
public static readonly DependencyProperty TileHeightProperty =
DependencyProperty.Register("TileHeight", typeof(int), typeof(TilePanel), new FrameworkPropertyMetadata(100, FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 容器内元素的宽度
/// </summary>
public int TileWidth
{
get { return (int)GetValue(TileWidthProperty); }
set { SetValue(TileWidthProperty, value); }
}
/// <summary>
/// 容器内元素的宽度
/// </summary>
public static readonly DependencyProperty TileWidthProperty =
DependencyProperty.Register("TileWidth", typeof(int), typeof(TilePanel), new FrameworkPropertyMetadata(100, FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static int GetWidthPix(DependencyObject obj)
{
return (int)obj.GetValue(WidthPixProperty);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <param name="value"></param>
public static void SetWidthPix(DependencyObject obj, int value)
{
if (value > 0)
{
obj.SetValue(WidthPixProperty, value);
}
}
/// <summary>
/// 元素的宽度比例,相对于TileWidth
/// </summary>
public static readonly DependencyProperty WidthPixProperty =
DependencyProperty.RegisterAttached("WidthPix", typeof(int), typeof(TilePanel), new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsParentMeasure));
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static int GetHeightPix(DependencyObject obj)
{
return (int)obj.GetValue(HeightPixProperty);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <param name="value"></param>
public static void SetHeightPix(DependencyObject obj, int value)
{
if (value > 0)
{
obj.SetValue(HeightPixProperty, value);
}
}
/// <summary>
/// 元素的高度比例,相对于TileHeight
/// </summary>
public static readonly DependencyProperty HeightPixProperty =
DependencyProperty.RegisterAttached("HeightPix", typeof(int), typeof(TilePanel), new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsParentMeasure));
/// <summary>
/// 排列方向
/// </summary>
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
/// <summary>
/// 排列方向
/// </summary>
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(TilePanel), new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 格子数量
/// </summary>
public int TileCount
{
get { return (int)GetValue(TileCountProperty); }
set { SetValue(TileCountProperty, value); }
}
/// <summary>
/// 格子数量
/// </summary>
public static readonly DependencyProperty TileCountProperty =
DependencyProperty.Register("TileCount", typeof(int), typeof(TilePanel), new PropertyMetadata(4));
/// <summary>
/// Tile之间的间距
/// </summary>
public Thickness TileMargin
{
get { return (Thickness)GetValue(TileMarginProperty); }
set { SetValue(TileMarginProperty, value); }
}
/// <summary>
/// Tile之间的间距
/// </summary>
public static readonly DependencyProperty TileMarginProperty =
DependencyProperty.Register("TileMargin", typeof(Thickness), typeof(TilePanel), new FrameworkPropertyMetadata(new Thickness(2), FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 最小的高度比例
/// </summary>
private int MinHeightPix { get; set; }
/// <summary>
/// 最小的宽度比例
/// </summary>
private int MinWidthPix { get; set; } #endregion #region 方法 private Dictionary<string, Point> Maps { get; set; }
private OccupyType SetMaps(Point currentPosition, Size childPix)
{
var isOccupy = OccupyType.NONE; if (currentPosition.X + currentPosition.Y != 0)
{
if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)
{
isOccupy = this.IsOccupyWidth(currentPosition, childPix);
}
else
{
isOccupy = this.IsOccupyHeight(currentPosition, childPix);
}
} if (isOccupy == OccupyType.NONE)
{
for (int i = 0; i < childPix.Width; i++)
{
for (int j = 0; j < childPix.Height; j++)
{
this.Maps[string.Format("x_{0}y_{1}", currentPosition.X + i, currentPosition.Y + j)] = new Point(currentPosition.X + i, currentPosition.Y + j);
}
}
} return isOccupy;
}
private OccupyType IsOccupyWidth(Point currentPosition, Size childPix)
{
//计算当前行能否放下当前元素
if (this.TileCount - currentPosition.X - childPix.Width < 0)
{
return OccupyType.OVERFLOW;
} for (int i = 0; i < childPix.Width; i++)
{
if (this.Maps.ContainsKey(string.Format("x_{0}y_{1}", currentPosition.X + i, currentPosition.Y)))
{
return OccupyType.WIDTHHEIGHT;
}
} return OccupyType.NONE;
}
private OccupyType IsOccupyHeight(Point currentPosition, Size childPix)
{
//计算当前行能否放下当前元素
if (this.TileCount - currentPosition.Y - childPix.Height < 0)
{
return OccupyType.OVERFLOW;
} for (int i = 0; i < childPix.Height; i++)
{
if (this.Maps.ContainsKey(string.Format("x_{0}y_{1}", currentPosition.X, currentPosition.Y + i)))
{
return OccupyType.WIDTHHEIGHT;
}
} return OccupyType.NONE;
}
/// <summary>
///
/// </summary>
/// <param name="finalSize"></param>
/// <returns></returns>
protected override Size ArrangeOverride(Size finalSize)
{
Size childPix = new Size();
Point childPosition = new Point();
Point? lastChildPosition = null;
OccupyType isOccupy = OccupyType.NONE;
FrameworkElement child; this.Maps = new Dictionary<string, Point>();
for (int i = 0; i < this.Children.Count; )
{
child = this.Children[i] as FrameworkElement;
childPix.Width = TilePanel.GetWidthPix(child);
childPix.Height = TilePanel.GetHeightPix(child); if (this.Orientation == System.Windows.Controls.Orientation.Vertical)
{
if (childPix.Height > this.TileCount)
{
childPix.Height = this.TileCount;
}
}
else
{
if (childPix.Width > this.TileCount)
{
childPix.Width = this.TileCount;
}
}
isOccupy = this.SetMaps(childPosition, childPix);
//换列
if (isOccupy == OccupyType.WIDTHHEIGHT)
{
if (lastChildPosition == null) lastChildPosition = childPosition;
if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)
{
childPosition.X += this.MinWidthPix;
}
else
{
childPosition.Y += this.MinHeightPix;
}
}
//换行
else if (isOccupy == OccupyType.OVERFLOW)
{
if (lastChildPosition == null) lastChildPosition = childPosition;
if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)
{
childPosition.X = 0;
childPosition.Y += this.Maps[string.Format("x_{0}y_{1}", childPosition.X, childPosition.Y)].Y;
//childPosition.Y++;//= this.MinHeightPix;
}
else
{
childPosition.Y = 0;
childPosition.X += this.Maps[string.Format("x_{0}y_{1}", childPosition.X, childPosition.Y)].X;
//childPosition.X++;//= this.MinWidthPix;
}
}
else
{
i++;
child.Arrange(new Rect(childPosition.X * this.TileWidth + Math.Floor(childPosition.X / this.MinWidthPix) * (this.TileMargin.Left + this.TileMargin.Right),
childPosition.Y * this.TileHeight + Math.Floor(childPosition.Y / this.MinHeightPix) * (this.TileMargin.Top + this.TileMargin.Bottom),
child.DesiredSize.Width, child.DesiredSize.Height));
if (lastChildPosition != null)
{
childPosition = (Point)lastChildPosition;
lastChildPosition = null;
}
else
{
if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)
{
childPosition.X += childPix.Width;
if (childPosition.X == this.TileCount)
{
childPosition.X = 0;
childPosition.Y++;
}
}
else
{
childPosition.Y += childPix.Height;
if (childPosition.Y == this.TileCount)
{
childPosition.Y = 0;
childPosition.X++;
}
}
}
}
} return finalSize;
}
/// <summary>
///
/// </summary>
/// <param name="constraint"></param>
/// <returns></returns>
protected override Size MeasureOverride(Size constraint)
{
int childWidthPix, childHeightPix, maxRowCount = 0; if (this.Children.Count == 0) return new Size();
//遍历孩子元素
foreach (FrameworkElement child in this.Children)
{
childWidthPix = TilePanel.GetWidthPix(child);
childHeightPix = TilePanel.GetHeightPix(child); if (this.MinHeightPix == 0) this.MinHeightPix = childHeightPix;
if (this.MinWidthPix == 0) this.MinWidthPix = childWidthPix; if (this.MinHeightPix > childHeightPix) this.MinHeightPix = childHeightPix;
if (this.MinWidthPix > childWidthPix) this.MinWidthPix = childWidthPix;
} foreach (FrameworkElement child in this.Children)
{
childWidthPix = TilePanel.GetWidthPix(child);
childHeightPix = TilePanel.GetHeightPix(child); child.Margin = this.TileMargin;
child.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
child.VerticalAlignment = System.Windows.VerticalAlignment.Top; child.Width = this.TileWidth * childWidthPix + (child.Margin.Left + child.Margin.Right) * ((childWidthPix - this.MinWidthPix) / this.MinWidthPix);
child.Height = this.TileHeight * childHeightPix + (child.Margin.Top + child.Margin.Bottom) * ((childHeightPix - this.MinHeightPix) / this.MinHeightPix); maxRowCount += childWidthPix * childHeightPix; child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
} if (this.TileCount <= 0) throw new ArgumentOutOfRangeException();
//if (this.MinWidthPix == 0) this.MinWidthPix = 1;
//if (this.MinHeightPix == 0) this.MinHeightPix = 1;
if (this.Orientation == Orientation.Horizontal)
{
this.Width = constraint.Width = this.TileCount * this.TileWidth + this.TileCount / this.MinWidthPix * (this.TileMargin.Left + this.TileMargin.Right);
double heightPix = Math.Ceiling((double)maxRowCount / this.TileCount);
if (!double.IsNaN(heightPix))
constraint.Height = heightPix * this.TileHeight + heightPix / this.MinHeightPix * (this.TileMargin.Top + this.TileMargin.Bottom);
}
else
{
this.Height = constraint.Height = this.TileCount * this.TileHeight + this.TileCount / this.MinHeightPix * (this.TileMargin.Top + this.TileMargin.Bottom);
double widthPix = Math.Ceiling((double)maxRowCount / this.TileCount);
if (!double.IsNaN(widthPix))
constraint.Width = widthPix * this.TileWidth + widthPix / this.MinWidthPix * (this.TileMargin.Left + this.TileMargin.Right);
} return constraint;
} #endregion
}
WPF中的瀑布流布局(TilePanel)控件的更多相关文章
- WPF中不规则窗体与WindowsFormsHost控件的兼容问题完美解决方案
首先先得瑟一下,有关WPF中不规则窗体与WindowsFormsHost控件不兼容的问题,网上给出的解决方案不能满足所有的情况,是有特定条件的,比如 WPF中不规则窗体与WebBrowser控件的兼 ...
- WPF中不规则窗体与WebBrowser控件的兼容问题解决办法
原文:WPF中不规则窗体与WebBrowser控件的兼容问题解决办法 引言 这几天受委托开发一个网络电视项目,要求初步先使用内嵌网页形式实现视频播放和选单,以后再考虑将网页中的所有功能整合进桌面程序. ...
- WPF 中动态创建和删除控件
原文:WPF 中动态创建和删除控件 动态创建控件 1.容器控件.RegisterName("Name",要注册的控件) //注册控件 2.容器控件.FindName(" ...
- 封装:WPF中可以绑定的BindPassWord控件
原文:封装:WPF中可以绑定的BindPassWord控件 一.目的:本身自带的PassWord不支持绑定 二.Xaml部分 <UserControl x:Class="HeBianG ...
- WPF 中动态创建、删除控件,注册控件名字,根据名字查找控件
动态创建控件 1.容器控件.RegisterName("Name",要注册的控件) //注册控件 2.容器控件.FindName("Name") as 控 ...
- WPF中增加Month Calendar月历控件
XAML代码:(这里使用了codeproject.com网站上的一个Dll,你可以在这里下载它:http://www.codeproject.com/cs/miscctrl/MonthCalendar ...
- WPF中实现多选ComboBox控件
在WPF中实现带CheckBox的ComboBox控件,让ComboBox控件可以支持多选. 将ComboBox的ItemsSource属性Binding到一个Book的集合, public clas ...
- 在WPF中的Canvas上实现控件的拖动、缩放
如题,项目中需要实现使用鼠标拖动.缩放一个矩形框,WPF中没有现成的,那就自己造一个轮子:) 造轮子前先看看Windows自带的画图工具中是怎样做的,如下图: 在被拖动的矩形框四周有9个小框,可以从不 ...
- WPF中Expander的用法和控件模板详解
一.Expander的用法 在WPF中,Expander是一个很实用的复合控件,可以很方便的实现下拉菜单和导航栏等功能.先介绍简单的用法,而后分析他的控件模板. <Window.Resource ...
随机推荐
- MS SQL SERVER 中的系统表
MS SQL SERVER 中的系统表 序号 名称 说明 备注 1 syscolumns 每个表和视图中的每列在表中占一行,存储过程中的每个参数在表中也占一行. 2 syscomments 包含每 ...
- DML 数据操控语言
一.DML数据操作语言 (DQL select ) 主要用于检索.插入和修改数据库信息.它是最常用的SQL命令,如INSERT(插入).UPDATE(更新).SELECT(选择).DELETE(删 ...
- 使用JSON进行数据传输的总结
一.选择的意义 在异步应用程序中发送和接收信息时,可以选择以纯文本和 XML 作为数据格式.为了更好的使用ajax, 我们将学习一种有用的数据格式 JavaScript Object Notation ...
- javascript组件开发之基类继承实现
上一篇文章大概的介绍了一下关于javascript组件的开发方式,这篇文章主要详细记一下基类的编写,这个基类主要是实现继承的功能 为什么要封装基类? 由于这次重构项目需要对各种组件进行封装,并且这些组 ...
- 剑指Offer02 替换空格
/************************************************************************* > File Name: 02_Replac ...
- hdu 3656 DLX
思路:二分枚举建边,用DLX判断是否满足. #include<set> #include<cmath> #include<queue> #include<cs ...
- 转自:如何自学Android(强烈推荐)
转自: http://gityuan.com/2016/04/24/how-to-study-android/ 看到很多人提问非科班该如何学习编程,其实科班也基本靠自学.有句话叫“师傅领进门修行靠个人 ...
- 动态创建DataTable总结
最简单的: DataTable dt = new DataTable(); dt.Columns.Add("id"); dt.Columns.Add("name" ...
- C#反射(转载)
[转]C#反射 反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类.结构.委托.接口和枚举等)的成员,包括方法.属性.事件,以及构造函数等. ...
- 通过Scrapy抓取QQ空间
毕业设计题目就是用Scrapy抓取QQ空间的数据,最近毕业设计弄完了,来总结以下: 首先是模拟登录的问题: 由于Tencent对模拟登录比较讨厌,各个防备,而本人能力有限,所以做的最简单的,手动登录后 ...