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 ...
随机推荐
- 使用keil建立标准STM32工程模版(图文详细版!)
1. 模板工程的创建(超级详细版,使用的是keil 4.5版本) 1.1创建工程目录 良好的工程结构能让文件的管理更科学,让开发更容易更方便,希望大家养成良好的习惯,使用具有合理结构的工程目录,当 ...
- Oracle基础 exp/imp命令
一.导出方式: 使用exp/imp方式导出数据分为四种方式: 1.表方式导出:一个或多个指定的表,包括表的定义.表数据.表的所有者授权.表索引.表约束,以及创建在该表上的触发器.也可以只导出结构,不导 ...
- hg(Mercurial)版本库迁移到git版本库
这几天没事干净搞迁移了,迁移完MVC又迁移版本库,还把工作电脑迁移了一下,开始用Win8.1了.这个迁移主要是因为实在不想在工作电脑上又装git又装hg了,点个右键出来一大堆菜单,况且现在git已经成 ...
- Python 标准库 urllib2 的使用细节[转]
转自[http://zhuoqiang.me/python-urllib2-usage.html] Python 标准库中有很多实用的工具类,但是在具体使用时,标准库文档上对使用细节描述的并不清楚,比 ...
- hdu 4055 动态规划
#include<map> #include<set> #include<cmath> #include<queue> #include<cstd ...
- 【原】 twemproxy ketama一致性hash分析
转贴请注明原帖位置:http://www.cnblogs.com/basecn/p/4288456.html 测试Twemproxy集群,双主双活 向twemproxy集群做写操作时,发现key的分布 ...
- 转:怎么使用github(通俗易懂版)
转: https://www.zhihu.com/question/20070065 作者:珊姗是个小太阳链接:https://www.zhihu.com/question/20070065/ans ...
- 自定义按钮设置BadgeNumber
TabbarButton.h @interface TabbarButton : UIButton @property (nonatomic, strong) UIButton *badgeValue ...
- thinkphp,javascript跨域请求解决方案
javascript跨域请求解决方案 前言 对于很多前端或者做混合开发的同学,我们难免会遇到跨域发起请求业务,比如A站点向B站点请求数据等等.由于最近要做一个站点集群的项目,所以具体业务要求很多个站点 ...
- Ionic Android开发环境搭建 上
首先,需要下载并安装Node.js. 什么是Node.js?百科上说:Node.js是一个基于Chrome JavaScript运行时建立的平台, 用于方便地搭建响应速度快.易于扩展的网络应用.Nod ...