WPF性能缓慢 - 许多DataItem = null绑定警告

时间:2013-07-05 15:29:39

标签: wpf performance data-binding hierarchicaldatatemplate

我有一个表现非常糟糕的树形控件,我正在尝试跟踪问题的根源。

我正在努力弄清楚以下警告是否重要:

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')

即使关闭所有这些diags,更新树内容的性能也非常糟糕(超过一秒钟重新填充~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中造成很多不必要的混乱,并且似乎隐藏了问题而不是解决它(假设它甚至是一个问题!)

所以我的问题是:

  1. 这些诊断是否可能导致性能问题?
  2. 如果是这样,当关闭诊断时,提供回退值是否会对性能产生任何影响?
  3. 如果是这样,有没有比用crud填充xaml更好的方法?
  4. 修改

    使用回退值看起来无论如何都不是解决方案,因为它也无法找到资源:

    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()时无法找到资源,但它不会产生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>
    

    FWIW,我也使用.NET 3.5(不幸的是我不得不)但是这个问题确实出现在.NET 4.0中。

    我在项目中也有两个图像:

    VS11_Light_Folder_Closed_In.png(VS11_Light_Folder_Closed_In.png) VS11_Light_Folder_Open_In.png(VS11_Light_Folder_Open_In.png)

    修改

    尝试更改ObjectDataProvider以使用DynamicResource:

    <ObjectDataProvider x:Key="TheModelProvider" ObjectInstance="{DynamicResource TheModel}" />
    

    但是这产生了这个例外:

    Exception generated using DynamicResource for model

1 个答案:

答案 0 :(得分:8)

我设法摆脱了所有绑定错误!

我不确定为什么但是将资源添加到Application.Resources而不是使用UserControl.Resources已解决了Resource not found错误。这远非理想的解决方案(将用户控制资源的范围扩展到用户控件会更好),但它可以工作。

通过在各种绑定中提供回退值,可以解析所有其他dataItem=null类型错误。

这解决了启动此问题的性能问题,因此我原来的问题的答案是修复绑定错误确实会对性能产生重大影响。现在我修复了绑定,我的树更新几乎是即时的,而不是接管一秒钟:)

非常感谢您的帮助!

杰里米