【问题标题】:WPF slow performance - many DataItem=null binding warningsWPF 性能缓慢 - 许多 DataItem=null 绑定警告
【发布时间】:2013-07-05 15:29:38
【问题描述】:

我有一个性能很差的树控件,我正在尝试跟踪问题的根源。

我正在尝试确定以下警告是否重要:

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 中造成很多不必要的混乱,并且似乎它隐藏了问题而不是解决它(假设它甚至是一个问题!) .

所以我的问题是:

  1. 这些诊断是否可能导致性能问题?
  2. 如果是这样,当诊断关闭时,提供后备值是否会对性能产生任何影响?
  3. 如果是这样,有没有比用 crud 填充 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() 时找不到资源,但它不会产生 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();
        }
    }
}

模型.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_Open_In.png)

编辑

尝试将 ObjectDataProvider 更改为使用 DynamicResource:

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

但这产生了这个异常:

【问题讨论】:

  • 您对ContextMenu.IsOpen 做了什么? Img_Folder_Closed_Ex 是否存在于某处?
  • 资源都存在并且都被找到了(最终)。问题在于项目正在发生变化,而 WPF 会毫无意义地重新评估数据已消失的绑定。
  • 如果您调用 clear,您的 Datatemplates 应该会消失并且不会因为no Hierarchi == no HierarchicalDataTemplate 而发疯。看起来您需要提供更多代码,或者如果行为相同,您还可以构建一个简单版本,然后发布或链接简单示例的完整代码
  • 只是在黑暗中拍摄,但由于您将底层模型绑定为StaticResource,因此该绑定将在资源的整个生命周期内“生成”一次 - 您是否尝试过交换改用DynamicResource 绑定?
  • 更改为 DynamicResource 导致异常(见上文)。我尝试绑定到 ObjectDataProvider 以及绑定到 TheModel,它们都产生了相同的异常。

标签: wpf performance data-binding hierarchicaldatatemplate


【解决方案1】:

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

我不知道为什么,但是将资源添加到 Application.Resources 而不是使用 UserControl.Resources 解决了 Resource not found 错误。这远不是一个理想的解决方案(将用户控件资源范围限定为用户控件会更好),但它确实有效。

所有其他 dataItem=null 类型错误都可以通过在各种绑定中提供后备值来解决。

这已经解决了引发这个问题的性能问题,所以我最初的问题的答案是修复绑定错误确实会对性能产生很大影响。现在我已经修复了绑定,我的树更新几乎是即时的,而不是占用一秒钟 :)

非常感谢您的帮助!

杰里米

【讨论】:

    猜你喜欢
    • 2023-04-06
    • 2012-08-16
    • 1970-01-01
    • 2014-07-29
    • 2015-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多