我有一个树形控件,性能非常差,我正在尝试追踪问题的来源。
我正在努力确定以下警告是否重要:
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=ContextMenu.IsOpen; DataItem=null; target element is 'MultipleSelectionTreeViewItem' (Name=''); target property is 'NoTarget' (type 'Object')
即使关闭所有这些诊断信息,更新树内容的性能仍然非常糟糕(重建约300个项目需要超过一秒钟),这就是我开始查看跟踪输出的原因。
每次单击我的树视图时,这些警告消息都会大量输出,并且当我将树切换为显示不同内容时,会出现数百个这样的警告。但是,树的内容始终正确显示,因此数据上下文必须是短暂地设置为 null。
我使用了一个显式绑定 DataContext
和值转换器来尝试查看发生了什么。
<HierarchicalDataTemplate x:Key="HierarchyItemTemplate"
DataType="{x:Type local:HierarchyItem}"
ItemsSource="{Binding Children}">
<StackPanel DataContext="{Binding Converter={StaticResource DbgConverter}}" Orientation="Horizontal">
...
</StackPanel>
</HierarchicalDataTemplate>
...但是这个值似乎永远不会等于null。
我可以为所有绑定设置回退值来消除这些警告,但这会在xaml中添加许多不必要的混乱,看起来更像是隐藏问题而不是解决问题(假设它真的是一个问题!)。
所以我的问题是:
- 这些诊断是否可能导致性能问题?
- 如果是,当关闭诊断时提供回退值会对性能造成任何影响吗?
- 如果是,有没有比填充xaml更好的方法?
编辑
使用回退值似乎也不是解决方案,因为它也无法找到资源:
System.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='Img_Folder_Closed_Ex'
它就像是忘记了资源字典中的所有内容,生成所有这些虚假错误,然后再次记住并显示正常。
编辑
好的,我通过注释掉所有绑定并逐个添加它们并解决问题来进一步缩小范围,现在它可以加载并且我可以单击项目并且不会产生任何诊断,直到...当我点击更改树项目的按钮时,它变得疯狂并喷出数百个错误。以下是一小部分错误:
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 41 : BindingExpression path error: 'IsFolder' property not found for 'object' because data item is null. This could happen because the data provider has not produced any data yet. BindingExpression:Path=IsFolder; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 20 :System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=IsFolder; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=IsFolder; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='Img_QA'
System.Windows.Data Information: 41 : BindingExpression path error: 'IsIncluded' property not found for 'object' because data item is null. This could happen because the data provider has not produced any data yet. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=IsFolder; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=IsFolder; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=IsFolder; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 41 : BindingExpression path error: 'IsIncluded' property not found for 'object' because data item is null. This could happen because the data provider has noSystem.Windows.Data Information: 20 : BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
t produced any data yet. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 20 : BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=IsIncluded; DataItem=null; target element is 'TreeViewItemIcon' (Name=''); target property is 'NoTarSystem.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='Img_QA'
System.Windows.Data Information: 41 : BindingExpression path error: 'Name' property not found for 'object' because data item is null. This could happen because the data provider has not produced any data yet. BindingExpression:Path=Name; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Information: 20 : BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=Name; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=Name; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Name; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
get' (type 'Object')
如果我将按钮处理程序更改为仅将ItemsSource设置为空列表,则它会生成相同的大量错误。看起来当源断开连接时,WPF会重新评估所有绑定,并且正如人们所期望的那样,它们都会失败。
编辑
更简单地说,ItemsSource绑定到ObservableCollection。我在ObservableCollection上调用Clear()。所有绑定都被重新评估,无法再找到它们的数据(因为已删除)。最终所有项目都被删除。
为什么这些绑定会被重新评估?有没有办法在不进行额外工作的情况下删除项目?
编辑
我创建了一个展示问题部分的项目。在调用Clear()时,它会生成 complaining that resources can't be found 的错误,但它不会产生dataItem=null的消息。我将继续尝试使用简单示例来重现这些消息。不幸的是,由于防火墙的原因,我无法访问pastebin等网站,因此以下是从标准WPF应用程序更改的代码...
App.xaml:
<Application x:Class="ObservableCollectionTest.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
</Application.Resources>
</Application>
MainWindow.xaml:
<Window x:Class="ObservableCollectionTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:ObservableCollectionTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ObservableCollectionTest;component/Theme.xaml" />
</ResourceDictionary.MergedDictionaries>
<l:Model x:Key="TheModel" />
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.Resources>
<ObjectDataProvider x:Key="TheModelProvider" ObjectInstance="{StaticResource TheModel}" />
<HierarchicalDataTemplate
x:Key="TheModelTemplate"
DataType="{x:Type l:TestItem}"
ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal">
<Image Style="{DynamicResource ImageStyle}" />
<Label>
<TextBlock Style="{DynamicResource TextBlockStyle}" Text="{Binding Name}" />
</Label>
</StackPanel>
</HierarchicalDataTemplate>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TreeView
ItemsSource="{Binding Source={StaticResource TheModelProvider}, Path=Items}"
ItemTemplate="{StaticResource TheModelTemplate}"/>
<Button
Grid.Row="1"
Height="30"
Content="Empty the list"
Click="EmptyTheList_Click" />
</Grid>
</Window>
MainWindow.cs:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ObservableCollectionTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
PresentationTraceSources.DataBindingSource.Listeners.Add(
new ConsoleTraceListener());
PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.All;
InitializeComponent();
}
private void EmptyTheList_Click(object sender, RoutedEventArgs e)
{
(Resources["TheModel"] as Model).Items.Clear();
}
}
}
Model.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
namespace ObservableCollectionTest
{
class Model
{
public ObservableCollection<TestItem> Items { get; set; }
public Model()
{
Items = new ObservableCollection<TestItem>()
{
new TestItem()
{
Name = "TopLevel",
Items = new List<TestItem>()
{
new TestItem() { Name = "Item1", Items = new List<TestItem>() },
new TestItem()
{
Name = "Item2",
Items = new List<TestItem>()
{
new TestItem() { Name = "SubItem1", Items = new List<TestItem>() },
new TestItem() { Name = "SubItem2", Items = new List<TestItem>() },
new TestItem() { Name = "SubItem3", Items = new List<TestItem>() }
}
},
new TestItem() { Name = "Item3", Items = new List<TestItem>() },
new TestItem() { Name = "Item4", Items = new List<TestItem>() }
}
}
};
}
}
class TestItem
{
public string Name { get; set; }
public bool IsRoot { get { return Name == "TopLevel"; } }
public List<TestItem> Items { get; set; }
}
}
Theme.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ObservableCollectionTest;component/Common.xaml" />
</ResourceDictionary.MergedDictionaries>
<BitmapImage x:Key="Img_Folder_Open_In" UriSource="/ObservableCollectionTest;component/VS11_Light_Folder_Open_In.png" />
<BitmapImage x:Key="Img_Folder_Closed_In" UriSource="/ObservableCollectionTest;component/VS11_Light_Folder_Closed_In.png" />
</ResourceDictionary>
Common.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Background" Value="Yellow" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Name}" Value="TopLevel" />
<Condition Binding="{Binding IsExpanded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TreeViewItem}}, FallbackValue=False}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Red" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ImageStyle" TargetType="{x:Type Image}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsRoot}" Value="False" />
<Condition Binding="{Binding IsExpanded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TreeViewItem}}, FallbackValue=False}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="{DynamicResource Img_Folder_Open_In}" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsRoot}" Value="False" />
<Condition Binding="{Binding IsExpanded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TreeViewItem}}, FallbackValue=False}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="{DynamicResource Img_Folder_Closed_In}" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
顺便提一下,我也在使用.NET 3.5(不幸的是我只能用这个版本),但这个问题在.NET 4.0中也出现了。
我的项目中还有两个图片:
(VS11_Light_Folder_Closed_In.png) (VS11_Light_Folder_Open_In.png)
编辑
尝试将ObjectDataProvider
更改为使用DynamicResource:
<ObjectDataProvider x:Key="TheModelProvider" ObjectInstance="{DynamicResource TheModel}" />
但是这会生成以下异常:
ContextMenu.IsOpen
做了什么?Img_Folder_Closed_Ex
是否存在于某个地方? - WiiMaxxDynamicResource
绑定? - JerKimball