SkillAgentSearch skills...

AvaloniaMusicStoreFSharp

Avalonia Music Store tutorial using F# and ReactiveProperty

Install / Use

/learn @xayvong/AvaloniaMusicStoreFSharp
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

AvaloniaMusicStore with F#

AvaloniaMusicStore

This is an MVVM project that uses Avalonia UI, F#, and ReactiveProperty. This will be a guide and an example of the Music Store App from their tutorial, and the different ways I approached and overcame some of the difficulties I had during the process.

I'm also still quite new to F# and Avalonia, so if anyone has any advice on what I could have differently or better (Or maybe explain how the heck I use ReactiveUI with F#), any suggestions are welcome!

You can check out Avalonia here: https://avaloniaui.net/

Here's the tutorial I followed: https://docs.avaloniaui.net/docs/next/tutorials/music-store-app/

And here is ReactiveProperty, which I used as an alternative to ReactiveUI and Community Toolkit: https://github.com/runceel/ReactiveProperty#documentation

One thing before you follow along, make sure you first grab the templates for Avalonia. At the time of when I started this project, Avalonia 11 did not come with these templates by default. https://github.com/AvaloniaUI/avalonia-dotnet-templates

So if the tutorial starts referencing project files you don't have, that might be why.

Follow along with the original tutorial for creating the UI. One of my favorite features of Avalonia is the live previewer. You can add and change things and the previewer will update them live. Neat!

AvaloniaPreviewer

Another thing to keep in mind is that hierarchy of your classes and files matters in F#! Whenever you make a new view or class, make sure to order them properly. You can pass things down, but not up!

AvaloniaFileOrder

The first hurdle I had to overcome was figuring out how to set the DataContext, and how to open a dialog.

The tutorial uses ReactiveUI to generate it and I was not able to figure out how to get it to work with F#. I ended up asking for help on the Avalonia Telegram and did some digging through their docs about it. The solution I came up with was to create a Service folder and then create static classes that managed my dialog functions.

image

Doc about referencing the main window https://docs.avaloniaui.net/docs/next/concepts/the-main-window

This way, I can easily open and close a dialog by calling the function and simply pass the view I want to be the dialog. I then set the data context, create a reactive command, and subscribe to my dialog service function so I can bind that to a button.

image

The ShowDialog() function is an async task, so you have to await it using |> Async.AwaitTask and start it with |> Async.Start. More on this later...

In the tutorial, they have you set the search text and the loading bar using reactiveUI, however with reactiveProperty, you only need to create it and set it to a public member for binding.

image

image

image

image

Another difference is that when you are binding your reactive properties, you need to add .Value to the binding.

image

If your bindings aren't working, check that first!

And instead of Observable collection, I use a ReactiveCollection for my search results, and another reactive property for selectedAlbum.

image

image

image

image

For some reason, you don't need to add .Value to a ReactiveCollection when binding. I'm not sure what makes this different, but it works, so ¯\_(ツ)_/¯

Album Service

When it comes to creating the album model, and implementing the iTunes search library I really struggled with this section since I'm still a beginner when it comes to Asynchronous programming. I eventually started getting comfortable with it and figured out how to use F# async tasks. Here's how I did it!

image

I added in the ArtistId to make sure we're saving the json file with a unique file name. More on that later!

Notice how I also set static members. This is a really neat F# feature that lets you call static members like functions without needing an instance of the type!

AvaloniaStaticMember

I also created a static function as a shortcut for creating an empty album. We'll need this later.

If you look at the SearchAsync() function, you'll notice the = async. This is how you build an async workflow in F# and you put your logic inside of the curly brackets.

In my async builder, I first set my searchManager as a new iTunesSearchManager() and then set my query using the GetAlbumsAsync() function. Notice how the let has a !. This is how you bind a Task to a name. Since the GetAlbumAsync() function is a Task, I have to pipe it to an Async.AwaitTask.

We then create the doSearch function in MusicStoreViewModel and it'll look something like this:

image

Under my first if statement, I create another async builder and bind my SearchAsync. Notice how I pipe it to an Async.StartAsTask? Since that function is a computation, I have to start it as a Task.

In F#, Async and Task are not the same thing. Yes, they are both asynchronous workflows, but are considered different value types. However, with the Async.AwaitTask, and Async.StartAsTask, I can work with those tasks inside of an async{} instead of Task{}.

AvaloniaAsyncAwaitTask

At the end of my async builder, I pipe it to an Async.Start

In F#, you await async logic with Async.Await, and when you call your async function you have to use Async.Start. Don't forget to start your async functions!

Another thing I did differently was instead of having the search begin with typing, I created a button, made it invisible, and set the hotkey to Enter.

AvaloniaSearchKey

In order to start the search, I need to create a command and subscribe to it, and then I create a startSearch ReactiveCommand().

image

And subscribe to it where I set all my do commands.

image

Notice how my startSearch is one line lower than the do? You can actually keep adding commands without having to type do ... over and over again for additional commands!

Then we bind the command to a public member and add it to our invisible search button.

image

image

To load the covers in my album model, I set a public member with LoadCoverBitmapAsync() and set my logic.

image

This part differs from the tutorial since I'm saving it to bmpCachePath instead of cachePath. You'll see why later.

And in my AlbumViewModel, we set the logic to load our covers.

image

I create the cover as a new ReactiveProperty and make my LoadCover() function async.

And in my MusicStoreViewModel, I add an async loadCover() function.

image

Don't worry about the cancellationToken yet, we'll get to it!

First we set our cancellation token.

image

And then add the cancellation logic to our doSearch() function.

image

loadCovers() is an async function, so don't forget to start it!

AvaloniaSearchOasis

Buy Music Command

Now that we have the album search working, we need to bind the music we buy to a collection. Follow along with the original

View on GitHub
GitHub Stars5
CategoryDevelopment
Updated27d ago
Forks0

Languages

F#

Security Score

70/100

Audited on Mar 9, 2026

No findings