瀑布流已经有点年代了吧,不过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 = ;
ColumnHeight = new double[ColumnCount];
childwidth = / 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(, );
//List<DependencyObject> Children = this.GetVisualChildren().ToList();
for (int i = ; i < Children.Count; i++)
{
Children[i].Measure(availableSize);
}
#region 测量值
for (int i = ; i < ColumnHeight.Count(); i++)
{
ColumnHeight[i] = ;
}
heightlist.Clear();
for (int i = ; i < Children.Count; i++)
{
double miniheight = ColumnHeight.Min();
int h = ;
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 == )
{
resultSize.Height = ;
resultSize.Width = ;
}
else
{
resultSize.Width = (Children.Count + ) * Children[].DesiredSize.Width;
} return resultSize;
} protected override Size ArrangeOverride(Size finalSize)
{
for (int i = ; i < ColumnHeight.Count(); i++)
{
ColumnHeight[i] = ;
} #region 排列值
heightlist.Clear();
for (int i = ; i < Children.Count; i++)
{
double miniheight = ColumnHeight.Min();
int h = ;
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 == )
{
if (Children[i].DesiredSize.Width > childwidth)
{
ColumnHeight[h + ] += Children[i].DesiredSize.Height;
}
}
heightlist.Add(ColumnHeight.Min());
}
if (Children.Count < ColumnCount)
finalSize.Width = childwidth * (Children.Count + );
else
finalSize.Width = childwidth * (ColumnCount + ); #endregion return finalSize;
}
private List<double> heightlist = new List<double>();
public double GetItemHeight(int item)
{
if (item >= && item < heightlist.Count)
{
return heightlist[item];
}
else
{
return ;
}
}
}

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;
}
}
});
}

其中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=;
dic.Clear();
for (int i = ; i < ItemsRootPanel.Children.Count; i++)
{
// 因为 ListBox 是通过数据绑定生成的列表,所以需要通过 ItemContainerGenerator 获得
// 每一项的 StackPanel 这个父控件
Border fe = VisualTreeHelper.GetChild(ItemsRootPanel.Children[i], ) 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) + ;
}
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 - ) > keyValue.Key || keyValue.Key > (_scrollView.VerticalOffset + )))
{
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 < || 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()); 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()); 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()); }
} }

其他见demo。

备注:

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

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

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

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

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

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

其他代码敬请发挥。

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

windowsphone 瀑布流&ui虚拟化的更多相关文章

  1. selenium测瀑布流UI页面的Python代码

    from  selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdri ...

  2. wpf 客户端【JDAgent桌面助手】开发详解(三) 瀑布流效果实现与UI虚拟化优化大数据显示

    目录区域: 业余开发的wpf 客户端终于完工了..晒晒截图 wpf 客户端[JDAgent桌面助手]开发详解-开篇 wpf 客户端[JDAgent桌面助手]详解(一)主窗口 圆形菜单... wpf 客 ...

  3. 39 (OC) 瀑布流、不规则UI

    39  (OC)  瀑布流.不规则UI

  4. Android 高级UI设计笔记10:瀑布流控件PinterestLikeAdapterView的使用

    1. 首先我们看看瀑布流的效果,如下: 2. 今天要介绍的瀑布流控件是:PinterestLikeAdapterView 项目地址:https://github.com/GDG-Korea/Pinte ...

  5. Android UI 之WaterFall瀑布流效果

        所谓瀑布流效果,简单说就是宽度相同但是高度不同的一大堆图片,分成几列,然后像水流一样向下排列,并随着用户的上下滑动自动加载更多的图片内容.     语言描述比较抽象,具体效果看下面的截图:   ...

  6. iOS教你轻松打造瀑布流Layout

    前言 : 在写这篇文章之前, 先祝贺自己, 属于我的GitHub终于来了. 这也是我的GitHub的第一份代码, 以下文章的代码均可以在Demo clone或下载. 欢迎大家给予意见. 觉得写得不错的 ...

  7. Masonry与AmazeUI结合实现瀑布流

    做一个图片列表展示,由于照片数量太多,决定用瀑布流来实现 由于之前没有接触过瀑布流,不知从何下手 百度一下大家都在用Masonry 官网 https://masonry.desandro.com/ 这 ...

  8. AJAX异步实现简单的瀑布流

    传统瀑布流布局ul-li,需要先设定显示几列,每列是一个li,需要左浮动并指定宽度,li里面的布局也要先布局好,主要是要定宽,高度自动:然后通过ajax异步,从数据库中得到数据,遍历后将数据插入最矮的 ...

  9. 分享一个Vue实现图片水平瀑布流的插件

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.需求来源 今天碰到了一个需求,需要在页面里,用水平瀑布流的方式,将一些图片进行加载,这让我突然想起我很久以前写的一篇文章<JS两 ...

随机推荐

  1. fs/ext2/inode.c相关函数注释

    用数组chain[4]描述四种不同的索引,即直接索引.一级间接索引.二级间接索引.三级间接索引.举例说明这个结构各个域的含义.如果文件内的块号为8,则不需要间接索引,所以只用chain[0]一个Ind ...

  2. centos复制到另外一台电脑连不上网

    http://snow-berry.iteye.com/blog/1991754 从一台电脑virtual box克隆出来的centos.vdi复制到另外一台电脑,找不到网卡,提示Device eth ...

  3. C++实现日期转换类DateTime

    概述 工作中我们在网络传输时使用time_t来传输时间,在显示时使用字符串来显示,下面是一个日期转换类的实现,方便以后使用: // DateTime.hpp #ifndef _DATETIME_H # ...

  4. android 布局之滑动探究 scrollTo 和 scrollBy 方法使用说明

    涉及到滑动,就涉及到VIEW,大家都知道,Android的UI界面都是由一个一个的View以及View的派生类组成,View作为基类,而常用的布局里面的各种布局就是它派生出来的ViewGroup的子类 ...

  5. ServiceStack.Redis.RedisNativeClient的方法“get_Db”没有实现

    Redis 4.0.0.0版本已经开始收费 Redis 4.0.5.0 已经完成收费 今日在更换Redis版本时 出现了ServiceStack.Redis.RedisNativeClient的方法“ ...

  6. HF Code Designer 代码生成器

    最近忙于项目和平台开发好久没写博客了,今天把最近的一个项目总结和大家分项一下. 在平台开发的过程中,代码生成器是必不可少的,Net领域的代码生成器比较多,以前用动软的,最早的那个版本还是开源,自己写了 ...

  7. api 翻译之AsyncTask

    AsyncTask 类的简介: AsyncTask可以使UI线程更合理更简单的使用.这个类允许执行后台操作,而且可以在不使用多线程或handlers的情况下给主线程传输数据. 异步任务 被定义为在后台 ...

  8. IIS与JIRA的反向代理配置

    JIRA配置修改 JIRA与IIS ARR的集成,除了上篇(Visual SVN IIS反向代理设置)中讲到的基本的ARR配置之外,还需要在JIRA安装目录的conf\server.xml文件中做一个 ...

  9. Oracle基础(五) 权限管理

    一.数据库权限分类 权限是指用户对某个功能是否具有执行的权利.在Oracle中,权限分为系统权限和对象权限. 1.系统权限 系统规定用户使用数据库的权限.例如创建表空间,创建表,删除表的权利.这些都属 ...

  10. 【MongoDB】MongoDB服务器搭建(Unix/Linux)

    1.安装MongboDB安装包(开源免费的哟) 在Mac 下 brew install mongoDB 就可以啦 安装要等一会儿,大概200M 2. cd到MongboDB文件夹 - 如果是用home ...