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, 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

WPF中的瀑布流布局(TilePanel)控件的相关文章

WPF中不规则窗体与WindowsFormsHost控件的兼容问题完美解决方案

首先先得瑟一下,有关WPF中不规则窗体与WindowsFormsHost控件不兼容的问题,网上给出的解决方案不能满足所有的情况,是有特定条件的,比如  WPF中不规则窗体与WebBrowser控件的兼容问题解决办法.该网友的解决办法也是别出心裁的,为什么这样说呢,你下载了他的程序认真读一下就便知道,他的webBrowser控件的是单独放在一个Form中,让这个Form与WPF中的一个Bord控件进行关联,进行同步移动,但是在移动的时候会出现闪烁,并且还会出现运动的白点,用户体验肯定不好. OK,

WPF布局之让你的控件随着窗口等比放大缩小,适应多分辨率满屏填充应用

一直以来,我们设计windows应用程序,都是将控件的尺寸定好,无论窗体大小怎么变,都不会改变,这样的设计对于一般的应用程序来说是没有问题的,但是对于一些比较特殊的应用,比如有背景图片的,需要铺面整个屏幕,由于存在多种不同的分辨率,所以会出现布局混乱的情况.今天我们来看看WPF中如何让我们的控件也随着分辨率放大缩小.下面来写一个例子看看效果吧~  一.普通布局中的问题 这里我们写一个简单的页面,新建WPF项目,在MainWindow里面添加按钮,如下图: 这个页面很简单,只有三个按钮,我们想的是

WPF自定义控件与样式(10)-进度控件ProcessBar自定义样

一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: ProcessBar自定义标准样式: ProcessBar自定义环形进度样式: 二.ProcessBar标准样式 效果图: ProcessBar的样式非常简单: <!--ProgressBar Style--> <Style TargetType="ProgressBar" x

asp.net中遍历界面上所有控件进行属性设置

* 使用方法: *  前台页面调用方法,重置:    protected void Reset_Click(object sender, EventArgs e)        {            initControl(Page, "isClear");        } * 备注信息: 上传部分自己总结的常用方法的封装,有不足和不完美之处,希望大家指出来,愿意一起 * 主要研究erp,cms,crm,b2b,oa等系统和网站的开发,欢迎有共同追求和学的IT人员一起学习和交流.

WPF: 实现 ScrollViewer 滚动到指定控件处

原文:WPF: 实现 ScrollViewer 滚动到指定控件处 在前端 UI 开发中,有时,我们会遇到这样的需求:在一个 ScrollViewer 中有很多内容,而我们需要实现在执行某个操作后能够定位到其中指定的控件处:这很像在 HTML 页面中点击一个链接后定位到当前网页上的某个 anchor. 要实现它,首先我们需要看 ScrollViewer 为我们提供的 API,其中并没有类似于 ScrollToControl 这样的方法:在它的几个以 ScrollTo 开头的方法中,最合适的就是 S

wpf怎么使用WindowsFormsHost(即winform控件)

原文:wpf怎么使用WindowsFormsHost(即winform控件) 使用方法: 1.首先,我们需要向项目中的引用(reference)中添加两个动态库dll,一个是.NET库中的System.Windows.Forms,另外一个是WindowsFormsIntegration: 2.添加完两个动态dll以后,就可以在控件库中找到WindowsFormsHost这个控件: 3.将这个控件放入窗体,放置完以后在xmal代码中会自动生成相应代码: <Grid> <WindowsFor

【WPF学习】第二十章 内容控件

原文:[WPF学习]第二十章 内容控件 内容控件(content control)是更特殊的控件类型,它们可包含并显示一块内容.从技术角度看,内容控件时可以包含单个嵌套元素的控件.与布局容器不同的是,内容控件只能包含一个子元素,而布局容器主要愿意可以包含任意多个牵头元素. 正如前面所介绍,所有WPF布局容器都继承自抽象类Panel,该类提供了对包含多个元素的支持.类似地,所有内容控件都继承自抽象类ContentControl.下图显示了ContentControl类的层次结构. 图 Conten

FineUI之使用SQL脚本从数据库表中生成相应的输入控件

在WEB开发时,经常需要依据数据库表中的字段建立相应的输入控件,来获取输入的数据.每次都需要按字段来敲,显然太低效,而且容易出错.这里提供一个SQL脚本生成相应输入控件的方法. USE DBDemo DECLARE @TEMP_TABLE_NAME NVARCHAR(512) DECLARE @WIDTH NVARCHAR(50) SET @TEMP_TABLE_NAME='Stuff' SET @WIDTH='200' SELECT '<f:'+TOKEN+' runat="server

解决Android中,禁止ScrollView内的控件改变之后自动滚动

问题: 最近在写一个程序界面,有一个scrollVIew,其中有一段内容是需要在线加载的. 当内容加载完成后,ScrollView中内容的长度会发生改变,这时ScrollView会自动下滚,如下图所示: 滚动的那一下体验特别不好,所以要防止这种情况.即不论Scrollview中内容如何,都要保持在最上. 解决办法: 先简单写一下我的xml文件的结构: [html] view plaincopy <ScrollView android:id="@+id/scrollView1" a