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 ...
随机推荐
- 【Android 界面效果28】Android应用中五种常用的menu
Android Menu在手机的应用中起着导航的作用,作者总结了5种常用的Menu. 1.左右推出的Menu 前段时间比较流行,我最早是在海豚浏览器中看到的,当时耳目一新.最早使用左右推出菜单的,听说 ...
- 【Android 界面效果26】listview android:cacheColorHint,android:listSelector属性作用
ListView是常用的显示控件,默认背景是和系统窗口一样的透明色,如果给ListView加上背景图片,或者背景颜色时,滚动时listView会黑掉, 原因是,滚动时,列表里面的view重绘时,用的依 ...
- Android小项目之三 splash界面
------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...
- iOS - UI - UISlider
6.UISlider //滑块 设置高度 UISlider * slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 100, CGRec ...
- iOS之block块
Block块. 1.声明Block int (^myBlock)(int n) = ^(int num) 类型 (^名称)(需要传的参数)= ^(参数) 2 __block 变量 在block块中修改 ...
- 100天成就卓越领导力:新晋领导者的First100训练法
<100天成就卓越领导力:新晋领导者的First100训练法> 基本信息 原书名:Your Frist 100 days: How to Make Maximum Impact in Yo ...
- Java跳出循环-break和continue语句
在实际编程中,有时需要在条件语句匹配的时候跳出循环.在Java里,由break和continue语句控制. “break”语句 “break”语句用来结束循环,即不再执行后边的所有循环. 示例:计算1 ...
- 话说Centos下nginx,php,mysql以及phpmyadmin的配置
大话centos下部署phalcon框架 Centos还是ubuntu? 当我沿用这个标题的时候,心里在想"我能说我之前用的windows吗?",windows下xampp,wam ...
- Oracle 学习笔记3:新建数据库没有scott用户解决办法
新建一个数据库,若选择Oracle组件时,没有选择实例方案,完成后进行口令管理,默认列表中是找不到scott用户解锁的.若要解锁scott用户,可以进行如下操作: 使用system或者sys连接数据库 ...
- Cocos2d-JS特效
Cocos2d-JS提供了很多特效,这些特效事实上属于间隔动作,特效类cc.GridAction类,也称为网格动作,它的类图如下图所示. 网格动作类图 网格动作cc.GridAction它有两个主要的 ...