我有一个填充运行时的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的部分来使我能够将两个属性IsSelected
和IsExpanded
绑定到同名的属性(类型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%的处理时间都会缩短。
有什么想法吗?
修改
经过一番调查后,我需要更新问题:
我发现装载真的很快。这些项目在从数据库中检索后几乎立即显示。
选择和扩展也几乎是即时的。
真正停顿的应用程序挂起了什么,通过键盘(向上+向下箭头)在节点中导航。每个大约需要15秒。如果我通过鼠标点击更改选择,则立即执行。
答案 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
继承,该基类提供IsSelected
,IsExpanded
,Index
(表示项目在父项内的位置&#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
中完成实现这个时需要考虑的事情:
为TreeViewItems模型创建基类(在Hemisphera的代码中, ManagementItem 类)。如Hemisphera的代码所示,包括必需的属性(IsSelected,IsExpanded,Index和 Parent 或 Owner )。
不要忘记在基类中实现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);
}
}
在TreeView_PreviewKeyDown方法中需要考虑一些极端情况。即:更高级别的节点没有父级(或所有者)。当从一个更高级别的节点更改为扩展的更低级别节点时,密钥逻辑也需要处理 IsExpanded 属性。