zoukankan      html  css  js  c++  java
  • windowsphone 瀑布流&ui虚拟化

    瀑布流已经有点年代了吧,不过wp上还真是挺少资料的。今天抽空把自己之前搞过的东西写出来,避免大家重复劳动。

    一、简单的瀑布流排版加入ui虚拟化。

    最近看了 段博琼  ui虚拟化的一篇博文,链接:http://www.cnblogs.com/hebeiDGL/p/3410575.html

    觉得还不错,于是下载了他的demo稍微改了一下瀑布流效果。

    demo截图如下:

          

    主要改动:

    1:自定义WaterFallPanel继承Panel,用于实现瀑布流排版,并保持容器children距离顶部高度的信息:

    public class WaterFallPanel : Panel
        {
            public WaterFallPanel()
            {
                /**默认2列**/
                ColumnCount = 2;
                ColumnHeight = new double[ColumnCount];
                childwidth = 480 / ColumnCount;
            }
    
            double childwidth;
            static double[] ColumnHeight;
    
            /// <summary>
            /// 列数
            /// </summary>
            public int ColumnCount
            {
                get { return (int)this.GetValue(ColumnCountProperty); }
                set { this.SetValue(ColumnCountProperty, value); }
            }
    
    
            public static DependencyProperty ColumnCountProperty = DependencyProperty.Register("WaterFallPanel", typeof(int), typeof(WaterFallPanel), new PropertyMetadata(new PropertyChangedCallback((o, e) =>
            {
                ColumnHeight = new double[(int)e.NewValue];
                if (o == null || e.NewValue == e.OldValue)
                    return;
                o.SetValue(ColumnCountProperty, e.NewValue);
            })));
    
    
            protected override Size MeasureOverride(Size availableSize)
            {
    
                Size resultSize = new Size(0, 0);
                //List<DependencyObject> Children = this.GetVisualChildren().ToList();
                for (int i = 0; i < Children.Count; i++)
                {
                    Children[i].Measure(availableSize);
                }
                #region 测量值
                for (int i = 0; i < ColumnHeight.Count(); i++)
                {
                    ColumnHeight[i] = 0;
                }
                heightlist.Clear();
                for (int i = 0; i < Children.Count; i++)
                {
                    double miniheight = ColumnHeight.Min();
                    int h = 0;
                    for (; h < ColumnHeight.Length; h++)
                    {
                        if (ColumnHeight[h] == miniheight)
                        {
                            break;
                        }
                    }
                    ColumnHeight[h] += Children[i].DesiredSize.Height;
    
                    heightlist.Add(ColumnHeight.Min());
                }
                #endregion
    
                resultSize.Height = ColumnHeight.Max();
                if (Children.Count == 0)
                {
                    resultSize.Height = 0;
                    resultSize.Width = 480;
                }
                else
                {
                    resultSize.Width = (Children.Count + 1) * Children[0].DesiredSize.Width;
                }
    
                return resultSize;
            }
    
            protected override Size ArrangeOverride(Size finalSize)
            {
                for (int i = 0; i < ColumnHeight.Count(); i++)
                {
                    ColumnHeight[i] = 0;
                }
    
                #region 排列值
                heightlist.Clear();
                for (int i = 0; i < Children.Count; i++)
                {
                    double miniheight = ColumnHeight.Min();
                    int h = 0;
                    for (; h < ColumnHeight.Length; h++)
                    {
                        if (ColumnHeight[h] == miniheight)
                        {
                            break;
                        }
                    }
                    Children[i].Arrange(new Rect(new Point(Children[i].DesiredSize.Width * h, ColumnHeight[h]), Children[i].DesiredSize));
                    ColumnHeight[h] += Children[i].DesiredSize.Height;
                    if (h == 0)
                    {
                        if (Children[i].DesiredSize.Width > childwidth)
                        {
                            ColumnHeight[h + 1] += Children[i].DesiredSize.Height;
                        }
                    }
                    heightlist.Add(ColumnHeight.Min());
                }
                if (Children.Count < ColumnCount)
                    finalSize.Width = childwidth * (Children.Count + 1);
                else
                    finalSize.Width = childwidth * (ColumnCount + 1);
    
    
                #endregion
    
    
                return finalSize;
            }
            private List<double> heightlist = new List<double>();
            public double GetItemHeight(int item)
            {
                if (item >= 0 && item < heightlist.Count)
                {
                    return heightlist[item];
                }
                else
                {
                    return 0;
                }
            }
        }
    View Code

    2:MainPage.xaml中修改itemspanl

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <LocalControl:WaterFallPanel x:Name="test" Loaded="test_Loaded"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>    

    3:MainPage.cs中修改虚拟化的计算方式:

     void Visualizition()
            {
                // 自定义一个“字典”,用来保存每项的 Y坐标 和它“本身”的引用
                Dictionary<double, StackPanel> dic = new Dictionary<double, StackPanel>();
    
                double height_sum = 0;
                for (int i = 0; i < listbox.Items.Count; i++)
                {
                    // 因为 ListBox 是通过数据绑定生成的列表,所以需要通过 ItemContainerGenerator 获得
                    // 每一项的 StackPanel 这个父控件
                    FrameworkElement fe = listbox.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
                    StackPanel sp = FindFirstElementInVisualTree<StackPanel>(fe);
                    
                    dic.Add(height_sum, sp);
    
                    // 累加 Y高度
                    if (height_sum <= waterpanel.GetItemHeight(i))
                    {
                        height_sum = waterpanel.GetItemHeight(i)+1;
                    }
                    else
                    {
                        height_sum = waterpanel.GetItemHeight(i);
                    }
                    
                    // 设置它的高度为自己的实际高度
                    sp.Height = sp.ActualHeight; 
                }
    
                // 每0.5秒钟,循环检查一次列表中,哪些项在屏幕内,如果在屏幕内,则显示,如果
                // 在屏幕外,则隐藏
                Observable.Interval(TimeSpan.FromSeconds(.5)).ObserveOnDispatcher().Subscribe((_) =>
                    {
                        foreach (var keyValue in dic)
                        {
                            if (((scrollViewer.VerticalOffset - 700) > keyValue.Key || keyValue.Key > (scrollViewer.VerticalOffset + 900)))
                            {
                                keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Collapsed;
                                keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Collapsed;
                            }
                            else
                            {
                                keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Visible;
                                keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Visible;
                            }
                        }
                    });
            }
    View Code

      其中WaterFallPanel的获取用了比较奇葩的一个方式。

    这样做的好处是代码简单明了,适合新手了解ui虚拟化。不好的地方则是每次想实现瀑布流排版,都得手动写入虚拟化计算代码。代码耦合过高。

    二、通过自定义listbox实现瀑布流&虚拟化。降低代码耦合度。

    demo截图:

    1、自定义PowerListBox继承listbox

     public class PowerListBox:ListBox
        {
            // 自定义一个“字典”,用来保存每项的 Y坐标 和它“本身”的引用
            Dictionary<double, Border> dic = new Dictionary<double, Border>();
    
            public PowerListBox()
            {
                DefaultStyleKey = typeof(PowerListBox);
                Loaded += PowerListBox_Loaded;
                Unloaded += PowerListBox_Unloaded;
            }
            private ScrollViewer _scrollView;
            private bool _isScrolling;
            void PowerListBox_Loaded(object sender, System.Windows.RoutedEventArgs e)
            {
                if (DesignerProperties.GetIsInDesignMode(this))
                    return;
                ApplyTemplate();
                _scrollView.ScrollToVerticalOffset(_scrollView.VerticalOffset);
    
                var border = _scrollView.Descendants<Border>().FirstOrDefault();
                Debug.Assert(border != null);
                var vsGroup = VisualStateManager.GetVisualStateGroups(border).OfType<VisualStateGroup>().FirstOrDefault(s => s.Name == "ScrollStates");
                Debug.Assert(vsGroup != null);
                vsGroup.CurrentStateChanging += vsGroup_CurrentStateChanging;
    
    
    
                if (ItemsRootPanel != null && ItemsRootPanel.Children != null)
                {
                    double height_sum=0;
                    dic.Clear();
                    for (int i = 0; i < ItemsRootPanel.Children.Count; i++)
                    {
                        // 因为 ListBox 是通过数据绑定生成的列表,所以需要通过 ItemContainerGenerator 获得
                        // 每一项的 StackPanel 这个父控件
                        Border fe = VisualTreeHelper.GetChild(ItemsRootPanel.Children[i], 0) as Border;
                        dic.Add(height_sum, fe);
    
                        // 累加 Y高度
                        //var height = .GetItemHeight(i);
                        if (height_sum <= (ItemsRootPanel as WaterFallPanel).GetItemHeight(i))
                        {
                            height_sum = (ItemsRootPanel as WaterFallPanel).GetItemHeight(i) + 1;
                        }
                        else
                        {
                            height_sum = (ItemsRootPanel as WaterFallPanel).GetItemHeight(i);
                        }
    
                        // 设置它的高度为自己的实际高度
                        fe.Height = fe.ActualHeight;
                    }
    
                }
    
            }
            void PowerListBox_Unloaded(object sender, System.Windows.RoutedEventArgs e)
            {
                var border = _scrollView.Descendants<Border>().FirstOrDefault();
                Debug.Assert(border != null);
                var vsGroup = VisualStateManager.GetVisualStateGroups(border).OfType<VisualStateGroup>().FirstOrDefault(s => s.Name == "ScrollStates");
                Debug.Assert(vsGroup != null);
                vsGroup.CurrentStateChanging -= vsGroup_CurrentStateChanging;
            }
    
    
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                _scrollView = GetTemplateChild("ScrollViewer") as ScrollViewer;
                Debug.Assert(_scrollView != null);
            }
    
            void vsGroup_CurrentStateChanging(object sender, VisualStateChangedEventArgs e)
            {
                if (e.NewState.Name == "NotScrolling")
                {
                    _isScrolling = false;
                    CompositionTarget.Rendering -= CompositionTarget_Rendering;
                }
                else
                {
                    _isScrolling = true;
                    CompositionTarget.Rendering += CompositionTarget_Rendering;
                }
            }
            void CompositionTarget_Rendering(object sender, EventArgs e)
            {
                RefreshDisplayArea();
            }
    
            void RefreshDisplayArea()
            {
                foreach (var keyValue in dic)
                {
                    if (((_scrollView.VerticalOffset - 20) > keyValue.Key || keyValue.Key > (_scrollView.VerticalOffset + 600)))
                    {
                        keyValue.Value.Child.Visibility = Visibility.Collapsed;
                    }
                    else
                    {
                        keyValue.Value.Child.Visibility = Visibility.Visible;
                    }
                }
                //for (int i = 0; i < ItemsRootPanel.Children.Count; i++)
                //{
                //    var item = GetItem(i);
                //    var height = (ItemsRootPanel as WaterFallPanel).GetItemHeight(i) ;
                //    if (((_scrollView.VerticalOffset - 20) > height || height > (_scrollView.VerticalOffset + 607)))
                //    {
                //        FrameworkElement fe = VisualTreeHelper.GetChild(item, 0) as FrameworkElement;
                //        fe.Visibility = Visibility.Collapsed;
                //        //keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Collapsed;
                //        //keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Collapsed;
                //    }
                //    else
                //    {
                //        FrameworkElement fe = VisualTreeHelper.GetChild(item, 0) as FrameworkElement;
                //        fe.Visibility = Visibility.Visible;
    
                //        //keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Visible;
                //        //keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Visible;
                //    }
                //}
    
            }
    
            private ListBoxItem GetItem(int index)
            {
                if (index < 0 || index >= Items.Count)
                    return null;
                if (ItemsSource != null)
                {
                    var isVirtualizing = VirtualizingStackPanel.GetIsVirtualizing(this);
                    if (!isVirtualizing)
                    {
                        Debug.Assert(ItemsRootPanel != null);
                        return ItemsRootPanel.Children[index] as ListBoxItem;
                    }
                    Debug.Assert(ItemContainerGenerator != null);
                    return ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
                }
                return Items[index] as ListBoxItem;
            }
           
            #region FirstVisibleIndex
            public static readonly DependencyProperty FirstVisibleIndexProperty =
                DependencyProperty.Register("FirstVisibleIndex", typeof(int), typeof(PowerListBox), new PropertyMetadata(0));
    
            public int FirstVisibleIndex
            {
                get { return (int)GetValue(FirstVisibleIndexProperty); }
                private set { SetValue(FirstVisibleIndexProperty, value); }
            }
            #endregion
    
            #region LastVisibleIndex
            public static readonly DependencyProperty LastVisibleIndexProperty =
                DependencyProperty.Register("LastVisibleIndex", typeof(int), typeof(PowerListBox), new PropertyMetadata(0));
    
            public int LastVisibleIndex
            {
                get { return (int)GetValue(LastVisibleIndexProperty); }
                private set { SetValue(LastVisibleIndexProperty, value); }
            }
            #endregion
            private Panel _itemsRootPanel;
            private Panel ItemsRootPanel
            {
                get
                {
                    if (_itemsRootPanel == null && ItemsPresenter != null)
                    {
                        _itemsRootPanel = ItemsPresenter.ChildrenEx().FirstOrDefault() as Panel;
                    }
                    return _itemsRootPanel;
                }
            }
    
            private ItemsPresenter _itemsPresenter;
            private ItemsPresenter ItemsPresenter
            {
                get { return _itemsPresenter ?? (_itemsPresenter = _scrollView.Descendants<ItemsPresenter>().FirstOrDefault()); }
            }
    
        }
    View Code

    其他见demo。

    备注:

    1、虚拟化过程中:注意容器的高度为自己的实际高度

    // 设置它的高度为自己的实际高度
    // 很重要,如果不为父容器指定固定高度,当子元素隐藏后,父容器高度变为0px sp.Height = sp.ActualHeight;

    2、计算虚拟化区域有很多种方法,demo中只是简单粗暴的计算,有很多可以进行优化。

    3、瀑布流排版只是见当实现两行的,需要多行的话请自行改动,详见附加链接,当然林政的瀑布流排版有点问题,改改更健康。

    4、demo2中 Dictionary<double, Border> dic的表只是在加载过程中进行一次初始化,在listbox动态加载过程中并没有修改,为此listbox可将虚拟化计算改为

     void RefreshDisplayArea()
            {
                for (int i = 0; i < ItemsRootPanel.Children.Count; i++)
                {
                    var item = GetItem(i);
                    Border fe = VisualTreeHelper.GetChild(item, 0) as Border;
                    fe.Height = fe.ActualHeight;
    
                    var height = (ItemsRootPanel as WaterFallPanel).GetItemHeight(i);
                    if (((_scrollView.VerticalOffset - fe.Height ) > height || height > (_scrollView.VerticalOffset + 820)))
                    {
                        fe.Child.Visibility = Visibility.Collapsed;
                    }
                    else
                    {
                        fe.Child.Visibility = Visibility.Visible;
                    }
                }
    
            }
    View Code

        其他代码敬请发挥。

    demo链接:

    1:http://files.cnblogs.com/fatlin/VirtualizationListBoxDemo.rar

    2:http://files.cnblogs.com/fatlin/TextListbox.rar

    其他相关链接:

    瀑布流:http://www.cnblogs.com/Smallcode/archive/2012/10/19/2730810.html

  • 相关阅读:
    [转]对Lucene PhraseQuery的slop的理解
    Best jQuery Plugins of 2010
    15 jQuery Plugins To Create A User Friendly Tooltip
    Lucene:基于Java的全文检索引擎简介
    9 Powerful jQuery File Upload Plugins
    Coding Best Practices Using DateTime in the .NET Framework
    Best Image Croppers ready to use for web developers
    28 jQuery Zoom Plugins Creating Stunning Image Effect
    VS2005 + VSS2005 实现团队开发、源代码管理、版本控制(转)
    禁止状态栏显示超链
  • 原文地址:https://www.cnblogs.com/fatlin/p/3612528.html
Copyright © 2011-2022 走看看