lysann.schlegel_pdx
Topic Author
Posts: 8
Joined: 04 Aug 2021, 11:12

Avoiding binding errors when part of the path is null

03 Nov 2021, 17:36

Simplified version of my XAML:
<Border x:Name="owner" Height="20" Width="20"
        Visibility="{Binding Owner, Converter={StaticResource booleanToVisibilityConverter}}"
        ToolTip="{Binding Owner.Name}">
    <TextBlock Text="{Binding Owner.Name}" />
</Border>
That booleanToVisibilityConverter is not actually the default one but an extended one that interprets null as false.
So basically if Owner is null I want the whole thing to be collapsed, otherwise we'll display its Name.

When Owner is not null, everything works as expected.
When Owner is null, the visibility works correctly, however I get binding errors for both the ToolTip and the TextBlock.Text:
[PropertyPath.cpp:263]: [Noesis Warning] Property 'MyViewModel.Owner' returned null
[Binding.cpp:94]: [Noesis Error] Binding failed: Path=Owner.Name, Source=MyViewModel(''), Target=TextBlock(''), TargetProperty=TextBlock.Text
[PropertyPath.cpp:263]: [Noesis Warning] Property 'MyViewModel.Owner' returned null
[Binding.cpp:94]: [Noesis Error] Binding failed: Path=Owner.Name, Source=MyViewModel(''), Target=Border('owner'), TargetProperty=ToolTipService.ToolTip
Setting FallbackValue and TargetNullValue on the bindings to an empty string doesn't help squelch the errors.

When running the same example with WPF I don't get any binding errors (to be fair I don't get any even if I remove the Visibility binding, so basically, no binding errors at all...).

Short of representing a missing Owner with a dummy value (which feels more like a workaround), I don't know how to fix this. Shouldn't FallbackValue kick in here?
I'm on version 3.0.12.

Edit: It appears that FallbackValue does kick in here, at least in my test I can see the FallbackValue when removing the Visibility binding and setting FallbackValue to 'foo' on the TextBlock.Text binding. So perhaps it is just the error reporting that is a bit too eager?
 
User avatar
sfernandez
Site Admin
Posts: 2983
Joined: 22 Dec 2011, 19:20

Re: Avoiding binding errors when part of the path is null

04 Nov 2021, 11:32

Hi,

All the issues that Noesis founds while trying to resolve bindings are logged to the "Binding" channel, so you can easily filter them out in your LogCallback if you are not interested.
In Visual Studio you can also go to the Options dialog and change the 'Data Binding' verbosity level in Debugging > Output Window > WPF Trace Settings. If you set it to Information you will get something similar to what Noesis generates.

Anyway, in this particular scenario I see that Noesis considers a binding error when a property of the binding path returns null and is trying to resolve a subproperty of that null object. WPF outputs that issue just as an information message, no warning or error. I don't know why, because for me it is an error, I would like to know why my binding is not showing anything. What do you think about it?
 
User avatar
ttermeer-reboundcg
Posts: 17
Joined: 13 Sep 2019, 11:10
Contact:

Re: Avoiding binding errors when part of the path is null

04 Nov 2021, 15:48

Just yesterday I was wondering the same thing (under Unity). There is merit to both sides.
The reason I believe this should not be considered an error is the following:
You have an element that is visible only if there is data in your binding. So the proper way, is to have a boolean check for visibility. But you data is still there is processed. However this data should be allowed to be null since you don't want it to be displayed.

If this scenario is considered an error, then we are required to do extra work in binding/view models - which serves no purpose as Noesis already handles it.


Example in C#
WPF Normal Binding
<Border x:Name="owner" Height="20" Width="20"
        Visibility="{Binding Owner, Converter={StaticResource booleanToVisibilityConverter}}"
        ToolTip="{Binding Owner.Name}">
    <TextBlock Text="{Binding Owner.Name}" />
</Border>
public class ViewModel {
	public OwnerClass Owner {get; set;}
}
Required code for Noesis to not generate error:
<Border x:Name="owner" Height="20" Width="20"
        Visibility="{Binding Owner, Converter={StaticResource booleanToVisibilityConverter}}"
        ToolTip="{Binding OwnerName}">
    <TextBlock Text="{Binding OwnerName}" />
</Border>
public class ViewModel {
	public OwnerClass Owner {get; set;}
	public string OwnerName => Owner?.Name;
}
As you can see, this creates a lot of overhead and makes writing view models a lot more complicated if you have a lot of objects
 
lysann.schlegel_pdx
Topic Author
Posts: 8
Joined: 04 Aug 2021, 11:12

Re: Avoiding binding errors when part of the path is null

05 Nov 2021, 10:02

I agree with the above post in general. My thoughts are a bit more complicated, so let me write them down for your consideration:
  1. It's probably not too wrong to consider this an error if there is no FallbackValue. However if there is a FallbackValue, as far as I understand from the documentation that should kick in when the binding path cannot be resolved. Looks like you interpret this differently though, so I guess it's not clear enough. Either way, if it doesn't consider the FallbackValue we'd have to put extra complexity into our view models just to work around this, like described in the post above.
  2. Even with FallbackValue preventing this error, we'd still have to put a lot of FallbackValues everywhere just to suppress the error. In the case when something is invisible (directly or indirectly via a parent) it should not become a binding error to avoid this boilerplate, example above applies. (I suspect it might not be easy for the system to know the visibility at this point, though?)
  3. This is a bit off topic, but it made me aware that the system evaluates bindings of things that are invisible, and I find that a bit concerning. While my UIs aren't complex enough yet, I worry about the performance implications of this behavior when you have complex conditional UI trees. I am curious how default WPF handles it. I understand that it's not straight forward at all to decide which bindings need to be evaluated with Visibility=Hidden, but at least for Collapsed it could probably skip everything? Anyway, I get off track a bit here, I just wanted to mention this thought.
  4. Thanks for pointing out how to adjust the log level in Blend and Noesis. I still want to be informed about legit Binding warnings/errors (or at least I guess what I think is legit ^^), so it doesn't really help to fix this scenario, but it is certainly good to know.
  5. In general, when our UX designers work with Blend most of the time, I think it is important that the warnings and errors logged are similar between Blend and Noesis, otherwise they'll make a nice UI in WPF that can then spew a lot of errors in Noesis. So I think deviating much from what WPF considers a warning/error needs to have very good reasons. And yes, this could be annoying if WPF is silent about an error state, but I hope that in practice it's outweighed by not getting some warnings only in Noesis? (I might be wrong about this, I haven't worked too much with WPF.)
 
User avatar
sfernandez
Site Admin
Posts: 2983
Joined: 22 Dec 2011, 19:20

Re: Avoiding binding errors when part of the path is null

08 Nov 2021, 19:13

Thanks for the feedback. I checked different xamls with Blend to see what WPF does on each case:

1. Defining a FallbackValue turns a binding error into a warning (this is not the case in Noesis).
<StackPanel Visibility="Collapsed">
  <TextBlock Text="{Binding Child.Name}"/>
  <TextBlock Text="{Binding Child.Age, FallbackValue=AGE_FALLBACK}"/>
</StackPanel>
System.Windows.Data Error: 40 : BindingExpression path error: 'Child' property not found on 'object' ''ViewModel' (HashCode=52136226)'. BindingExpression:Path=Child.Name; DataItem='ViewModel' (HashCode=52136226); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Warning: 40 : BindingExpression path error: 'Child' property not found on 'object' ''ViewModel' (HashCode=52136226)'. BindingExpression:Path=Child.Age; DataItem='ViewModel' (HashCode=52136226); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
2 and 3. Visibility has no effect on binding evaluation. Once an element is part of the logical tree, its bindings get evaluated. But if you have a collapsed control, its Template/ContentTemplate is not generated, so any bindings defined in the template visual tree won't be evaluated and won't raise binding errors/warnings (Noesis does exactly the same here):
<ContentControl Content="Hello" Visibility="Collapsed">
  <ContentControl.ContentTemplate>
    <DataTemplate>
      <StackPanel>
        <TextBlock Text="{Binding Child.Name}"/>
        <TextBlock Text="{Binding Child.Age, FallbackValue=AGE_FALLBACK}"/>
      </StackPanel>
    </DataTemplate>
  </ContentControl.ContentTemplate>      
</ContentControl>
5. We agree on this, so it looks we need to fix a few issues with how we interpret binding errors, could you please report this in our bugtracker?
 
lysann.schlegel_pdx
Topic Author
Posts: 8
Joined: 04 Aug 2021, 11:12

Re: Avoiding binding errors when part of the path is null

03 Dec 2021, 10:42

I made a bug report for the log level difference here (which was point 5): https://www.noesisengine.com/bugs/view.php?id=2207
 
lysann.schlegel_pdx
Topic Author
Posts: 8
Joined: 04 Aug 2021, 11:12

Re: Avoiding binding errors when part of the path is null

03 Dec 2021, 10:43

I want to clarify something about your last example concerning point 1 and the logs generated by Blend when part of the path is null.
Setting up a minimal example using your xaml:
<Window x:Class="Game.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <Window.Resources>
        <sys:Int32 x:Key="AGE_FALLBACK">10</sys:Int32>
    </Window.Resources>
    <StackPanel Visibility="Collapsed">
        <TextBlock Text="{Binding Child.Name}"/>
        <TextBlock Text="{Binding Child.Age, FallbackValue=AGE_FALLBACK}"/>
    </StackPanel>
</Window>
using System.Windows;

namespace Game
{
    public class ChildModel
    {
        public string Name { get; } = "Hello";
        public int Age { get; } = 42;
    }

    public class RootModel
    {
        public ChildModel Child { get; } = null;
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new RootModel();
        }
    }
}
Running this I get no binding errors or warnings in Blend, only Information level messages:
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=Child.Name; DataItem='RootModel' (HashCode=62718864); 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=Child.Name; DataItem='RootModel' (HashCode=62718864); 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=Child.Name; DataItem='RootModel' (HashCode=62718864); 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=Child.Name; DataItem='RootModel' (HashCode=62718864); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Information: 41 : BindingExpression path error: 'Age' 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=Child.Age; DataItem='RootModel' (HashCode=62718864); 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=Child.Age; DataItem='RootModel' (HashCode=62718864); 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=Child.Age; DataItem='RootModel' (HashCode=62718864); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
I believe the example you've posted where we saw an error and a warning, the "Child" path didn't even exist on the data context. I can reproduce these logs if I remove or rename the property in C#:
System.Windows.Data Error: 40 : BindingExpression path error: 'Child' property not found on 'object' ''RootModel' (HashCode=6968762)'. BindingExpression:Path=Child.Name; DataItem='RootModel' (HashCode=6968762); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Warning: 40 : BindingExpression path error: 'Child' property not found on 'object' ''RootModel' (HashCode=6968762)'. BindingExpression:Path=Child.Age; DataItem='RootModel' (HashCode=6968762); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
I want to clarify that the issues I would like to see adjusted concern the case where a property that is part of the path exists but returns null.
 
User avatar
sfernandez
Site Admin
Posts: 2983
Joined: 22 Dec 2011, 19:20

Re: Avoiding binding errors when part of the path is null

03 Dec 2021, 11:21

Hi lyssan,

That's right, in my first example I was forcing the error by not defining the Child property to see how the FallbackValue affected the type of binding messages.
If the property exists and returns null, as you said, the binding just outputs an information message.

We reviewed our implementation to match both behaviors and will work like that in the upcomping release: #2200

Who is online

Users browsing this forum: Google [Bot] and 93 guests