wpf - 虚拟化 WPF Wrap 面板问题

标签 wpf .net-3.5 codeplex

用于 WPF 的虚拟化包装面板的选项并不多。出于某种原因,MS 决定不在标准库中发布一个。

如果有人能如此大胆地为以下 codeplex 项目的第一个工作项提供众包答案(和解释),我将不胜感激:

http://virtualwrappanel.codeplex.com/workitem/1

谢谢!

问题总结:

我最近尝试使用这个项目中的虚拟化包装面板并遇到了一个错误。

重现步骤:

  • 创建列表框。
  • 将虚拟化包装面板设置为列表框面板模板中的项目主机。
  • 将列表框的 itemsource 绑定(bind)到可观察的集合。
  • 从支持的可观察集合中删除一个项目。

  • MeasureOverride 中的 Debug.Assert 失败 (Debug.Assert(child == _children[childIndex], "Wrong child was generated");),并且继续执行会导致 Cleanup 方法中出现空异常 [参见随附的屏幕截图]。

    如果您能够纠正此问题,请告诉我。

    谢谢,

    AO

    代码:

    http://virtualwrappanel.codeplex.com/SourceControl/list/changesets#

    alt text http://virtualwrappanel.codeplex.com/Project/Download/AttachmentDownload.ashx?ProjectName=virtualwrappanel&WorkItemId=1&FileAttachmentId=138959

    最佳答案

    问题解释

    您要求解释发生了什么问题以及如何解决问题的说明。到目前为止,没有人解释这个问题。我会照办的。

    在带有 VirtualizingWrapPanel 的 ListBox 中有五个单独的数据结构来跟踪项目,每个数据结构都以不同的方式:

  • ItemsSource:原始集合(在本例中为 ObservableCollection)
  • CollectionView:保留一个单独的排序/过滤/分组项目列表(仅在使用这些功能中的任何一个时)
  • ItemContainerGenerator:跟踪项目和容器之间的映射
  • InternalChildren:跟踪当前可见的容器
  • WrapPanelAbstraction:跟踪哪些容器出现在哪一行

  • 从 ItemsSource 中删除项目时,必须通过所有数据结构传播此删除。下面是它的工作原理:
  • 您在 ItemsSource
  • 上调用 Remove()
  • ItemsSource 删除该项目并触发其 CollectionChanged 由 CollectionView
  • 处理
  • CollectionView 删除项目(如果正在使用排序/过滤/分组)并触发其 CollectionChanged 由 ItemContainerGenerator
  • 处理
  • ItemContainerGenerator 更新其映射,触发由 VirtualizingPanel
  • 处理的 ItemsChanged
  • VirtualizingPanel 调用由 VirtualizingWrapPanel
  • 实现的虚拟 OnItemsChanged 方法
  • VirtualizingWrapPanel 丢弃了它的 WrapPanelAbstraction,所以它会被构建,但是 它从不更新 InternalChildren

  • 因此,InternalChildren 集合与其他四个集合不同步,从而导致出现错误。

    问题解决方法

    要解决此问题,请在 VirtualizingWrapPanel 的 OnItemsChanged 方法中的任意位置添加以下代码:
    switch(args.Action)
    { 
        case NotifyCollectionChangedAction.Remove: 
        case NotifyCollectionChangedAction.Replace: 
            RemoveInternalChildRange(args.Position.Index, args.ItemUICount); 
            break; 
        case NotifyCollectionChangedAction.Move: 
            RemoveInternalChildRange(args.OldPosition.Index, args.ItemUICount); 
            break; 
    } 
    

    这使 InternalChildren 集合与其他数据结构保持同步。

    为什么这里不调用 AddInternalChild/InsertInternalChild

    您可能想知道为什么上面的代码中没有调用 InsertInternalChild 或 AddInternalChild,尤其是为什么处理 Replace 和 Move 不需要我们在 OnItemsChanged 期间添加新项目。

    理解这一点的关键在于 ItemContainerGenerator 的工作方式。

    当 ItemContainerGenerator 收到一个 remove 事件时,它会立即处理所有事情:
  • ItemContainerGenerator 立即从它自己的数据结构中删除该项目
  • ItemContainerGenerator 触发 ItemChanged 事件。预计专家组将立即移除容器。
  • ItemContainerGenerator 通过删除其 DataContext
  • 来“取消准备”容器

    另一方面,ItemContainerGenerator 获知添加了一个项目,所有内容通常都是延迟的:
  • ItemContainerGenerator 立即在其数据结构中为项目添加一个“槽”,但不创建容器
  • ItemContainerGenerator 触发 ItemChanged 事件。面板调用 InvalidateMeasure() [这是由基类完成的 - 你不必这样做]
  • 稍后调用 MeasureOverride 时,使用 Generator.StartAt/MoveNext 生成项目容器。那时,任何新生成的容器都会添加到 InternalChildren。

  • 因此,InternalChildren 集合中的所有删除(包括属于 Move 或 Replace 的部分)必须在 OnItemsChanged 内完成,但添加可以(并且应该)推迟到下一个 MeasureOverride。

    关于wpf - 虚拟化 WPF Wrap 面板问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3356172/

    相关文章:

    c# - 全局数据真的那么糟糕吗?

    jquery - 在后面的代码中将图像添加到div

    tfs - 搁置 Team Foundation 服务器中的待定更改

    c# - 如何将 WPF DataGridTextColum 文本最大长度限制为 10 个字符

    c# - WPF Fonts.GetFontFamilies()缓存字体列表,如何清除缓存?

    c# - 此代码段中是否存在无法访问的代码?我不这么认为,但 Resharper 告诉我的不是这样

    c# - 如何从强制终止管道中正确清除异步回调?

    open-source - 选择 Google Code vs. SourceForge vs. Codeplex 有什么固有的好处或缺点吗?

    git - git svn 的作者文件格式是什么,特别是反斜杠或下划线等特殊字符?

    wpf - 如何使用 MVVM 将 ViewModel 中的元数据应用于 WPF 中的 View