最近又在搞点小东西,美化界面的时候发现瀑布流效果比较不错.顺便就搬到了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.不知道是否有更简单明了的方法.

posted on 2013-10-25 11:30  烟灰灰  阅读(4646)  评论(12编辑  收藏  举报