Closed bradphelan closed 5 years ago
Maybe I was too quick... If you set recycling true on the itemTemplate ie
let itemTemplate view dispatch =
Avalonia.Controls.Templates.FuncDataTemplate<(int*'state)>( ( fun (id,state) ->
let viewElement = (view state id dispatch)
let view = viewElement |> VirtualDom.createView
let delta = Avalonia.FuncUI.VirtualDom.Delta.ViewDelta.From viewElement
VirtualDom.Patcher.patch(view, delta)
view
) ,true )
then it is super fast but doesn't render the items correctly. ie: if I delete an item in the middle of the list only the item at the end is deleted.
If I do
let itemTemplate view dispatch =
Avalonia.Controls.Templates.FuncDataTemplate<(int*'state)>( ( fun (id,state) ->
let viewElement = (view state id dispatch)
let view = viewElement |> VirtualDom.createView
let delta = Avalonia.FuncUI.VirtualDom.Delta.ViewDelta.From viewElement
VirtualDom.Patcher.patch(view, delta)
view
) ,false )
Then it is not so snappy but it renders correctly. I guess I'm not so sure about what Avalonia is doing in this case.
First, this is super interesting. The current version had no focus at all on templates and virtualisation support.
I just ran your example.
It seems like the dataTemplate needs to subscribe to its DataContext and manually apply changes. This is not a good way for FuncUI.
I have plans to get this right in vNext - but currently have nothing concrete. My idea is basically to wrap each item template automatically with a HostControl and the (also in the background) subscribe to changes. This would be super convenient ( - and could also support further optimisations).
What are you trying to do with FuncUI ?
I personally use it to build 'Flink' my 2D Editor and seeing other use cases is super interesting. I have big plans for FuncUI.
I hope once Avalonia gets more traction this will be the thing F# devs happily use to write apps for all platforms and devices.
Actually. I'm not trying to build anything. I'm a C++ dev by day and I had a lazy sunday. It's been a while since i hacked on F# and it's a super elegant language. We do have a need at work for platform independant UI's ( linux, windows ) and I thought maybe I'd look at Avalonia but XAML is so boring to work with so I thought I'd try some F# with it.
However understanding how to write dispatchers and updates for heavily nested structures with lists is non-trivial or at least less trivial than using XAML binders with mutable structures. There is certainly some duality between the dispatch logic and the update logic that is asking for a set of abstractions to make this obvious to deal with but I can't quite figure out what they should be.
I'm no web developer but probably looking at how React handles virtualizing long lists might assist. https://github.com/bvaughn/react-window
I had an idea. Have you looked into using optics / lenses. You could create binders that look very much like standard XAML binders but still working with immutable data.
https://github.com/eiriktsarpalis/TypeShape/blob/master/samples/TypeShape.Samples/lens.fs
Imagine binders/optics that have the behaviour of XAML binders ( not the same as the above link )
let personView binder = [
Views.stackLayout [
StackLayout.children [
Views.TextBox [ TextBox.Text (binder >>= <@ fun c -> c.name @>) ]
Views.TextBox [ TextBox.Text (binder >>= <@ fun c -> c.age @>) ]
Views.TextBox [ TextBox.Text (binder >>= <@ fun c -> c.address @>) ]
Views.TextBox [ TextBox.Text (binder >>= <@ fun c -> c.starSign @>) ]
]
]
]
Notice that the binders are composable based on optics technology. You would have one root mutable object which I assume you have in FuncUI already. Then you create optics/binders that index into the model. Essentially a binder is a pair IObservable
It's kind of a compromise between mutable binders and a fully immutable data store and for simple cases easier to manage that a pure functional system.
Just an idea.
Haven't done a lot with lenses so far to be honest.
I'm currently implementing lazy views and I am pretty sure the same concept works fine for virtualisation. I'll let you know when I have something to play around with
I now have a working prototype!
backing data is a list from 1 to 100.000
Awsome :)
On Fri., 4 Oct. 2019, 20:19 Josua Jäger, notifications@github.com wrote:
backing data is a list from 1 to 100.000
[image: 2019-10-04 20 13 26] https://user-images.githubusercontent.com/13090415/66230106-b4fdb000-e6e3-11e9-8e5c-2c8698b29d99.gif
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/JaggerJo/Avalonia.FuncUI/issues/19?email_source=notifications&email_token=AAAEJ4SKR37GT7QXUFKCEZTQM6CJ3A5CNFSM4I3TNPUKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAMPUZQ#issuecomment-538507878, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAEJ4URSUHGSVXJBEFPUWTQM6CJ3ANCNFSM4I3TNPUA .
I've tested adding 1000 items to a stack panel using the naive implementation and the UI grinds. However I spent a bit of time figuring out how to get DataTemplates working in FuncUI way and it seems to work and the UI becomes super snappy even for 1000 element lists.
The code for the working mini app is
https://gist.github.com/bradphelan/06d2e2250facfcf01b848ee71fda4064
The critical line is a helper
and can be used to render lists efficiently like below
Maybe I'm telling you something you already know but it seemed like a non-obvious trick and I had to pull some code out of the API to make it work. Hope it is useful for vNext as you think about it.