The initialisation sequence of XAML bindings and WhenActivated has changed
See original GitHub issueUsing build 0.8.999-cibuild0003548-beta. The creation of the view’s XAML bindings now occurs before the WhenActivated call in the view model. In v0.8.1, WhenActivated was called before the XAML bindings were made.
The original sequence feels correct to me as you can use the WhenActivated call to setup view model properties that can then be bound in XAML without implementing INotifyPropertyChanged. This is how DynamicData (ReactiveUI for collections) with ReadOnlyObservableCollection works, requiring no INotifyPropertyChanged code.
This sequence change causes failure to display the items generated using DynamicData Bind when called from WhenActivated. This previously worked in v0.8.1. Here is an example of a view / view model that worked in v0.8.1 but now creates the ListBox bound to null instead of the generated list:
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaApplication8.Views.MyView">
<StackPanel>
<TextBlock Text="Here is my list of 3 items:" />
<ListBox Items="{Binding Items}"/>
</StackPanel>
</UserControl>
public class MyViewModel : ReactiveObject, IRoutableViewModel, ISupportsActivation
{
public string UrlPathSegment => "MyView";
public IScreen HostScreen { get; set; }
public ViewModelActivator Activator { get; } = new ViewModelActivator();
private ReadOnlyObservableCollection<string> _items;
public ReadOnlyObservableCollection<string> Items => _items;
public MyViewModel(IScreen screen, MyModel model)
{
HostScreen = screen;
this.WhenActivated(disposables =>
{
model.Values.ToObservableChangeSet()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _items)
.Subscribe()
.DisposeWith(disposables);
//this.RaisePropertyChanged(nameof(Items));
});
}
}
Here is the full repo example. App8.zip
Adding the this.RaisePropertyChanged(nameof(Items));
line fixes the problem in build 0.8.999-cibuild0003548-beta.
So my question is whether : a) the initialisation sequence behaviour is undefined and I should not rely on the initialisation sequence being in any particular order? or b) is this a breaking change? or c) is it a bug?
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:8 (7 by maintainers)
Top GitHub Comments
The workaround using
RaisePropertyChanged
gets quite messy when you want to bind an initialSelectedItem
to theItemsControl
.<ListBox Items="{Binding Items}" SelectedItem="{Binding MySelection}"/>
As the Items binding returns null when the SelectedItem binding is made, the ItemsControl sets the SelectedItem back to null as it doesn’t match any item in the Items.
When WhenActivated is called and the Items gets updated by RaisePropertyChanged, the SelectedItem is now null and the ItemsControl selects the first item in the Items. This is not the required behaviour.
My current workaround involves saving the current selection and restoring it after RaisePropertyChanged
I suspect this will not be the only messy side affect of this change.
We use
AttachedToVisualTree
andDetachedFromVisualTree
events forIVisual
s andOpened
andClosed
events forWindow
s to handle activation and deactivation, and that shouldn’t have changed since 0.8.1 https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs#L61 So looks like the behaviour of visual tree events changed recently.Worth investigating how activation works on WPF etc. but generally a call to
RaisePropertyChanged
looks reasonable, while on most platforms activation happens after view model initialization, and the_items
property is left unassigned when the initialization completes