zoukankan      html  css  js  c++  java
  • 【WPF】制作自定义的列表项面板

    我们在使用像ListBox的列表控件时,我们都知道可以通过其ItemsPanel的依赖项属性来自定义一个面板来放置列表控件中的列表项。除了CLR库提供的几个面板外,我们完全可以把自己写的面板作为项列表的容器。

    先给各位看看效果。

    如何?效果还好吧?

    面板的原理是这样的:

    1、从Panel类派出一个类,我命名为MyPanel。

    2、重写MeasureOverride方法,分别计算所有子元素的大小。

    3、重写ArrangeOverride方法,为每个子元素随机生成X和Y坐标,然后再用这个随机生的坐标来放置子元素。

    4、为了能隔一段时间自动排版一次,我就加入了一个DispatcherTimer,并公开一个SwapInterval属性,可以为调用者设置计时器的执行音隔,以秒为单位。

    5、后来想想,如果每次都仅仅调用InvalidateArrange方法来重新排列子元素,好像有些枯燥,不如在重新排列之间弄一些动画效果好看点。于是,我就在重新排列子元素之前让面板“淡出”;当面板排列子元素完成后,再来个“淡入”效果,不错。只是对Opacity属性进行动画处理就可以了,也不费事。

    好,整体的思路就是这样,然后把这个面板用到ListBox等列表控件的ItemsPanel上就行。

                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <local:MyPanel SwapInterval="6"/>
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>

    下面我贴一下整个MyPanel类的代码,以供大家参考。(代码进行了修订)

        public class MyPanel:Panel
        {
            Random rand = null;
            DispatcherTimer Timer = null;
    
            public MyPanel()
            {
                rand = new Random();
                Timer = new DispatcherTimer();
                Timer.Interval = TimeSpan.FromSeconds(SwapInterval);
                Timer.Tick += Timer_Tick;
                Timer.Start();
            }
    
            #region 属性
            public static readonly DependencyProperty SwapIntervalProperty = DependencyProperty.Register("SwapInterval", typeof(double), typeof(MyPanel), new PropertyMetadata(10d, new PropertyChangedCallback(SwapIntervalChanged), new CoerceValueCallback(CoerceValCallback)));
    
            private static void SwapIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                MyPanel p = d as MyPanel;
                double sec = (double)e.NewValue;
                p.Timer.Stop();
                p.Timer.Interval = TimeSpan.FromSeconds(sec);
                p.Timer.Start();
            }
    
            private static object CoerceValCallback(DependencyObject d, object baseValue)
            {
                // 通过该方法测检依赖项属性的值
                // 如果设置的值小于5秒,就自动改为5秒
                double dv = (double)baseValue;
                if (dv < 5d)
                {
                    dv = 5d;
                }
                return dv;
            }
    
            /// <summary>
            /// 切换动画间隔,以秒为单位
            /// </summary>
            public double SwapInterval
            {
                get { return (double)GetValue(SwapIntervalProperty); }
                set { SetValue(SwapIntervalProperty, value); }
            }
            #endregion
    
            void Timer_Tick(object sender, EventArgs e)
            {
                DoubleAnimation dat = new DoubleAnimation();
                dat.From = 1d;
                dat.To = 0d;
                dat.Duration = TimeSpan.FromMilliseconds(900);
                dat.Completed += (sd, arg) =>
                    {
                        // 当动画完成时,面板的Opacity为0
                        // 此时重新排列子元素
                        this.InvalidateArrange();
                        DoubleAnimation datBack = new DoubleAnimation();
                        datBack.From = 0d;
                        datBack.To = 1d;
                        datBack.Duration = TimeSpan.FromSeconds(1);
                        // 子元素排列完成后,再执行一个动画
                        // 将Opacity再改为1
                        this.BeginAnimation(OpacityProperty, datBack);
                    };
                // 开始动画,隐藏面板
                this.BeginAnimation(OpacityProperty, dat);
            }
    
            protected override Size MeasureOverride(Size availableSize)
            {
                // 处理子元素的大小
                foreach (UIElement u in InternalChildren)
                {
                    // 一定要对每个子元素调用Measure
                    // 不然,就看不到子元素了
                    u.Measure(availableSize);
                }
                // 如果不要求精确计算子元素占了多少
                // 空间,可以直接返回0-0的Size,但不
                // 要返回正无穷大,否则后果自负
                return new Size();
            }
    
            protected override Size ArrangeOverride(Size finalSize)
            {
                // 通过该方法对子元素进行排列
                foreach (UIElement item in InternalChildren)
                {
                    // 算出子元素的坐标的随机值
                    double maxX = finalSize.Width - item.DesiredSize.Width;
                    double maxY = finalSize.Height - item.DesiredSize.Height;
                    if (maxX <=0d)
                    {
                        maxX = 1d;
                    }
                    if (maxY <= 0d)
                    {
                        maxY = 1d;
                    }
                    double X = rand.Next(0, (int)maxX);
                    double Y = rand.Next(0, (int)maxY);
                    // 调用Arrange方法安排子元素放在哪个位置
                    item.Arrange(new Rect(X, Y, item.DesiredSize.Width, item.DesiredSize.Height));
                }
                return finalSize;
            }
        }


    代码可以移植到Windows Phone或Windows StoreApp中,原理都是一样的。


    其他代码就没有必要贴了,免得影响大家看帅哥美女,我把整个项目上传就是了。

    下载地址:http://files.cnblogs.com/tcjiaan/MyLayoutPanel.zip

  • 相关阅读:
    springboot mail 发送邮件
    颜色透明度16进制对照表
    Java基础系列之(三)
    QQ2010协议分析系列(五)
    QQ2010协议分析系列(四)
    QQ2010协议分析系列(三)
    QQ2010协议分析系列(二)
    QQ2010协议分析系列(一)
    Java基础系列之(二)
    Java基础系列之(一)
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/3612111.html
Copyright © 2011-2022 走看看