Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Converter receiving unexpected null value #16016

Closed
MartinZikmund opened this issue Mar 25, 2024 · 4 comments · Fixed by #16114
Closed

Converter receiving unexpected null value #16016

MartinZikmund opened this issue Mar 25, 2024 · 4 comments · Fixed by #16114
Assignees
Labels
difficulty/medium 🤔 Categorizes an issue for which the difficulty level is reachable with a good understanding of WinUI kind/bug Something isn't working kind/papercut Issue that is especially relevant to users beginning to use Uno project/binding 🪢 Categorizes an issue or PR as relevant to the binding engine

Comments

@MartinZikmund
Copy link
Member

MartinZikmund commented Mar 25, 2024

Current behavior

Converter is receiving null value when it should not be possible.

Expected behavior

No null value

How to reproduce it (as minimally and precisely as possible)

uno-bug-repro-converter-null-main.zip

Workaround

Gracefully handle null values in converters.

Works on UWP/WinUI

Yes

Environment

No response

NuGet package version(s)

No response

Affected platforms

No response

IDE

No response

IDE version

No response

Relevant plugins

No response

Anything else we need to know?

No response

@MartinZikmund MartinZikmund added kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. project/binding 🪢 Categorizes an issue or PR as relevant to the binding engine difficulty/medium 🤔 Categorizes an issue for which the difficulty level is reachable with a good understanding of WinUI kind/papercut Issue that is especially relevant to users beginning to use Uno and removed triage/untriaged Indicates an issue requires triaging or verification difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. labels Mar 25, 2024
@ramezgerges ramezgerges self-assigned this Apr 2, 2024
@ramezgerges
Copy link
Contributor

This seems to be a problem not with ComboBox but something a lot deeper. I've followed the traces and managed to reduce it to this.

	<Page.Resources>
		<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
	</Page.Resources>
	<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
		<Button Click="Button_Click">Set</Button>
		<Button Click="Button_Click_1">Reset</Button>
		<ContentPresenter x:Name="cp">
		  <ContentPresenter.ContentTemplate>
		    <DataTemplate x:DataType="local:TestItem">
		      <TextBlock Text="Test" Visibility="{x:Bind Test, Converter={StaticResource BoolToVisibilityConverter}}" />
		    </DataTemplate>
		  </ContentPresenter.ContentTemplate>
		</ContentPresenter>
	</StackPanel>
	private void Button_Click(object sender, RoutedEventArgs e)
	{
		cp.Content = new TestItem();
	}

	private void Button_Click_1(object sender, RoutedEventArgs e)
	{
		cp.Content = null;
	}

And then even further to:

  <Page.Resources>
    <local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
  </Page.Resources>
  <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <Button Click="Button_Click">Set</Button>
    <Button Click="Button_Click_1">Reset</Button>
    <TextBlock x:Name="tb"  Text="Test" Visibility="{Binding Test, Converter={StaticResource BoolToVisibilityConverter}}" />
  </StackPanel>
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        tb.DataContext = new TestItem();
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        tb.DataContext = null;
    }

Which clears any doubts about lifecycle-related problems. If you very explicitly set the DataContext of an element to null, WinUI will still not call the converter with null.

@ramezgerges
Copy link
Contributor

More updates: it seems like the converter can be called with null, but only if the final property of the path is null. I.e. if you have a bindinging to {Binding Outer.Inner, Converter=...},

  1. If the DataContext is null, the converter won't be called.
  2. if DataContext.Outer is null, the converter won't be called.
  3. if DataContext.Outer.Inner is null, the converter WILL be called.

@ramezgerges
Copy link
Contributor

ramezgerges commented Apr 3, 2024

I noticed that I forgot to go back and test my fix for x:Bind (I assumed fixing Binding would make x:Bind behave the same way), and it turns out that there're quite a lot of subtle behavioural differences between x:Bind and Binding on WinUI

I'm testing on a TextBlock like this:

<StackPanel>
  <Button Click="Button_Click">set</Button>
  <Button Click="Button_Click_1">reset</Button>
  <ContentPresenter x:Name="cp">
    <ContentPresenter.ContentTemplate>
      <DataTemplate x:DataType="local:TestItem">
        <TextBlock Text="{x:Bind Outer, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay, FallbackValue='fallback'}" />
      </DataTemplate>
    </ContentPresenter.ContentTemplate>
  </ContentPresenter>
  <TextBlock Text="----------------------------------" />
  <TextBlock x:Name="tb" Text="{Binding Outer, Converter={StaticResource BoolToVisibilityConverter}, FallbackValue='fallback'}" />
</StackPanel>
Scenario x:Bind in ContentPresenter DataTemplate Binding x:Bind outside DataTemplate
binding to Outer.Inner, DC = null converter NOT called, TextBlock.Text value unchanged (!!), visually blank converter NOT called, TextBlock.Text = fallback not applicable
binding to Outer, DC = null same as above same as above not applicable
binding to blank path, DC = null same as above same as above not applicable
binding to Outer.Inner, DC not null, DC.Outer = null converter NOT called, TextBlock.Text = fallback converter NOT called, TextBlock.Text = fallback <-- same
binding to Outer.Inner, DC not null, DC.Outer not null, DC.Outer.Inner = null converter called, TextBlock.Text = value returned by converter if not null, otherwise TextBlock.Text = TargetNullValue ?? "" <-- same <-- same
binding to Outer, DC not null, DC.Outer = null same as above same as above <-- same
  • Setting TargetNullValue to null is identical to not setting it at all (the generated code in the case of x:Bind is the exact same).

@ramezgerges
Copy link
Contributor

ramezgerges commented Apr 3, 2024

Additionally, when we set ContentPresenter.Content to null, in Uno the ContentPresenter in the xaml snipper in the previous comment will have no children, while in WinUI, the TextBlock will continue to exist as a child of the ContentPresenter (again, visually blank, so it's like it's not there). I will have to recheck this later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty/medium 🤔 Categorizes an issue for which the difficulty level is reachable with a good understanding of WinUI kind/bug Something isn't working kind/papercut Issue that is especially relevant to users beginning to use Uno project/binding 🪢 Categorizes an issue or PR as relevant to the binding engine
Projects
None yet
2 participants