最近在用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, bool> 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)] = true; } } } 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, bool>(); 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.MinHeightPix; } else { childPosition.Y = 0; childPosition.X += this.MinWidthPix; } } else { i++; child.Arrange(new Rect(childPosition.X * this.TileWidth + childPosition.X / this.MinWidthPix * (this.TileMargin.Left + this.TileMargin.Right), childPosition.Y * this.TileHeight + 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; } else { childPosition.Y += childPix.Height; } } } } 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)控件,布布扣,bubuko.com
时间: 2024-10-24 17:48:06