Advanced Avalonia+ReactiveUI+Routing example
See original GitHub issueHello,
I want to say that I’m excited to work with Avalonia, this is a fantastic experience to write cross-platform applications which actually works the same on Windows and Linux!
However, to write some advanced applications which uses the full power of chain Avalonia+ReactiveUI+Routing+Asyncs we definitely need some advanced example of code which can show us how to use all these things properly. Also, the current docs about integration with ReactiveUI shows us the very simple use-cases and docs for ReactiveUI do not cover enough to be sure that you are writing the leaks-free code in proper style.
Long story short, so, I have started this issue because I want to understand the following concepts.
VM Reactive Commands and Routing
- Where the
ReactiveCommandshould be created - in VM constructor or inWhenActivatedblock? And why? - What is the best way to create the async commands with
async/awaitlogic? Or may be they should be created in other way? - How to properly lock the view (aka make it ReadOnly) when the command is executing?
- How to properly setup and use Routing - for example if I have a command which performs some async operation (like request to some REST API) how should I handle properly:
- The making UI readonly during the request (related to previous point)
- Return errors to UI textblock
- Navigate somewhere using
Router.Navigate(call it in subscription/call it inasync/awaitsource method/in other way)
Binding VMs to Views
- What is the best way of binding - via XAML or via
WhenActivatedin view withDisposeWithcall? And how I should decide which way should be chosen? - How to avoid memory leaks on bindings?
Observable examples
- Good use-cases with observing
SourceCache<TModel, TKey>from singleton service viaConnectwith covering the following topics:- How to use
Connect? - How to
Bindconnected SourceCache toObservableCollectionExtended - How it should be disposed?
- How to use
- Advanced example of Oaph usage - assign from multiple sources, etc. (And how to properly dispose them/Do we need to dispose them?)
- Cover most common use cases with
Subscriptionand proper disposing of them - Example of
this.WhenAnyValueusage and proper disposing of it (or when and how it should be disposed?)
I’m not sure what is the best way how to communicate about it. Ideally, I want to write some example (and possibly exemplary application) after understanding all described concepts. Hope that this future sample application can be used as reference to get started and include it into samples in Avalonia (or as reference here https://github.com/AvaloniaCommunity/awesome-avalonia).
In best case I want to help to extend the Avalonia documentation with all these info.
Hope that you will be able to answer on my questions or provide some links to samples which covers all described cases.
Thanks!
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:8 (3 by maintainers)

Top Related StackOverflow Question
Generally most of the design patterns folks tend to use in WPF are also applicable to Avalonia. There is an online ReactiveUI Handbook which covers almost every aspect of the use cases of the reactive framework, including commanding, routing, activation and memory management. There is also a book by Kent Boogaart which includes a lot of
ReactiveUIandSystem.Reactiveusage examples. There is also a bunch of open-source samples for various platforms.VM Reactive Commands and Routing
On every platform ReactiveUI supports folks usually create the commands in the constructor, and asynchronous logic is usually extracted into separate services which are injected into the view model via the constructor. A button bound to a command will be locked automatically, and to lock the whole view you could declare an OAPH and bind it to the
IsEnabledproperty of any Avalonia XAML control, or of a window.A feature unique to Avalonia is, that the GUI framework allows us to bind to an
IObservable<T>directly without usingOAPHs. Subscriptions to such observables will get disposed automatically. See the Binding to Tasks and Observables section for more info on this topic. Binding to anIObservable<T>directly using Avalonia bindings is generally more perfomant.In case if you are sharing your view models across different platforms that don’t support this feature (e.g. WPF, WinForms or XF), use the
WhenAnyObservablecall inside aWhenActivatedblock on the view side, to bindIObservable<T>to a property manually, e.g.Binding VMs to Views
AFAIK, the code-behind bindings were developed to allow the bindings to be compile-time-checked, and aren’t necessarily needed in Avalonia, because its XAML markup is compiled. The memory won’t leak as well if you bind only to view model properties and not to the properties of injected services that can potentially outlive both the view and the view model. In short, if an object subscribes to its own properties, there is no need to dispose such subscriptions. For example, here the memory won’t leak. See When should I bother disposing IDisposable objects?
If you have a
ReadOnlyObservableCollection<SomeDisposableViewModel>of view models with subscriptions and you need those subscriptions to be disposed automatically once an element is removed from the collection, take a look at theDisposeManyDynamicData operator. With its help, you can use a different approach — simply implement theIDisposableinterface as usual, or, using aCompositeDisposable; and add a call to.DisposeMany()to yourIObservable<IChangeSet<T>>pipeline.Observable examples
To combine multiple observable streams into a single stream either use
CombineLatestorZipdepending on what you are willing to achieve. For better understanding of such operators, asConnectandRefCount, read a note about Hot and Cold Observables.For
DynamicDataquestions consider joining ReactiveUI Slack, the#dynamicdatachannel. Generally you don’t want to bind aSourceCacheto anObservableCollectionExtendedas it is a mutable collection, and the thing you are looking for is aReadOnlyObservableCollectionand some kind of aMergeoperator. See DynamicData Blog for more info about idiomatic usage. See also the DynamicData.Snippets project which contains a lot of code examples and a blog post which explains the differences between the typicalIObservable<T>andIObservable<IChangeSet<T>>. Thanks for your interest!@cheprogrammer didn’t have time to do a full profiling session, but probably the issue is in here https://github.com/cheprogrammer/SampleAvaloniaApplication/blob/001d741a15af7284b0960f51bb910b1c580cc8e0/Client/SampleAvaloniaApplication.Client.Core/Models/EmployeeModel.cs All the properties of the dto are marked as
[Reactive]. Then, those properties are observed in theEditEmployeeViewModeland never unsubscribed, and the referenced model is kept inside theEmployeesService. Try usingNavigateAndResetinstead ofNavigatehere, because otherwise the view model is left in the navigation stack as well https://github.com/cheprogrammer/SampleAvaloniaApplication/blob/001d741a15af7284b0960f51bb910b1c580cc8e0/Client/SampleAvaloniaApplication.Client.Core/ViewModels/Employees/EmployeesViewModel.cs#L56 But still worth doing a profiling session, probably will have time this weekend.A common pattern is to proxy the properties of a model to the view model, so the dto which is saved to the database is never subscribed to, and as a result it never introduces a potential for a memory leak. If a view model observes its own properties, then it won’t leak. So another suggestion is to keep your database entities as plain C# objects without
Reactiveattributes.