Подтвердить что ты не робот

Высокая производительность WPF - многие DataItem = null обязательные предупреждения

У меня есть элемент управления дерева, который демонстрирует очень низкую производительность, и я пытаюсь отслеживать источник проблемы.

Я пытаюсь выяснить, важны ли предупреждения, такие как следующее:

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>

... но значение никогда не кажется равным нулю, входящему в него.

Я мог бы установить резервное значение для всех привязок, чтобы избавиться от этих предупреждений, но это ставит много лишнего беспорядка в xaml и кажется, что он скрывает проблему, а не решает ее (при условии, что это даже проблема!).

Итак, мой вопрос:

  • Возможно, эти проблемы могут вызвать проблемы с производительностью?
  • Если да, то обеспечит ли значение резервных значений значение производительности при отключении diags?
  • Если да, лучше ли это сделать, чем заполнять xaml с помощью crud?

Edit

Использование резервных значений выглядит так, как будто это не решение, потому что оно также не находит ресурсы:

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

Это похоже на то, что он забывает все в словаре ресурсов, генерируя все эти ложные ошибки, а затем снова запоминает все и отображает ok.

Edit

Хорошо, я немного сузил это, прокомментировав все привязки и вернув их один за другим и разрешив проблемы на этом пути, так что теперь он загружается, и я могу щелкнуть по элементам и не создавать диагемы пока... Когда я нажимаю кнопку, которая меняет элементы дерева, она сходит с ума и извергает сотни ошибок. Ниже приведено небольшое количество ошибок:

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 переоценивает все привязки и, как можно было ожидать, все они не работают.

Edit

Проще говоря...

  • ItemsSource привязан к ObservableCollection.
  • Я вызываю Clear() в ObservableCollection.
  • Все привязки пересматриваются и больше не могут найти свои данные (поскольку они были удалены)
  • В конечном итоге все элементы удаляются.

Почему эти привязки пересматриваются? Есть ли способ заставить его удалить элементы без дополнительной работы?

Edit

Я создал проект, который является частью проблемы. Он генерирует ошибки, жалующиеся на то, что ресурсы не могут быть найдены при вызове 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)

Edit

Попробовал изменить ObjectDataProvider для использования DynamicResource:

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

Но это вызвало это исключение:

Exception generated using DynamicResource for model

4b9b3361

Ответ 1

Мне удалось избавиться от всех ошибок привязки!

Я не уверен, почему, но добавление ресурсов в Application.Resources вместо использования UserControl.Resources разрешило ошибки Resource not found. Это далеко не идеальное решение (было бы гораздо лучше обладать ресурсами пользовательского контроля для пользовательского элемента управления), но он работает.

Все другие ошибки типа dataItem=null были разрешимы путем предоставления резервных значений в различных привязках.

Это решило проблему производительности, которая начала этот вопрос, поэтому ответ на мой первоначальный вопрос заключается в том, что исправление ошибок привязки имеет большое значение для производительности. Теперь я исправил привязки, мои обновления деревьев почти мгновенно, а не на секунду:)

Большое спасибо за помощь!

Джереми