WPF TreeView - 键盘导航性能低

时间:2015-06-23 15:29:55

标签: wpf data-binding treeview

我有一个填充运行时的TreeView。它的定义如下:

<TreeView ItemsSource="{Binding Path=RootItem.ChildItems}">

  <TreeView.ItemContainerStyle>
    <Style TargetType="{x:Type TreeViewItem}">
      <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    </Style>
  </TreeView.ItemContainerStyle>

  <TreeView.ItemTemplate>
  ...    
  </TreeView.ItemTemplate>
</TreeView>

我正在使用Style / Setter的部分来使我能够将两个属性IsSelectedIsExpanded绑定到同名的属性(类型bool)我正在使用它们来确定何时以及如何异步加载子项目。

树视图上显示的项目都从相同的基类继承。我绑定的属性如下所示:

public bool IsExpanded
{
  get { return _IsExpanded; }
  set
  {
    if (HasAsynchChildItems)
      if (value)
        LoadChildItems();
      else if (!value)
        Collapse();
    _IsExpanded = value;
  }
}

HasAsynchChildItems是一个属性,用于确定是否应异步加载子项。 LoadChildItems()是装载它们的人。它是异步的。

只要只有少数项目,一切正常。

<击> 在树的某个点上有大约2500多个项目,应用程序开始变得没有响应,大约需要20-30秒才能加载。不仅如此,但在加载后,当我更改选择时,应用程序会挂起10秒以上。

导致应用变慢的所有项目都将HasAsycnchChildItems设置为false。所以LoadChildItems()无法启动。

我通过样式设置器绑定的问题是什么?

PS:我还使用Performance Explorer监控了这个问题,而Visual Studio告诉我,PresentationFramework.ni.dll中90%的处理时间都会缩短。

有什么想法吗?

修改

经过一番调查后,我需要更新问题:

  1. 我发现装载真的很快。这些项目在从数据库中检索后几乎立即显示。

  2. 选择和扩展也几乎是即时的。

  3. 真正停顿的应用程序挂起了什么,通过键盘(向上+向下箭头)在节点中导航。每个大约需要15秒。如果我通过鼠标点击更改选择,则立即执行。

3 个答案:

答案 0 :(得分:1)

我找到了某种解决方法。

正如我在上述评论中所述(因此我再次编辑了我的原始问题),我发现问题既不在于DataBinding,也不在于其他与数据相关的问题。这只是TreeView控件本身的问题,以及它如何处理键盘导航。

如果您通过 UpArrow DownArrow 浏览TreeView,则问题仅显示

PageDown PageUp 鼠标点击不会发生这种情况。

所以这就是我所做的。首先,我在TreeView上启用了虚拟化,仅此一项已经改进了很多东西,但还不令人满意:

<TreeView ItemsSource="{Binding Path=RootItem.Items}" Grid.Row="1"
          VirtualizingStackPanel.IsVirtualizing="True"
          VirtualizingStackPanel.VirtualizationMode="Recycling"

我做的第二件事是编写自己的自定义事件处理程序来处理ArrowUp / ArrowDown通过TreeView的导航。我必须补充说,我完全不喜欢编写这样的代码(这也只是快速测试/实验的结果,所以也许是龙!)。此外:我的TreeView中的所有项都从基类ManagementItem继承,该基类提供IsSelectedIsExpandedIndex(表示项目在父项内的位置&#39;儿童用品)。

private void TreeView_PreviewKeyDown(object sender, KeyEventArgs e)
{
  var tv = sender as TreeView;
  var item = tv.SelectedItem as ManagementItem;

  e.Handled = false;

  if (e.Key == Key.Down)
    try
    {
      if (item.IsExpanded)
        item.Items.First().IsSelected = true;
      else
        if (item.Index == item.Owner.Items.Count - 1)
          item.Owner.Owner.Items[item.Owner.Index + 1].IsSelected = true;
        else
          item.Owner.Items[item.Index + 1].IsSelected = true;
      e.Handled = true;
    } catch {}

  if (e.Key == Key.Up)
    try
    {
      if (item.Index == 0)
        item.Owner.IsSelected = true;
      else
        item.Owner.Items[item.Index - 1].IsSelected = true;
      e.Handled = true;
    } catch {}

 }

这大大提高了导航性能。

答案 1 :(得分:0)

我怀疑所有物品都被召唤了 尝试

public bool IsExpanded
{
  get { return _IsExpanded; }
  set
  {
      if (_IsExpanded == value) return;
      _IsExpanded = value;
      if (HasAsynchChildItems)
      {
          if (_IsExpanded)
            LoadChildItems();
          else 
            Collapse();
      }        
   }
} 

一旦你的LoadChildItems()保存结果,你不必再次加载它们

答案 2 :(得分:0)

我确认了Hemisphera的解决方法。它实际上使树视图导航按预期工作。

我有一个具有2个初始级别的TreeView:第一个5K节点,第二个20K节点(每个父节点约4个子节点)。默认WPF导航从一个节点移动到另一个节点需要3-4秒。这个实现在~100ms

中完成

实现这个时需要考虑的事情:

  1. 为TreeViewItems模型创建基类(在Hemisphera的代码中, ManagementItem 类)。如Hemisphera的代码所示,包括必需的属性(IsSelected,IsExpanded,Index和 Parent Owner )。

  2. 不要忘记在基类中实现INotifyPropertyChanged。此外,您需要在设置IsExpanded和IsSelected时触发事件。这是TwoWayBinding所需要的:

    private bool _isExpanded;
    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            _isExpanded = value;
            if (PropertyChanged != null)
                PropertyChanged(this, IsExpandedEventsArgs);
        }
    }
    
    private bool _isSelected;
    public bool IsSelected 
    { 
        get {return _isSelected;}
        set
        {
            _isSelected = value;
            if (PropertyChanged != null)
                PropertyChanged(this, IsSelectedEventArgs);
        }
    }
    
  3. 在TreeView_PreviewKeyDown方法中需要考虑一些极端情况。即:更高级别的节点没有父级(或所有者)。当从一个更高级别的节点更改为扩展的更低级别节点时,密钥逻辑也需要处理 IsExpanded 属性。