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 ...
随机推荐
- ASP.NET页面与IIS底层交互和工作原理详解(第三回)
引言 Http 请求处理流程 和 Http Handler 介绍 这两篇文章里,我们首先了解了Http请求在服务器端的处理流程,随后我们知道Http请求最终会由实现了IHttpHandler接口的类进 ...
- iOS开发-解决AVAudioRecorder录音文件无法保存的问题
我们在开发iOS客户端APP时,有时候会用到录音的功能,一般会使 AVAudioRecorder 这个类.如下面这样: @interface MyViewController : UIViewCont ...
- linux_cpu信息查询
查看cpu信息: [root@css-management ~]# cat /proc/cpuinfo processor : 0vendor_id : GenuineIntelcpu family ...
- SQL Server表的数据量大小查询
今天想在服务器上还原一个DB,发现磁盘空间不够,查看发现,其中一个DB竟然有56G了.因此想收缩一下这个DB,发现大小没多大变化.然后在网上找了找SQL脚本,看能不能查看下哪个表的数据量那么大. 网上 ...
- Providers、Controller 、Service、DirectiveFactory
Providers 是唯一一种你可以传进 .config() 函数的 service.当你想要在 service 对象启用之前,先进行模块范围的配置,那就应该用 provider Controller ...
- 第五节 关于SpringMVC中Ajax的配置和应用[下午]
成熟,不是学会表达,而是学会咽下,当你一点一点学会克制住很多东西,才能驾驭好人生. 还有一周,祥云19就算结算了,一个半月的相处希望,胖先生算一个合格的老师 小白,小蔡,2婷婷,小猴,小恒,小崔,小龙 ...
- web前端开发学习指南(大群主推荐)
http://www.ituring.com.cn/book/1140 http://www.ituring.com.cn/book/1361
- Sharepoint中有关文件夹的操作
1.GetItemsWithUniquePermissions根据返回数量和是否返回文件夹获取唯一权限的列表项集合 对于SharePoint对象模型中SPList的GetItemsWithUnique ...
- DOM 1
首先getAttribute setAttribute只能被元素节点对象调用.(属性节点和文本节点调用不了) 我们可以通过一下三种方式得到元素: document.getElementById(); ...
- 使用Jeditable插件时遇到的问题
Jeditable在渲染页面已有DIV=>form的时候 首先使用 $("div").html(); 去获取原DIV中的内容. 这样导致一个问题, 如果原div中带有html ...