(avalonia-gui-framework)=
# Avalonia GUI framework

Follow this tutorial to install and run your first graphical user interface (GUI).

## GUI templates

GUI applications typically require a lot of starter code and files.  Templates ease GUI development. Let us install Avalonia templates.

1. Open any existing project or create a new one.
1. On the tool window bar on the left click  {{TerminalIcons_icons_expui_toolwindow_terminal_20x20}} (Terminal).
1. Run the following command.

If your operating system terminal cannot run the `dotnet` command, then use a terminal inside your code editor.

```sh
dotnet new install Avalonia.Templates
```
You should get an output similar to:
```text
Welcome to .NET ...!
---------------------
SDK Version: ...

...

The following template packages will be installed:
   Avalonia.Templates

Success: Avalonia.Templates::... installed the following templates:
Template Name       Short Name                 Language  Tags                                     
------------------  -------------------------  --------  -----------------------------------------
Avalonia .NET App   avalonia.app               [C#],F#   Desktop/Xaml/Avalonia/Windows/Linux/macOS

...

The following templates might not work because their constraints are not met:
Avalonia TemplatedControl (avalonia.templatedcontrol) C#(identity: Avalonia.TemplatedControl)
   Project capabiltities: No project was found at the path: /home/user/. This template can only be created inside the project.
...
```

[You can ignore](https://github.com/AvaloniaUI/avalonia-dotnet-templates/discussions/284) the last error about `No project was found at the path`.

`dotnet new` command installs project templates. Now we installed project templates for the Avalonia GUI framework that we can use upon creating a new project.

## Rider Avalonia Plugin

This plugin provides GUI preview during code development.

1. On the welcome screen, click `Configure`
1. Click `Plugins`. `Settings` window will pop up with `Plugins` item and `Installed` tab being active.
1. Click `Marketplace` tab.
1. Search for `avalonia`.
1. Install `AvaloniaRider`.
1. Restart your IDE.

If you had problems, look into the official instructions [here](https://docs.avaloniaui.net/docs/reference/jetbrains-rider-ide/jetbrains-rider-setup#install-the-avalonia-plugin).

(creating-a-gui-application)=
## Creating a GUI application

1. On the welcome screen, click `New Solution`.
1. On the left, in `Custom Templates`, click `Avalonia .NET app`.

   On the right, you should see the settings for project creation.

1. Activate `Put solution and project in the same directory`.
1. You can leave the defaults for the solution and project name.
1. Click `Create`.

   You should see a new window with `MainWindow.axaml.cs` and `MainWindow.axaml` files open.
   
1. Run your project using <kbd>F5</kbd>.

   You should see a new window opening with `Welcome to Avalonia!`.
   
1. Close this window to quit your application.

## Changing how the GUI looks like

1. Click `MainWindow.axaml`.

   You should see some code. This code describes the layout of a window and data shown on it.

1. :::{figure} ../img/avalonia-editor-icons.png
   :name: avalonia-editor-icons
   :align: right
   :figwidth: 35%
   `Editor and Preview` icon activated on the `.axaml` editor.
   :::

   Preview the code using the `Editor and Preview` icon as shown in {numref}`avalonia-editor-icons`.
   
   You should see the same window as when you last ran your program.
   
1. Change the text, e.g., `Hi 👋`.

   :::{tip}
   To use another emojis in your code, refer to {ref}`typing-emojis`.
   
`Mainwindow.axaml` is a AXAML file. AXAML is an Avalonia-specific *XAML* file.

:::{wpd} XAML
{#XAML}
Extensible Application Markup Language (XAML) is a XML-based language that can be used to define user interface elements, data binding and events.
:::

:::{wpd} XML
Extensible Markup Language (XML) is a markup language for storing, transmitting and reconstructing data.
:::
:::{wpd} markup language
A code (text-encoding system) which specifies the *structure* and *formatting* of a document.

For example, HTML and Markdown.
:::

In the following we will refer to AXAML file as *XAML* in short.

## Template files and their purpose

We have several files in our template. Let us look at the files relevant to implement our ideas:

- `MainWindow.axaml`: We already discussed about this.
  - Under the `axaml` `MainWindow.axaml.cs`: This is called the [*code-behind*][code-behind] file. This is the C# code, where we:
    - describe how a window reacts to user events like mouse clicks
    - store or get data relevant to the window
    - setup the connection between the variables in AXAML file and the C# code.
1. `Program.cs`: This is the main program that initialized the GUI system. We will typically not touch this file.


## Controlling GUI data from the code-behind

Currently, data shown on our GUI is static. Typically the GUI will show data from a database or logic written in our program code. In the following we will reference a GUI element in the code-behind file make its content dynamic.

1. In the XAML, delete the greeting message and add the following instead:

   ```xml
   <TextBlock Name="GreetingTextBlock"/>
   ```
   
   This user interface element is a [`TextBlock`][textblock]. `TextBlock` is one of several [built-in control elements][built-in-control] the Avalonia framework provides to us.
   
   We gave it a `Name` to reference it in the code-behind.
   
1. Switch to code-behind and add the following line under `InitializeComponent()`:

   ```cs
   GreetingTextBlock.Text = "What did make you 😀 happy today?";
   ```
1. Run your code.

   You should see the text that you set in code-behind. 
   
## A dynamic data example – a diary prompter
Our example is still static. Let us make it more dynamic by sketching an interactive diary program that prompts the user with random diary suggestions:

1. Replace the code we wrote in the code-behind with the following:

   ```cs
    string[] basicEmotions =[
        "😀 happy",
        "😢 sad",
        "😡 angry",
        "😱 surprised",
        "🤢 disgusted",
        "😨 fear"
    ]
    ;
    var randomEmotion = Random.Shared.GetItems(basicEmotions, 1)[0];
    greetingTextBlock.Text = $"What made you {randomEmotion} today?";
   ```
1. `Random`{l=cs} will be red, because this identifier is not defined as default. Click it and use <kbd>Ctrl</kbd><kbd>.</kbd> to fix it automatically by selecting `Import missing references in file`.  

1. Run your code.

   Each time you run it, you will see another prompt.

In the code above, we define an array of basic emotion strings and then choose one using the random item chooser [`Random.Shared.GetItems`][system.random.getitems]. This function takes an array and chooses `n` random elements and returns these `n` elements as an array. In our case `n` is 1. Therefore, we have to select this only element using the index `0`.
   
(getting-input-from-the-gui)=
## Getting input from the GUI

Now we can output data to the user, now we will integrate the user's input to our program logic.

Let us put up a button that changes the prompt. Browse the [built-in controls][built-in-controls] and search for a button and come back.

---

You probably landed on the [`Button`][controls.buttons.button] like me.

GUI programs typically handle user inputs by executing up a function whenever the user interacts with the GUI, e.g., a mouse click. Using this event, we can react to the user in our program logic.

First we will add the control to the XAML and then the corresponding function to the code-behind.

1. Let us use the example from the [`Button` example](https://docs.avaloniaui.net/docs/reference/controls/buttons/button#example) and modify it for example as follows and add to our XAML like this:

   ```xml
   <Button Click="ClickHandler">Change prompt</Button>
   <TextBlock Name="GreetingTextBlock"/>
   ```
   `ClickHandler` will react to user's click. Text between the `> <` is button's text content.
   
   After adding it, you should get an error about:
   ```text
   Property Content is set more than once
   ``` 
   The `Window` element allows only a single object under it, but we have two objects. We have to put these elements into a *container* element that supports many objects. These are design decisions made by the designers of Avalonia and we have to adhere to that.
   
1. Change the lines as follows:

   ```xml
   <StackPanel>
   <Button Click="ClickHandler">Change prompt</Button>
   <TextBlock Name="greetingTextBlock"/>
   </StackPanel>
   ```

   The preview should show you the new layout. 
   :::{card} Troubleshooting
   If the preview is frozen:
   1. try the {{refresh}} icon on the top right of the preview window.
   2. run your project
   3. make sure your XAML does not have any errors.
   :::
   
   You will see <code><span style="color:red;">ClickHandler</span></code> (red), because this symbol is not defined. 
1. Click the red symbol and use <kbd>Ctrl</kbd><kbd>.</kbd> to `Create event handler`.

   You will be sent to the code-behind and you will see a new function called `ClickHandler`{l=cs}.
   
1. The content of this function will be executed whenever the user interacts with the button. We will now create a new prompt whenever the user clicks the button. So **move** code from the `MainWindow()` function to the `ClickHandler` as follows:

   ```xml
    private void ClickHandler(object? sender, RoutedEventArgs e)
    {
        string[] basicEmotions =[
                "😀 happy",
                "😢 sad",
                "😡 angry",
                "😱 surprised",
                "🤢 disgusted",
                "😨 fear"
            ]
            ;
        var randomEmotion = Random.Shared.GetItems(basicEmotions, 1)[0];
        GreetingTextBlock.Text = $"What made you {randomEmotion} today?";
    }
   ```
1. Run your program. You should see a new prompt each time you click the button.

## Visual XAML editor

The Avalonia plugin for Rider does not feature where you can design your GUI with by dragging and dropping visual elements. When you first sketch your UI, a visual drag-and-drop UI designer is very helpful. You can use the following web-based editor for this purpose:

<https://xaml.io>

After editing, **only copy** the `<Grid/>` component that is included under the `<Page/>`, because this XAML editor is designed for Visual Studio.

Most of the controls in this web-based editor is available in Avalonia, but not every. Moreover, the design may look slightly differently in Avalonia. If you want to see interactive examples of Avalonia UI elements, take a look at [Avalonia UI playground](https://play.avaloniaui.net/).

I recommend using `xaml.io` only for initial design and preview, because the web-based editor may add many unnecessary attributes that may overcomplicate the XAML and lead to errors. Writing XAML manually may train you to write clean XAML with meaningful structure using grids (instead of manually placed UI elements.)

:::{tip}
Use `Layout` elements for the UI layout, e.g., use a `StackPanel` to evenly distribute `Buttons` in an area. This helps to keep symmetry in your user interface.
:::

(upgrading-net-templates)=
## Upgrading .NET templates

:::{margin}
Projects created using `dotnet new` should automatically check for template updates according to `dotnet new --help`. Rider does not do this automatically, so this is a workaround.
:::

Templates installed using `dotnet` are typically in steady development, so the template you have installed today may have a version mismatch with a package you install today, which leads to an error in the IDE. If this happens, then you can upgrade templates as follows:

1. Open a new project based on the Avalonia template.

   Click {{nuget}}. A template is based on package/s. You should see Avalonia packages with version numbers. Grey numbers indicate the current project number. The turquoise numbers on the right show the latest version. If they are higher, then the template can be updated,
1. Open a terminal in the IDE and run the following command:

   ```text
   dotnet new update
   ```

   This upgrade only upgrades the template, so only new projects will get new versions of the packages included in the template. If you want to upgrade packages in an existing project, follow section {ref}`upgrading-net-packages-in-the-project`.
1. Close the project and create a new one and check the version numbers.

   If nothing has changed, then [the repository for templates](https://github.com/AvaloniaUI/avalonia-dotnet-templates) may not have been updated. Follow {ref}`upgrading-net-packages-in-the-project` to manually update the packages in your project and try updating the templates after ~one week.


(upgrading-net-packages-in-the-project)=
## Upgrading .NET packages in the project

Note that each project can have different versions of packages installed.

::::{tab-set}

:::{tab-item} On the IDE
1. Open your project.
1. On the tool window bar on the left click NuGet {{nuget}}.
1. Click {{nuget_upgrade}} (`Upgrade packages in solution`). A new window should pop up.
1. Click `Upgrade`.
:::

:::{tab-item} Using terminal
```{warning}
Only works for > .NET 10
```
1. Open your project.
1. On the tool window bar on the left click  {{TerminalIcons_icons_expui_toolwindow_terminal_20x20}} (Terminal).
1. Use command:

   ```sh
   dotnet package update
   ```
:::
::::

## Appendix

For this tutorial I used the following documentation. Peek into them if you run into errors.

- [Avalonia documentation](https://docs.avaloniaui.net/docs/welcome)
  - [JetBrains Rider Setup](https://docs.avaloniaui.net/docs/reference/jetbrains-rider-ide/jetbrains-rider-setup)
  - [Test-drive](https://docs.avaloniaui.net/docs/get-started/test-drive)
  - [Avalonia XAML](https://docs.avaloniaui.net/docs/basics/user-interface/introduction-to-xaml)
  - [Code-behind][code-behind]
  - [`TextBlock`][textblock]
- [Random.GetItems method][system.random.getitems]
  
[code-behind]: https://docs.avaloniaui.net/docs/basics/user-interface/code-behind
[textblock]: https://docs.avaloniaui.net/docs/reference/controls/textblock
[built-in-controls]: https://docs.avaloniaui.net/docs/reference/controls/
[system.random.getitems]: https://learn.microsoft.com/en-us/dotnet/api/system.random.getitems
[controls.buttons.button]: https://docs.avaloniaui.net/docs/reference/controls/buttons/button