14

パフォーマンスが非常に低いツリー コントロールがあり、問題の原因を追跡しようとしています。

次のような警告が重要かどうかを判断しようとしています。

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

これらすべての diag がオフになっている場合でも、ツリーの内容を更新するパフォーマンスは本当に恐ろしいものです (約 300 個の項目を再作成するのに 1 秒以上かかります)。

これらの警告は、ツリー ビューでクリックするたびに数十個吐き出されます。ツリーを切り替えて別のコンテンツを表示すると、これらの警告が数百個発生します。ただし、ツリーの内容は常に正しく表示されるため、データ コンテキストは一時的にのみ 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. もしそうなら、xaml をクラッドで埋めるよりも良い方法はありますか?

編集

フォールバック値を使用しても、リソースの検索に失敗しているため、とにかく解決策ではないようです。

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

リソース ディクショナリ内のすべてを忘れて、これらすべての偽のエラーを生成し、すべてを再度記憶して OK を表示しているようです。

編集

OK、すべてのバインディングをコメントアウトして 1 つずつ元に戻し、途中で問題を解決することで、これをもう少し絞り込みました。これでロードされ、項目をクリックして確認できます。ツリー項目を変更するボタンをクリックすると、おかしくなり、何百ものエラーが吐き出されます。エラーの小さなサブセットを次に示します。

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アプリケーションから変更されたコードは次のとおりです...

アプリ.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; }
    }
}

テーマ.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でも発生しました。

プロジェクトには 2 つの画像もあります。

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

編集

ObjectDataProviderDynamicResource を使用するように変更してみました:

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

しかし、それはこの例外を生成しました:

モデルの DynamicResource を使用して生成された例外

4

1 に答える 1

10

すべてのバインディング エラーを取り除くことができました。

理由はわかりませんが、Application.Resources使用する代わりにリソースを追加するとエラーUserControl.Resourcesが解決しました。Resource not foundこれは理想的な解決策とはほど遠いものですが (ユーザー コントロールのリソースをユーザー コントロールにスコープする方がはるかに優れています)、機能します。

他のdataItem=nullタイプのエラーはすべて、さまざまなバインディングでフォールバック値を提供することで解決できました。

これにより、この質問を開始したパフォーマンスの問題が解決されたため、最初の質問に対する答えは、バインディング エラーを修正するとパフォーマンスに大きな違いが生じるということです。これでバインディングが修正されました。ツリーの更新は 1 秒もかからず、ほぼ瞬時に行われます :)

助けてくれてありがとう!

ジェレミー

于 2013-07-09T10:29:49.310 に答える