Closed mrpmorris closed 1 year ago
Oh, VERY NICE!!!
You check if the source is a different instance. That is a very good way to do change detection IMHO, it makes it easy for the control to know it doesn't need to re-render the contents - I can see that confusing people who are used to Blazor WASM working differently though.
Does this mean you aren't building a render tree and diff'ing it?
(The following code works as expected)
@page "/main"
@using System.Collections.Immutable;
<ContentPage>
<ScrollView>
<VerticalStackLayout>
<Button Text="Rotate data" OnClick="RotateData" />
<Button Text="Change data" OnClick="ChangeData" />
<CollectionView ItemsSource="People">
<ItemTemplate>
<HorizontalStackLayout Spacing="4">
<Label Text="@context.GivenName" />
<Label Text="@context.FamilyName" />
</HorizontalStackLayout>
</ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
@code {
ImmutableList<Person> People = new List<Person>
{
new Person
{
ID = 1,
GivenName = "Peter",
FamilyName = "Morris"
},
new Person
{
ID = 2,
GivenName = "Bob",
FamilyName = "Monkhouse"
},
new Person
{
ID = 3,
GivenName = "Frank",
FamilyName = "Sinatra"
},
new Person
{
ID = 4,
GivenName = "David",
FamilyName = "Banner"
}
}.ToImmutableList();
void ChangeData()
{
Person person = People[0];
person = person with { GivenName = person.GivenName + "!" };
People = People.RemoveAt(0).Insert(0, person);
}
void RotateData()
{
var person = People[0];
People = People.RemoveAt(0);
People = People.Add(person);
}
public record Person
{
public required int ID { get; init; }
public required string GivenName { get; init; }
public required string FamilyName { get; init; }
}
}
Yeah, I need to update the documentation regarding CollectionView))
You don't need to update the DTO itself, but you need to use ObservableCollection so that CollectionView would understand the updates.
Unfortunately, I wasn't able to use @key directive here (or anywhere else in MAUI, in fact). @key directive works with StackLayout, but, frankly, StackLayout is not a great fit for cases with lots of items anyway.
Your updated code works, because you're replacing the whole ItemsSource on each update.
That works, but probably not as efficient, it will probably re-render all the items.
I know why the latter works, I am pointing out that the former does not work the same as in Blazor.
The rule should be 1: Is it a simple type like int/string etc? If so, then only render if the value has changed. 2: Is it an Observable? If so, only render on notification. 3: Otherwise, always render.
Blazor doesn't do step 2, but I can see it would be needed for MAUI, however, it wouldn't be a good approach for Blazor WASM and this difference makes it difficult to write an app once and have it run in both.
BlazorBindings.Maui uses the same renderer internals as regular Blazor. The problem here is that CollectionView is not fully managed by Blazor Renderer. Renderer manages CollectionView instance itself, and it manages each rendered from template item separately. However, the decision when to add or remove items, and which itemContext should be used by a template - that is managed by a CollectionView itself.
I was thinking maybe to create some kind of a diff mechanism, which would compare the items before and after the render - to avoid the requirement to use ObservableCollections. That doesn't sound easy though.
But if you want to run the app once and run it in both, BlazorBindings.Maui is probably not a great pick anyway. It renders "native" Maui controls, therefore you won't be able to use it without Maui. MAUI Blazor Hybrid (yeah, naming is a bit confusing here) probably would be a better pick - as you would use web controls instead of MAUI contols.
I am writing the business logic + state using Fluxor, which is UI agnostic. I then only need to create views in WASM and equivalent views in MAUI and I will get the same app.
My boss doesn't want to use hybrid, he doesn't like the feel of it compared to a native experience.
@mrpmorris I have merged changes to allow to track updates in ItemsSource collection. It's not yet released to Nuget, but it would be great if you'd be able to test in the latest MyGet build. https://dreamescaper.github.io/MobileBlazorBindingsDocs/maui-blazor-bindings/contribute/nightly-builds.html
I've been trying to reproduce this article about
@key
https://blazor-university.com/components/render-trees/optimising-using-key/But the UI doesn't rerender when the user interacts with the component, even if I call
StateHasChanged
.I realise I can implement INotifyPropertyChanged, but as I edit DTOs from an API Contracts library I would like not to have to do this (introduce UI concerns into the API).