11.2+ : Binding of ItemsSource in a UserControl children is broken if the DataContext isn't explicitly set in the UserControl
Whiletru3 opened this issue · 20 comments
Describe the bug
In Avalonia 11.2+, there are some regression regarding the DataContext in UserControl.
When the DataContext of a UserControl is heritated, if a child control is an ItemsControl, the ItemsSource binding remain null.
To Reproduce
Using this example (just put it in the sample folder of avalonia and add it in the solution), as is in 11.1.x you can see 20 images (with random different sizes) with one red recrangle in it like this :
When imported in 11.2 (here in 11.2.2),
The ItemSource of the ListBox is null
In the sample, if you set the DataContext explicitly in the SingleScrollingViewerPanel (the UserControl), it brings back the images
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="clr-namespace:VirtualizationImagesWithEvents.ViewModels"
xmlns:virtualizationImagesWithEvents="clr-namespace:VirtualizationImagesWithEvents"
xmlns:layout="clr-namespace:VirtualizationImagesWithEvents.Layout"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="VirtualizationImagesWithEvents.SingleScrollingViewerPanel"
x:DataType="model:MainWindowViewModel"
x:CompileBindings="True"
>
<!--todo put this DataContextBinding in the UserControl
DataContext="{Binding (model:ViewModelLocator).MainWindowViewModel, Source={StaticResource ViewModelLocator}}"
-->
<!--The DataContext of the UserControl need to be set otherwise the listbox ItemsSource will remain null-->
BUT not yet the rectangles.
For the rectangles, in the same file, we need to set the DataContext of the LayoutControl and use an ugly trick (a member of the PageviewModel returning this) or remove the original Binding to point :
// This PageSource (pointing to this) is used only to be able to trigger the DataContext binding
public PageViewModel PageSource
{
get
{
return this;
}
}
<layout:LayoutControl x:Name="ctlLayout"
HorizontalAlignment="Center"
VerticalAlignment="Top"
x:DataType="model:PageViewModel"
DataContext="{Binding .}"
>
<!--todo put this DataContextBinding in the LayoutControl
DataContext="{Binding PageSource }"
-->
<!--Here we need to re set the DataContext of the control to a member (pointing to the same object this), otherwise the ItemsControl ItemsSource will stay null, or remove the original binding to point-->
</layout:LayoutControl>
This is a blocker issue for us to go in 11.2+ :-(
Here is the package to reproduce :
VirtualizationImagesWithEvents.zip
Expected behavior
As the Datacontext id Inherited, the listbox shouldn't be empty and the LayoutControl for each page as well. The behavior should be the same as in 11.1.3
Avalonia version
11.2.1 11.2.2
OS
Windows, macOS
Additional context
No response
Removing DataContext="{Binding ., Mode=OneWay}
from your project makes it work.
If DataContext is inherited, there's no need to set it via a binding.
I'm not sure what sort of behaviour you're expecting here, if the data-context changes what things are bound to, how is binding a datacontext without an explicit source going to work? Changing the data-context would prompt bindings to change, but if the datacontext itself is bound would that prompt itself to change itself?
What you are doing sounds very strange to me. Just remove your DataContext binding and everything works fine, because the datacontext is inherited.
Hi,
I also encountered this bug on a feed reader app (not public) I wrote. However, I postponed reporting it because I failed to make a minimal reproduction, and assumed I must be doing something wrong (which might be the case after all).
This is how it looks on avalonia 11.1.4
:
Trying to upgrade to 11.2.2, without any other change to code:
Hi, I also encountered this bug on a feed reader app (not public) I wrote. However, I postponed reporting it because I failed to make a minimal reproduction, and assumed I must be doing something wrong (which might be the case after all).
This is how it looks on avalonia
11.1.4
:Trying to upgrade to 11.2.2, without any other change to code:
Are you also using a binding on the DataContext itself?
I haven't seen any defined behaviour of what it means to bind a data-context like this. I don't even have a conceptualization of what ought to be expected when you attempt to bind that which changes the meaning of what things point to on bindings.
So, I would perhaps recommend not doing this and not relying on that kind of behaviour.
Can any of y'all find documentation on what the intended behaviour is for Binding a DataContext property?
No, I am not doing anything with
DataContext
. The previewer is rendering the contorls correctly, yet it is blank when I run it.
If we can get a minimal (as small as one could possibly get) reproduction, then we can get this fixed.
Hi, I also encountered this bug on a feed reader app (not public) I wrote. However, I postponed reporting it because I failed to make a minimal reproduction, and assumed I must be doing something wrong (which might be the case after all).
This is how it looks on avalonia11.1.4
:
Trying to upgrade to 11.2.2, without any other change to code:Are you also using a binding on the DataContext itself?
I haven't seen any defined behaviour of what it means to bind a data-context like this. I don't even have a conceptualization of what ought to be expected when you attempt to bind that which changes the meaning of what things point to on bindings.
So, I would perhaps recommend not doing this and not relying on that kind of behaviour.
Can any of y'all find documentation on what the intended behaviour is for Binding a DataContext property?
The binding on itself {Binding .} or {Binding} is commonly use in xaml wpf and even in avalonia repository.
We have a workaround yes, but still it is a regression in Avalonia 11.2+.
It was really hard to reproduce it in avalonia and point this Binding.
I'll try to create an even smaller sample.
The binding on itself {Binding .} or {Binding} is commonly use in xaml wpf and even in avalonia repository. We have a workaround yes, but still it is a regression in Avalonia 11.2+. It was really hard to reproduce it in avalonia and point this Binding. I'll try to create an even smaller sample.
This works for me
<ListBox DataContext="{Binding Path=., Mode=OneWay}" ItemsSource="{Binding Path=Items}" />
Correctly, and the ItemsSource is populated.
Interestingly in your sample. LstSingleScrolling_OnDataContextChanged
never gets called. 🤔
Here is a smaller sample to reproduce. See the MyUserControl.axaml
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:AvaloniaListBoxDemo.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaListBoxDemo.MyUserControl"
x:DataType="viewModels:MainWindowViewModel"
x:CompileBindings="True"
>
<!--
In avalonia 11.1.x, this works out of the box
In Avalonia 11.2.x, the ListBox is empty. To get it worked, we need to set the datacontext in the usercontrol .
DataContext="{Binding (model:ViewModelLocator).MainWindowViewModel, Source={StaticResource ViewModelLocator}}"
OR remove the DataContext in the listbox
The Grid is mandatory to reproduce the issue, otherwise it works with the DataContext="{Binding .}"
-->
<Grid>
<ListBox x:Name="lstSingleScrolling"
ItemsSource="{Binding Pages}"
DataContext="{Binding .}"
>
<!--
-->
<ListBox.DataTemplates>
<DataTemplate DataType="viewModels:PageViewModel">
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.DataTemplates>
</ListBox>
</Grid>
</UserControl>
I'll try it in the PR: #17683
AvaloniaListBoxDemo.zip
@MrJul , I applied your changes in BindingExpression.cs in the 11.2.2 tag, but the issue remains.
Okay, I'm reproducing it now.
Yes, I can confirm that the grid is necessary to replicate the issue.
I'm using an even simpler model and data-context, just showing a list of strings.
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Debugging.Issue1744"
xmlns:local="clr-namespace:ControlCatalog.Debugging;assembly=ControlCatalog"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
x:DataType="local:Issue17744DataContext"
x:CompileBindings="True"
>
<Grid>
<ListBox DataContext="{Binding Path=., Mode=OneWay}" ItemsSource="{Binding Path=Items}" >
<ListBox.DataTemplates>
<DataTemplate DataType="system:String">
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListBox.DataTemplates>
</ListBox>
</Grid>
</UserControl>
But I noticed something interesting.
If I swap the InitializeComponent and DataContext assignment it works fine.
How curious! If I assign the data-context first and then load the xaml it works. If I load the xaml and then assign the data-context it doesn't work.
[Edit]
And I can confirm that the event for DataContextChanged does NOT fire when the problem is observed.
I think I've perhaps found the problem...
In the above screenshot, the Owner
is the Listbox in question.
The Binding
the data-context COUNTS as it being set locally
Which means it's not notified that the data-context has changed
Because locally set values override inherited values.
So, it doesn't raise the property-changed notification. 🤔
Locally set values override inherited values. Then how is binding to a DataContext supposed to work?
@Whiletru3 @ can you try my attempted fix in
And confirm whether or this this appropriately addresses your issue?
@ShadowMarker789 : you mention in your PR some test failures. Do I need to test it anyway ?
@ShadowMarker789 : you mention in your PR some test failures. Do I need to test it anyway ?
Those have been addressed. Those were unit-tests that were helpful in making sure I did not break functionality elsewhere.
Please test when you can.
@ShadowMarker789 : I'll test it next monday, can't do it sooner :-/