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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17492359/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 09:09:44  来源:igfitidea点击:

WPF slow performance - many DataItem=null binding warnings

wpfperformancedata-bindinghierarchicaldatatemplate

提问by Jeremy Orme

I have a tree control that exhibits very poor performance and I'm trying to track the source of the problem.

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

I am trying to work out whether warnings such as the following are important:

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

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

The performance of updating the tree contents even with all these diags switched off is really dreadful (over a second to repopulate ~300 items), which is what started me looking at the trace output.

即使关闭所有这些诊断,更新树内容的性能也非常糟糕(重新填充约 300 个项目超过一秒钟),这就是我开始查看跟踪输出的原因。

These warnings are spewed out by the dozen for each click in my tree view and when I switch the tree to display different contents, several hundreds of these warnings occur. However, the contents of the tree always appear correctly displayed so the data context must be getting set to null only transiently.

在我的树视图中,每次单击都会发出十几个警告,当我切换树以显示不同的内容时,会出现数百个这样的警告。但是,树的内容始终正确显示,因此必须仅暂时将数据上下文设置为 null。

I put an explicit binding for DataContextwith a value converter to try and see what is going on.

我将一个显式绑定用于DataContext值转换器,以尝试查看发生了什么。

<HierarchicalDataTemplate x:Key="HierarchyItemTemplate"
                          DataType="{x:Type local:HierarchyItem}"
                          ItemsSource="{Binding Children}">
    <StackPanel DataContext="{Binding Converter={StaticResource DbgConverter}}" Orientation="Horizontal">
       ...
    </StackPanel>
</HierarchicalDataTemplate>

... but the value never seems to be equal to null coming into there.

...但值似乎永远不会等于 null 进入那里。

I could set a fallback value for all the bindings to get rid of these warnings but that puts a lot of unnecessary mess in the xaml and seems like it's hiding the problem rather than solving it (assuming it is even a problem!).

我可以为所有绑定设置一个回退值来消除这些警告,但这会给 xaml 带来很多不必要的混乱,似乎它隐藏了问题而不是解决它(假设它甚至是一个问题!)。

So my question is:

所以我的问题是:

  1. Are these diags likely to cause the performance problem?
  2. If so, would supplying fallback values make any difference to performance when diags are turned off?
  3. If so, is there a better way of doing this than filling the xaml with crud?
  1. 这些诊断是否可能导致性能问题?
  2. 如果是这样,当诊断关闭时,提供回退值对性能有什么影响吗?
  3. 如果是这样,有没有比用 crud 填充 xaml 更好的方法呢?

Edit

编辑

Using fallback values looks like it isn't a solution anyway because it's also failing to find resources:

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

System.Windows.ResourceDictionary Warning: 9 : Resource not found; ResourceKey='Img_Folder_Closed_Ex'

It's like it is forgetting everything in the resource dictionary, generating all these spurious errors and then remembering it all again and displaying ok.

这就像忘记资源字典中的所有内容,生成所有这些虚假错误,然后再次记住所有内容并显示正常。

Edit

编辑

Ok, I've narrowed this down a bit further by commenting out all the bindings and putting them back in one by one and resolving problems along the way so now it loads and I can click through the items and no diags are produced until... When I click the button that changes the tree items it goes crazy and spews out hundreds of errors. Here is a small subset of the errors:

好的,我通过注释掉所有绑定并将它们一个一个地放回原处并解决沿途的问题来进一步缩小范围,所以现在它加载了,我可以点击这些项目,直到.. . 当我单击更改树项的按钮时,它会发疯并抛出数百个错误。这是错误的一小部分:

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

If I change the button handler to just set ItemsSource to an empty list then it generates the same huge set of errors. It looks as though when the source is disconnected, WPF re-evaluates all of the bindings and as one would expect they all fail.

如果我将按钮处理程序更改为仅将 ItemsSource 设置为空列表,那么它会生成相同的大量错误。看起来好像当源断开连接时,WPF 会重新评估所有绑定,并且正如人们所期望的那样,它们都失败了。

Edit

编辑

To put it more simply...

更简单的说...

  • ItemsSource is bound to an ObservableCollection.
  • I call Clear() on the ObservableCollection.
  • All the bindings are re-evaluated and can't find their data anymore (because it's been removed)
  • Ultimately all the items are removed
  • ItemsSource 绑定到 ObservableCollection。
  • 我在 ObservableCollection 上调用 Clear()。
  • 所有绑定都被重新评估,再也找不到它们的数据(因为它已被删除)
  • 最终删除所有项目

Why are those bindings being re-evaluated? Is there a way to get it to remove the items without all that extra work?

为什么要重新评估这些绑定?有没有办法让它在没有所有额外工作的情况下删除项目?

Edit

编辑

I have created a project that exhibits part of the problem. It generates errors complaining that resources can't be found when calling Clear() but it doesn't produce the dataItem=null messages. I'm going to keep trying to reproduce those with the simple example. Unfortunately I'm blocked from pastebin and suchlike by the firewall so here is the code that is changed from the standard WPF application...

我创建了一个展示部分问题的项目。它会产生错误,抱怨在调用 Clear() 时找不到资源,但它不会产生 dataItem=null 消息。我将继续尝试用简单的例子重现那些。不幸的是,我被防火墙阻止了 pastebin 之类的东西,所以这里是从标准 WPF 应用程序更改的代码......

App.xaml:

应用程序.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:

主窗口.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:

主窗口.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:

模型.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:

主题.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:

通用.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, I'm also using .NET 3.5 (I have to unfortunately) but this problem did appear with .NET 4.0 also.

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

I also have two images in the project:

我在项目中也有两张图片:

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

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

Edit

编辑

Tried changing the ObjectDataProviderto use DynamicResource:

尝试更改ObjectDataProvider以使用 DynamicResource:

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

But that generated this exception:

但这产生了这个异常:

Exception generated using DynamicResource for model

使用 DynamicResource 为模型生成的异常

采纳答案by Jeremy Orme

I have managed to get rid of all the binding errors!

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

I'm not sure why but adding the resources into Application.Resourcesinstead of using UserControl.Resourceshas solved the Resource not founderrors. This is far from an ideal solution (it would be much nicer to scope user control resources to the user control) but it works.

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

All of the other dataItem=nulltype errors were resolvable by providing fallback values in the various bindings.

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

This has resolved the performance problem that started this question so the answer to my original question is that fixing the binding errors does make a big difference to performance. Now I have fixed the bindings, my tree updates are almost instant instead of taking over a second :)

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

Many thanks for the help!

非常感谢您的帮助!

Jeremy

杰里米