fabulous-dev / Fabulous

Declarative UI framework for cross-platform mobile & desktop apps, using MVU and F# functional programming
https://fabulous.dev
Apache License 2.0
1.15k stars 121 forks source link

[Question] How to ensure a custom renderer does not generate a new element each time the view function is called (WPF) #829

Closed BeardedPlatypus closed 3 years ago

BeardedPlatypus commented 3 years ago

Heya, let me start off with thank you for creating Fabulous, I have been toying with it for the past few months and it has been an absolute blast.

I am not sure if this is the appropriate forum to post this, so if there is a better location, I'd be more than happy to move it to there.

Question / Discussion

For a small hobby project, I have created a small library on top of SDL2 and exposed this to .NET. I am using this to render code with SDL within a WPF application. I have created a Fabulous Xamarin.Forms WPF application in which I want to use this. I have written a really basic custom renderer to create the component within my application that uses this SDL2 view.

My problem is now, that each time I call my view function within my Fabulous code, a new control is created, which in turn creates a new WPF element. This is problematic as (in my current understanding) SDL2 binds its rendering to the window its contained in. When a new WPF element is created, the old window is thrown away and my SDL2 window needs to rebuild its complete state. While this would be technically possible, this is not the behaviour I want, instead I want to just update the view with the new state.

What I am trying to achieve now is that my custom renderer does not generate a complete new view, but reuses my old underlying view element, without instantiating a new element. For the life of me I cannot figure out how to do this, if someone could point me towards me some documentation on how to achieve this, I would be more than grateful.

I understand that having state associated with my view is not necessarily the best idea, and it might be well possible that using Fabulous Xamarin.Forms is just the wrong idea here, however I would like to build the majority of the user interface with Fabulous, and only do the actual specific rendering code within SDL2.

For reference, this is my current code:

I have tried wrapping the viewport in a fix function, as well as assigning it a key, neither has effect on the recreation of the view. I hope this properly explains my problem I am having, if not I'd gladly provide additional details.

Thank you so much for your time!

TimLariviere commented 3 years ago

@BeardedPlatypus Thank you for using Fabulous!

I'm not sure what's happening here. Fabulous tries (relatively aggressively) to reuse existing elements as much as possible. If your viewport is at the same place, with the same type (and you're not switching between viewport displayed, viewport removed), Fabulous will reuse it.

Your wrapping code is ok. Fabulous should reuse the element.

Could you tell me if in the debug output (usually in your IDE) you see a message telling that a new Viewport is created each time? I don't know if OnElementChanged behaves differently on WPF. If so, this is maybe where the viewport keeps getting recreated.

That, or a parent control keeps getting recreated, forcing all its children to get recreated as well.

BeardedPlatypus commented 3 years ago

@TimLariviere Thank you so much for your response

That, or a parent control keeps getting recreated, forcing all its children to get recreated as well.

I feel so dumb! I should have realised that I actually was recreating a parent.

After your comment, I went over my complete view code again, and further investigated with the help of the debug output.

Of course, it turns out I was doing something stupid. In my main view logic, I wanted to place an overlay when the project page was loading, which would darken my UI until it was ready. In order to do so, I, in my infinite wisdow, placed both the actual content, and the a boxview in a grid to create the overlay. This was triggered by single boolean, which determined whether I returned either the grid with both the content and the overlay, or just the content. Of course, this means that either I would have a grid as a parent of my viewport, or the content directly being the child of my content presenter. I assume that this is the reason why the viewport was being recreated, especially given that removing this behaviour indeed fixes my problem!

I apologise for wasting your time with this! I do really appreciate your time and effort in responding, and for all the work you've done on Fabulous! Thank you again.

I'll go ahead and close this issue.