/// <summary>
/// 间接实现了虚拟化的ListBox
/// 子项必须实现IVisible接口
/// 你可以在IsVisible发生改变时实现一系列自定义动作
/// 比如:当IsVisible = false时,清空子项的内容;当IsVisible = true时,还原子项的内容
/// </summary>
public class VirtualizedListBox : ListBox
{
private ScrollViewer scrollViewer; public override void OnApplyTemplate()
{
scrollViewer = FindVisualChild<ScrollViewer>(this);
if (scrollViewer != null)
{
scrollViewer.ScrollChanged -= OnScrollChanged;
scrollViewer.ScrollChanged += OnScrollChanged;
}
} #region 事件
/// <summary>
/// 滚动条滚动事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
foreach (IVisible item in this.Items)
{
var listBoxItem = (FrameworkElement)this.ItemContainerGenerator.ContainerFromItem(item);
item.IsVisible = IsChildVisibleInParent(listBoxItem, scrollViewer);
}
}
#endregion #region 私有方法
/// <summary>
/// 判断子控件是否在父控件中可见
/// </summary>
/// <param name="child">子控件</param>
/// <param name="parent">父控件</param>
/// <returns></returns>
private bool IsChildVisibleInParent(FrameworkElement child, FrameworkElement parent)
{
var childTransform = child.TransformToAncestor(parent);
var childRectangle = childTransform.TransformBounds(new Rect(new Point(, ), child.RenderSize));
var ownerRectangle = new Rect(new Point(, ), parent.RenderSize);
return ownerRectangle.IntersectsWith(childRectangle);
} public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
if (obj != null)
{
for (int i = ; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null) return childItem;
}
}
return null;
}
#endregion
}

VirtualizedListBox

    /// <summary>
/// 表示可见的类型
/// </summary>
public interface IVisible
{
/// <summary>
/// 是否可见
/// </summary>
bool IsVisible
{
get;
set;
}
}

IVisible

核心代码是IsChildVisibleInParent方法,可以判断某个子控件是否在父控件中可见。

针对ListBox,需要判断某个ListBoxItem是否在ListBox的ScrollView中可见。然后根据子项是否可见,再对子项进行处理,实现间接的虚拟化。

于是,需要获取ListBox的模板中的ScrollViewer。于是,祭出神器:

public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
if (obj != null)
{
for (int i = ; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null) return childItem;
}
}
return null;
}

FindVisualChild

这个方法,我觉得了解WPF的应该都知道。我甚至在学习WPF的第一天就见到了这个方法。

一开始,我在VirtualizedListBox的构造函数中调用此方法,发现获取到的ScrollViewer是空的。

我想,应该这时候ListBox还没开始加载。

然后,我在ListBox的Loaded事件中调用此方法,发现获取到的ScrollViewer还是空的。

什么鬼?感觉有点颠覆三观。

最后,我心灰意冷,把键盘砸了。砸键盘的过程中,代码成了。

嘿嘿,不知道大家是否知道在什么情况下,ListBox即使已经加载完成(Loaded),却依然无法获取到ListBox控件模板中的ScrollViewer呢?

WPF:间接支持虚拟化的ListBox的更多相关文章

  1. WPF的UI虚拟化

    许多时候,我们的界面上会呈现大量的数据,如包含数千条记录的表格或包含数百张照片的相册.由于呈现UI是一件开销比较大的动作,一次性呈现数百张照片就目前的电脑性能来说是需要占用大量内存和时间的.因此需要对 ...

  2. 【WPF】UI虚拟化之------自定义VirtualizingWrapPanel

    原文:[WPF]UI虚拟化之------自定义VirtualizingWrapPanel 前言 前几天QA报了一个关于OOM的bug,在排查的过程中发现,ListBox控件中被塞入了过多的Item,而 ...

  3. WPF 显示文件列表中使用 ListBox 变到ListView 最后使用DataGrid

    WPF 显示文件列表中使用 ListBox 变到ListView 最后使用DataGrid 故事背景: 需要检索某目录下文件,并列出来,提供选择和其他功能. 第一版需求: 列出文件供选择即可,代码如下 ...

  4. 查看CPU是否支持虚拟化

    参考:http://www.cnblogs.com/jankie/archive/2012/07/04/2575695.html 一.Windows平台:使用cpu-Z即可查看. 二.Linux平台: ...

  5. 问题 Windows7VMware14安装虚拟机时出现 此主机不支持虚拟化实际模式。需要具备 Intel“VMX 不受限客户机”功能才能在 Intel 处理器上运行此虚拟机。 模块“CPUIDEarly”启动失败。

    问题 Windows7VMware14安装虚拟机时出现 此主机不支持虚拟化实际模式.需要具备 Intel“VMX 不受限客户机”功能才能在 Intel 处理器上运行此虚拟机. 模块“CPUIDEarl ...

  6. CentOS 7下KVM支持虚拟化/嵌套虚拟化配置

    开启虚拟化: cat << EOF > /etc/modprobe.d/kvm-nested.conf options kvm-intel nested=1 options kvm- ...

  7. VMware中让虚拟机支持虚拟化

    一.问题 由于需要玩一下OpenNebula,但是现在自己只有一台笔记本,如何玩?当然是VMVare了,于是装了几台Ubuntu的虚拟机,但是在看安装OpenNebula的前提要求是 安装的主机cpu ...

  8. 怎样知道 CPU 是否支持虚拟化技术(VT) | Linux 中国

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/F8qG7f9YD02Pe/article/details/79832475 wx_fmt=png&a ...

  9. 英特尔老款CPU支持虚拟化对照表(转)

    说明:一般来说新款的挤牙膏公司出的CPU都基本支持虚拟化,但不包括Atom系列的,也就是小主机低功耗机器使用的CPU. Intel® Virtualization Technology List YE ...

随机推荐

  1. [Swift]LeetCode228. 汇总区间 | Summary Ranges

    Given a sorted integer array without duplicates, return the summary of its ranges. Example 1: Input: ...

  2. [Swift]LeetCode236. 二叉树的最近公共祖先 | Lowest Common Ancestor of a Binary Tree

    Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According ...

  3. [Swift]LeetCode481. 神奇字符串 | Magical String

    A magical string S consists of only '1' and '2' and obeys the following rules: The string S is magic ...

  4. 巡风源码阅读与分析--querylogic函数

    文件位置:views/lib/QueryLogic.py Querylogic() # 搜索逻辑 def querylogic(list): query = {} if len(list) > ...

  5. javascript 使用小技巧总结

    按位取反 ~a 即:返回 -(a+1),会去掉小数点. let a = 3.14; let b = ~a; //b = -(3.14+1) 取整 为-4: let c = ~b; //c = -(-4 ...

  6. 侯哥的Python分享

    侯哥语录 我曾经是一个职业教育者,现在是一个自由开发者.我希望我的分享可以和更多人一起进步.分享一段我喜欢的话给大家:"我所理解的自由不是想干什么就干什么,而是想不干什么就不干什么.当你还没 ...

  7. .net core在Ocelot网关中统一配置Swagger

    最近在做微服务的时候,由于我们是采用前后端分离来开发的,提供给前端的直接是Swagger,如果Swagger分布在各个API中,前端查看Swagger的时候非常不便,因此,我们试着将Swagger集中 ...

  8. SignalR学习笔记(二)高并发应用

    虽然SignalR借助Websocket提供了很强大的实时通讯能力,但是在有些实时通讯非常频繁的场景之下,如果使用不当,还是会导致服务器,甚至客户端浏览器崩溃. 以下是一个实时拖拽方块项目的优化过程 ...

  9. mybatis中resultMap配置细则

    resultMap算是mybatis映射器中最复杂的一个节点了,能够配置的属性较多,我们在mybatis映射器配置细则这篇博客中已经简单介绍过resultMap的配置了,当时我们介绍了resultMa ...

  10. LeetCode专题-Python实现之第21题:Merge Two Sorted Lists

    导航页-LeetCode专题-Python实现 相关代码已经上传到github:https://github.com/exploitht/leetcode-python 文中代码为了不动官网提供的初始 ...