WPF下製做的簡單瀑布流效果

最近又在搞點小東西,美化界面的時候發現瀑布流效果比較不錯.順便就搬到了WPF,下面是界面前端

 

我對WEB前端不熟,JS和CSS怎麼實現的,我沒去研究過,這裏就說下WPF的實現思路,至關簡單.ide

1.最重要的就是每一個子項的順序填充,我是把界面看作N列,而後在每列裏依次加載子項.最後結果就是,界面放一個Uniform,設置Columns,再添加幾個ItemsControl.this

2.添加Item的時候,判斷每一個ItemsControl的實際高度,把子項添加到最小的那個ItemsControl,這樣避免了某一列拉得很長.spa

3.再作一層封裝,就變成了一個支持Binding的WaterfallControl.code

 

這裏上幾段控件的源碼,供參考:orm

1.WaterfallControl.csblog

  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.不知道是否有更簡單明瞭的方法.

相關文章
相關標籤/搜索