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.
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.
I put an explicit binding for DataContext
with a value converter to try and see what is going on.
<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.
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!).
So my question is:
- Are these diags likely to cause the performance problem?
- If so, would supplying fallback values make any difference to performance when diags are turned off?
- If so, is there a better way of doing this than filling the xaml with crud?
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.
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
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...
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, I'm also using .NET 3.5 (I have to unfortunately) but this problem did appear with .NET 4.0 also.
I also have two images in the project:
(VS11_Light_Folder_Closed_In.png) (VS11_Light_Folder_Open_In.png)
Edit
Tried changing the ObjectDataProvider
to use DynamicResource:
<ObjectDataProvider x:Key="TheModelProvider" ObjectInstance="{DynamicResource TheModel}" />
But that generated this exception:
ContextMenu.IsOpen
? and doesImg_Folder_Closed_Ex
exist somewhere? – Dewyno Hierarchi == no HierarchicalDataTemplate
. It looks like you need to provide more code or you could also build a simple version an look if the behavior is the same and than post or link the full code of the simple example – DewyStaticResource
, that binding will be "generated" once for the entire lifetime of the resource - have you tried swapping over to usingDynamicResource
bindings instead? – Hygroscopic