• WPF下制作的简单瀑布流效果


    最近又在搞点小东西,美化界面的时候发现瀑布流效果比较不错.顺便就搬到了WPF,下面是界面

    我对WEB前端不熟,JS和CSS怎么实现的,我没去研究过,这里就说下WPF的实现思路,相当简单.

    1.最重要的就是每个子项的顺序填充,我是把界面看做N列,然后在每列里依次加载子项.最后结果就是,界面放一个Uniform,设置Columns,再添加几个ItemsControl.

    2.添加Item的时候,判断每个ItemsControl的实际高度,把子项添加到最小的那个ItemsControl,这样避免了某一列拉得很长.

    3.再做一层封装,就变成了一个支持Binding的WaterfallControl.

    这里上几段控件的源码,供参考:

    1.WaterfallControl.cs

      1 [TemplatePart(Name = "grdRoot", Type = typeof(UniformGrid))]
      2     public class WaterfallControl : ItemsControl
      3     {
      4         private UniformGrid grdRoot;
      5 
      6         private List<ItemsControl> itemsContorls;
      7 
      8         public int Columns
      9         {
     10             get { return (int)GetValue(ColumnsProperty); }
     11             set { SetValue(ColumnsProperty, value); }
     12         }
     13 
     14         // Using a DependencyProperty as the backing store for Columns.  This enables animation, styling, binding, etc...
     15         public static readonly DependencyProperty ColumnsProperty =
     16             DependencyProperty.Register("Columns", typeof(int), typeof(WaterfallControl), new PropertyMetadata(3, OnColumnsChanged));
     17 
     18         private static void OnColumnsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
     19         {
     20             int columns = (int)e.NewValue;
     21             if (columns < 1)
     22             {
     23                 throw new ArgumentOutOfRangeException("Columns");
     24             }
     25             var control = sender as WaterfallControl;
     26             control.Columns = columns;
     27             control.InitPanel();
     28         }
     29 
     30         public WaterfallControl()
     31         {
     32             this.Loaded += WaterfallControl_Loaded;
     33             this.itemsContorls = new List<ItemsControl>();
     34         }
     35 
     36         void WaterfallControl_Loaded(object sender, RoutedEventArgs e)
     37         {
     38             this.InitPanel();
     39         }
     40 
     41         private void InitPanel()
     42         {
     43             if (!this.IsLoaded)
     44             {
     45                 return;
     46             }
     47 
     48             grdRoot.Children.Clear();
     49             itemsContorls.Clear();
     50             for (var i = 0; i < this.Columns; i++)
     51             {
     52                 var ic = new ItemsControl();
     53                 ic.ItemTemplate = this.ItemTemplate;
     54                 ic.VerticalAlignment = System.Windows.VerticalAlignment.Top;
     55                 grdRoot.Children.Add(ic);
     56                 itemsContorls.Add(ic);
     57             }
     58 
     59             if (this.ItemsSource != null)
     60             {
     61                 var enumerator = this.ItemsSource.GetEnumerator();
     62                 while (enumerator.MoveNext())
     63                 {
     64                     this.AddChild(enumerator.Current);
     65                 }
     66             }
     67         }
     68 
     69         public override void OnApplyTemplate()
     70         {
     71             base.OnApplyTemplate();
     72             grdRoot = (UniformGrid)this.GetTemplateChild("grdRoot");
     73         }
     74 
     75         protected override void AddChild(object value)
     76         {
     77             var ic = itemsContorls.OrderBy(t => t.ActualHeight).FirstOrDefault();
     78             ic.Items.Add(value);
     79             ic.UpdateLayout();
     80         }
     81 
     82         protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
     83         {
     84             if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Remove)
     85             {
     86                 var enumerator = e.NewItems.GetEnumerator();
     87                 while (enumerator.MoveNext())
     88                 {
     89                     if (e.Action == NotifyCollectionChangedAction.Add)
     90                     {
     91                         this.AddChild(enumerator.Current);
     92                     }
     93                     else
     94                     {
     95                         foreach (var ic in this.itemsContorls)
     96                         {
     97                             ic.Items.Remove(enumerator.Current);
     98                         }
     99                     }
    100                 }
    101             }
    102         }
    103     }
    View Code

    2.WaterfallControl的样式

    <Style TargetType="controls:WaterfallControl">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="controls:WaterfallControl">
                        <ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
                            <UniformGrid Name="grdRoot" Columns="{TemplateBinding Columns}">
    
                            </UniformGrid>
                        </ScrollViewer>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    View Code

    3.调用

    WaterfallControl继承自ItemsControl,所以和ItemsControl的使用没有区别,只需要额外指定一个Columns即可.

    可能遇到的问题:

    1.遇到图片不能直接计算高度,可能导致某列很长.可以用扩展属性给图片指定一个初始占位高度.

    2.遇到界面大小变化,列数是不是应该动态变化,这个要实现也简单,监视下Window.SizeChanged,然后改变Columns就行了.

    3.我直接把ScrollViewer放到WaterfallControl的模板里了,建议抽出来,监视下滚动事件,实现滚动到底加载数据.

    4.不知道是否有更简单明了的方法.

  • 相关阅读:
    Python学习第二天
    Python学习第一天
    linux下使用命令修改IP地址
    Java消息队列
    转:《什么是敏捷软件测试》
    测试流程优化
    MacOS安装使用Kettle
    用OneNote写博客的方法
    Matlab给三维点云添加高斯噪声和随机噪声
    如何高效完成英文文献翻译
  • 原文地址:https://www.cnblogs.com/scheshan/p/3387582.html
Copyright © 2020-2023  润新知