WPF中ItemsControl应用虚拟化时找到子元素的方法

简介: 原文:WPF中ItemsControl应用虚拟化时找到子元素的方法  wpf的虚拟化技术会使UI的控件只初始化看的到的子元素, 而不是所有子元素都被初始化,这样会提高UI性能。
原文: WPF中ItemsControl应用虚拟化时找到子元素的方法

 wpf的虚拟化技术会使UI的控件只初始化看的到的子元素, 而不是所有子元素都被初始化,这样会提高UI性能。

但是我们经常会遇到一个问题:
应用虚拟化后看不见的子元素因为没有实际产生导致ItemContainerGenerator的查找元素方法( ContainerFromIndex /  ContainerFromItem)失效。


解决办法1:
(1)监听ItemsControl的ItemContainerGenerator的StatusChanged事件, 当GeneratorStatus为ContainerGenerated时再进行查找,
(2)遍历ItemsControl的Items,获取当前需要查找元素的Index,
(3)利用反射调用VirtualizingPanel的BringIndexIntoView,或者直接调用BringIntoView,然后强制滚动以产生Item(具体可以参考TreeViewItem的ExpandRecursicve的内部实现)。

需要注意的是:
(1)ItemContainerGenerator的StatuChanged事件会多次被触发, 原因是每次初始化的Item数量就是当前空间所能看到的数量,StatusChanged触发的次数就是总共Items除以每次初始的Item数量。
(2)调用BringIndexInToView不正确会导致InvalidOperationException,具体为“Cannot call StartAt when content generation is in progress.”  或者 ”无法在正在进行内容生成时调用StartAt。”。 可以用Dispatcher.BeginInvoke来解决, 如下面代码。
(3)当然ItemsControl中的虚拟化模式设置为Recycling, 即 VirtualizingStackPanel.VirtualizationMode ="Recycling"时,后端存储的子元素选中项会在ItermContainerGenerator重新产生子项时变为DisconnectedItem。可以把模式设置为Standard解决。


具体代码如下:
  • 1. 查找入口 

         private  ItemsControl  _currentSelectedItem  =  null ;

         private  void  BtnFind_Click (  object  sender  ,  System .  Windows . RoutedEventArgs  e )
        {
             if  ( string  . IsNullOrEmpty (  txtContent . Text  ))
            {
                 return ;
            }

             if  ( _currentSelectedItem  ==  null )
            {
                 _currentSelectedItem  =  _treeView  ;
            }
             else
            {
                 if  ( _currentSelectedItem  is  TreeViewItem )
                {
                    (  _currentSelectedItem  as  TreeViewItem ).  IsExpanded  =  true  ;
                }
            }

             if  ( _currentSelectedItem  . ItemContainerGenerator .  Status  !=  GeneratorStatus  . ContainersGenerated )
            {
                 _currentSelectedItem . ItemContainerGenerator  . StatusChanged  -=  new  EventHandler ( ItemContainerGenerator_StatusChanged  );
                 _currentSelectedItem . ItemContainerGenerator  . StatusChanged  +=  new  EventHandler ( ItemContainerGenerator_StatusChanged  );
            }
             else
            {
                 treeViewItem_BringIntoView ( txtContent  . Text );
            }
        }

  • 2.StatusChanged事件的处理
        void  ItemContainerGenerator_StatusChanged  ( object  sender ,  EventArgs  e )
        {
             var  generator  =  sender  as  ItemContainerGenerator  ;
             if  ( null  ==  generator )
            {
                 return ;
            }

             //once the children have been generated, expand those children's children then remove the event handler
             if  ( generator  . Status  ==  GeneratorStatus . ContainersGenerated  &&  _currentSelectedItem  . ItemContainerGenerator .  Status  ==  GeneratorStatus  . ContainersGenerated )
            {
                 treeViewItem_BringIntoView ( txtContent  . Text );
            }
        }

  • 3.具体虚拟化时的强制产生子元素及查找处理
  private  void  treeViewItem_BringIntoView( string  findItem)
        {
            System.Diagnostics.  Debug .WriteLine( "enter treeViewItem_BringIntoview"  );

             try
            {
                _currentSelectedItem.ApplyTemplate();
                 ItemsPresenter  itemsPresenter = ( ItemsPresenter )_currentSelectedItem.Template.FindName( "ItemsHost" , ( FrameworkElement )_currentSelectedItem);
                 if  (itemsPresenter !=  null  )
                    itemsPresenter.ApplyTemplate();
                 else
                    _currentSelectedItem.UpdateLayout();
                 VirtualizingPanel  virtualizingPanel = _currentSelectedItem.GetItemsHost()  as  VirtualizingPanel ;
                virtualizingPanel.CallEnsureGenerator();

                 int  selectedIndex = -1;
                 int  count1 = _currentSelectedItem.Items.Count;
                 for  ( int  i = 0; i < count1; i++)
                {
                     ItemsItem1  tviItem = _currentSelectedItem.Items.GetItemAt(i)  as  ItemsItem1 ;

                     if  ( null  != tviItem && tviItem.Label.Equals(findItem))
                    {
                        selectedIndex = i;

                         break ;
                    }
                }

                 if  (selectedIndex < 0)
                {
                     return ;
                }

                 Action  action = () =>
                {
                     TreeViewItem  itemSelected =  null  ;

                     //Force to generate every treeView item by using scroll item
                     if  (virtualizingPanel !=  null  )
                    {
                         try
                        {
                            virtualizingPanel.CallBringIndexIntoView(selectedIndex);
                        }
                         catch  (System. Exception  ex)
                        {
                            System.Diagnostics.  Debug .WriteLine( "CallBringIndexIntoView exception : "  + ex.Message);
                        }

                        itemSelected = ( TreeViewItem )_currentSelectedItem.ItemContainerGenerator.ContainerFromIndex(selectedIndex);
                    }
                     else
                    {
                        itemSelected = ( TreeViewItem )_currentSelectedItem.ItemContainerGenerator.ContainerFromIndex(selectedIndex);
                        itemSelected.BringIntoView();
                    }

                     if  ( null  != itemSelected)
                    {
                        _currentSelectedItem = itemSelected;
                        (_currentSelectedItem  as  TreeViewItem  ).IsSelected =  true ;
                        _currentSelectedItem.BringIntoView();
                    }
                };

                Dispatcher.BeginInvoke(  DispatcherPriority .Background, action);
            }
             catch  (System. Exception  ex)
            {
                 //
            }
        }

  • 4.xaml代码

< Window
     xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns : x  ="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns : d  ="http://schemas.microsoft.com/expression/blend/2008"
     xmlns : mc  ="http://schemas.openxmlformats.org/markup-compatibility/2006"
     mc : Ignorable  ="d"
     x : Class  ="WpfApplication1.MainWindow"
     x : Name  ="Window"
     Title ="MainWindow"
     Width ="640"
     Height ="480" >
     < Window.Resources >
         < HierarchicalDataTemplate
             x : Key  ="ItemsItem1Template"
             ItemsSource ="{ Binding  Items }" >
             < StackPanel >
                 < TextBlock
                     Text ="{ Binding  Label }" />
             </ StackPanel >
         </ HierarchicalDataTemplate >
     </ Window.Resources >

     < Grid
         x : Name  ="LayoutRoot" >
         < TreeView
             x : Name  ="_treeView"
             HorizontalAlignment ="Left"
             Width ="287"
             d : DataContext  ="{ Binding }"
             ItemsSource ="{ Binding  Items ,  Source  ={ StaticResource  SampleDataSource  }}"
             ItemTemplate ="{ DynamicResource  ItemsItem1Template }"
             VirtualizingStackPanel.IsVirtualizing  ="True"
              VirtualizingStackPanel.VirtualizationMode  ="Standard"
              />
         < Button
             x : Name  ="btnFind"
             Content ="Find"
             HorizontalAlignment ="Right"
             Margin ="0,8,8,0"
             VerticalAlignment ="Top"
             Width ="75"
             Click ="BtnFind_Click" />
         < TextBox
             x : Name  ="txtContent"
             Margin ="291,8,87,0"
             TextWrapping ="Wrap"
             VerticalAlignment ="Top"
             Height ="21.837" />
     </ Grid >
</ Window >

  • 5.反射系统控件私有方法的类

public  static  class  WPFUIElementExtension
    {
        #region  Functions to get internal members using reflection

         // Some functionality we need is hidden in internal members, so we use reflection to get them

        #region  ItemsControl .ItemsHost

         static  readonly  PropertyInfo  ItemsHostPropertyInfo  =  typeof  ( ItemsControl ).  GetProperty ( "ItemsHost"  ,  BindingFlags . Instance  |  BindingFlags .  NonPublic );

         public  static  Panel  GetItemsHost ( this  ItemsControl  itemsControl )
        {
             Debug . Assert  ( itemsControl  !=  null );
             return  ItemsHostPropertyInfo  . GetValue (  itemsControl ,  null  )  as  Panel ;
        }

        #endregion  ItemsControl .ItemsHost

        #region  Panel .EnsureGenerator

         private  static  readonly  MethodInfo  EnsureGeneratorMethodInfo  =  typeof ( Panel  ). GetMethod (  "EnsureGenerator" ,  BindingFlags  . Instance  |  BindingFlags . NonPublic  );

         public  static  void  CallEnsureGenerator ( this  Panel  panel )
        {
             Debug . Assert  ( panel  !=  null );
             EnsureGeneratorMethodInfo . Invoke  ( panel ,  null );
        }

        #endregion  Panel .EnsureGenerator

        #region  VirtualizingPanel .  BringIndexIntoView

         private  static  readonly  MethodInfo  BringIndexIntoViewMethodInfo  =  typeof ( VirtualizingPanel  ). GetMethod (  "BringIndexIntoView" ,  BindingFlags  . Instance  |  BindingFlags . NonPublic  );

         public  static  void  CallBringIndexIntoView ( this  VirtualizingPanel  virtualizingPanel ,  int  index )
        {
             Debug . Assert  ( virtualizingPanel  !=  null );
             BringIndexIntoViewMethodInfo . Invoke  ( virtualizingPanel ,  new  object  [] {  index  });
        }

        #endregion  VirtualizingPanel .  BringIndexIntoView

        #endregion  Functions to get internal members using reflection
    }


解决方法2:
(1)参考方法1的第一步解决方法
(2)遍历ItemsControl的Items, 根据ContainerFromIndex去找到当前可见的元素的index。
(3)利用BringInoView去滚动现有的Item以便UI产生后续的子元素, 然后循环直到找见要查找的子元素。(遍历分为2部分,向前遍历和向后遍历)

注意事项:
(1)参考方法1的第一注意事项
(2)因为比方法1多了一次循环遍历,当items很多时有点卡顿,不过还在可以忍受的范围。

具体代码:
1.参考方法1的代码,具体只有强制生成子元素的方法有区别, 即与方法1中的步骤3有区别。
2.如下:

   private   void  treeViewItem_BringIntoView2( string  findItem)
        {
            System.Diagnostics.  Debug  .WriteLine( "enter treeViewItem_BringIntoview"  );

             try
            {
                _currentSelectedItem.ApplyTemplate();
                 ItemsPresenter  itemsPresenter = ( ItemsPresenter  )_currentSelectedItem.Template.FindName(  "ItemsHost" , ( FrameworkElement  )_currentSelectedItem);
                 if  (itemsPresenter !=  null  )
                    itemsPresenter.ApplyTemplate();
                 else
                    _currentSelectedItem.UpdateLayout();
                 VirtualizingPanel  virtualizingPanel = _currentSelectedItem.GetItemsHost()  as   VirtualizingPanel  ;
                virtualizingPanel.CallEnsureGenerator();

                 TreeViewItem  itemTemp =  null  ;
                 ItemsItem1  objTemp =  null  ;
                 int  visiableIndex = -1;
                 int  findIndex = -1;
                 int  count1 = _currentSelectedItem.Items.Count;
                 for  ( int  i = 0; i < count1; i++)
                {
                    itemTemp = (  TreeViewItem  )_currentSelectedItem.ItemContainerGenerator.ContainerFromIndex(i);
                     if  ( null  != itemTemp)
                    {
                        visiableIndex = i;
                    }

                    objTemp = _currentSelectedItem.Items.GetItemAt(i)  as   ItemsItem1  ;
                     if  ( null  != objTemp && objTemp.Label.Equals(findItem))
                    {
                        findIndex = i;
                    }
                }

                 if  (findIndex == -1 || visiableIndex == -1)
                {
                     return  ;
                }

                 if  (findIndex < visiableIndex)
                {
                     for  ( int  j = visiableIndex; j >= findIndex; j--)
                    {
                        itemTemp = ( TreeViewItem  )_currentSelectedItem.ItemContainerGenerator.ContainerFromIndex(j);
                         if  ( null  != itemTemp)
                        {
                            itemTemp.BringIntoView();
                        }
                    }
                }
                 else   if  (findIndex > visiableIndex)
                {
                     for  ( int  j = visiableIndex; j <= findIndex; j++)
                    {
                        itemTemp = ( TreeViewItem  )_currentSelectedItem.ItemContainerGenerator.ContainerFromIndex(j);
                         if  ( null  != itemTemp)
                        {
                            itemTemp.BringIntoView();
                        }
                    }
                }
                 else
                {
                    itemTemp = ( TreeViewItem  )_currentSelectedItem.ItemContainerGenerator.ContainerFromIndex(visiableIndex);
                     if  ( null  != itemTemp)
                    {
                        itemTemp.BringIntoView();
                    }
                }

                 if  ( null  != itemTemp)
                {
                    _currentSelectedItem = itemTemp;
                    (_currentSelectedItem  as   TreeViewItem  ).IsSelected =  true ;
                    _currentSelectedItem.BringIntoView();
                }

            }
             catch  (System. Exception  ex)
            {
                 //
            }
        }


目录
相关文章
|
2月前
|
安全 C# 数据安全/隐私保护
WPF安全加固全攻略:从数据绑定到网络通信,多维度防范让你的应用固若金汤,抵御各类攻击
【8月更文挑战第31天】安全性是WPF应用程序开发中不可或缺的一部分。本文从技术角度探讨了WPF应用面临的多种安全威胁及防护措施。通过严格验证绑定数据、限制资源加载来源、实施基于角色的权限管理和使用加密技术保障网络通信安全,可有效提升应用安全性,增强用户信任。例如,使用HTML编码防止XSS攻击、检查资源签名确保其可信度、定义安全策略限制文件访问权限,以及采用HTTPS和加密算法保护数据传输。这些措施有助于全面保障WPF应用的安全性。
41 0
|
2月前
|
C# 开发者 Windows
全面指南:WPF无障碍设计从入门到精通——让每一个用户都能无障碍地享受你的应用,从自动化属性到焦点导航的最佳实践
【8月更文挑战第31天】为了确保Windows Presentation Foundation (WPF) 应用程序对所有用户都具备无障碍性,开发者需关注无障碍设计原则。这不仅是法律要求,更是社会责任,旨在让技术更人性化,惠及包括视障、听障及行动受限等用户群体。
51 0
|
2月前
|
C# UED 开发者
WPF与性能优化:掌握这些核心技巧,让你的应用从卡顿到丝滑,彻底告别延迟,实现响应速度质的飞跃——从布局到动画全面剖析与实例演示
【8月更文挑战第31天】本文通过对比优化前后的方法,详细探讨了提升WPF应用响应速度的策略。文章首先分析了常见的性能瓶颈,如复杂的XAML布局、耗时的事件处理、不当的数据绑定及繁重的动画效果。接着,通过具体示例展示了如何简化XAML结构、使用后台线程处理事件、调整数据绑定设置以及利用DirectX优化动画,从而有效提升应用性能。通过这些优化措施,WPF应用将更加流畅,用户体验也将得到显著改善。
71 0
|
2月前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
58 0
|
2月前
|
容器 C# Docker
WPF与容器技术的碰撞:手把手教你Docker化WPF应用,实现跨环境一致性的开发与部署
【8月更文挑战第31天】容器技术简化了软件开发、测试和部署流程,尤其对Windows Presentation Foundation(WPF)应用程序而言,利用Docker能显著提升其可移植性和可维护性。本文通过具体示例代码,详细介绍了如何将WPF应用Docker化的过程,包括创建Dockerfile及构建和运行Docker镜像的步骤。借助容器技术,WPF应用能在任何支持Docker的环境下一致运行,极大地提升了开发效率和部署灵活性。
48 0
|
2月前
|
存储 C# 关系型数据库
“云端融合:WPF应用无缝对接Azure与AWS——从Blob存储到RDS数据库,全面解析跨平台云服务集成的最佳实践”
【8月更文挑战第31天】本文探讨了如何将Windows Presentation Foundation(WPF)应用与Microsoft Azure和Amazon Web Services(AWS)两大主流云平台无缝集成。通过具体示例代码展示了如何利用Azure Blob Storage存储非结构化数据、Azure Cosmos DB进行分布式数据库操作;同时介绍了如何借助Amazon S3实现大规模数据存储及通过Amazon RDS简化数据库管理。这不仅提升了WPF应用的可扩展性和可用性,还降低了基础设施成本。
56 0
|
2月前
|
vr&ar C# 图形学
WPF与AR/VR的激情碰撞:解锁Windows Presentation Foundation应用新维度,探索增强现实与虚拟现实技术在现代UI设计中的无限可能与实战应用详解
【8月更文挑战第31天】增强现实(AR)与虚拟现实(VR)技术正迅速改变生活和工作方式,在游戏、教育及工业等领域展现出广泛应用前景。本文探讨如何在Windows Presentation Foundation(WPF)环境中实现AR/VR功能,通过具体示例代码展示整合过程。尽管WPF本身不直接支持AR/VR,但借助第三方库如Unity、Vuforia或OpenVR,可实现沉浸式体验。例如,通过Unity和Vuforia在WPF中创建AR应用,或利用OpenVR在WPF中集成VR功能,从而提升用户体验并拓展应用功能边界。
36 0
|
2月前
|
区块链 C# 存储
链动未来:WPF与区块链的创新融合——从智能合约到去中心化应用,全方位解析开发安全可靠DApp的最佳路径
【8月更文挑战第31天】本文以问答形式详细介绍了区块链技术的特点及其在Windows Presentation Foundation(WPF)中的集成方法。通过示例代码展示了如何选择合适的区块链平台、创建智能合约,并在WPF应用中与其交互,实现安全可靠的消息存储和检索功能。希望这能为WPF开发者提供区块链技术应用的参考与灵感。
46 0
|
2月前
|
C# 机器学习/深度学习 搜索推荐
WPF与机器学习的完美邂逅:手把手教你打造一个具有智能推荐功能的现代桌面应用——从理论到实践的全方位指南,让你的应用瞬间变得高大上且智能无比
【8月更文挑战第31天】本文详细介绍如何在Windows Presentation Foundation(WPF)应用中集成机器学习功能,以开发具备智能化特性的桌面应用。通过使用Microsoft的ML.NET框架,本文演示了从安装NuGet包、准备数据集、训练推荐系统模型到最终将模型集成到WPF应用中的全过程。具体示例代码展示了如何基于用户行为数据训练模型,并实现实时推荐功能。这为WPF开发者提供了宝贵的实践指导。
29 0
|
2月前
|
开发者 C# UED
WPF与多媒体:解锁音频视频播放新姿势——从界面设计到代码实践,全方位教你如何在WPF应用中集成流畅的多媒体功能
【8月更文挑战第31天】本文以随笔形式介绍了如何在WPF应用中集成音频和视频播放功能。通过使用MediaElement控件,开发者能轻松创建多媒体应用程序。文章详细展示了从创建WPF项目到设计UI及实现媒体控制逻辑的过程,并提供了完整的示例代码。此外,还介绍了如何添加进度条等额外功能以增强用户体验。希望本文能为WPF开发者提供实用的技术指导与灵感。
74 0