Appendix#

Model–view–viewmodel (MVVM)#

Warning

MVVM adds unnecessary complexity for the goals of this course. The tutorial in Presenting data on a grid builds on the basic template we used in section Creating a GUI application.

Some reasons: MVVM requires an additional class (viewmodel) for each class of the model that should be shown on the GUI, which adds additional complexity. Moreover, the model classes should send a signal to the viewmodel whenever data in the model change, which can be hard to implement for beginners.

We introduce MVVM mainly because the the framework we utilize also uses MVVM.

Model–view–viewmodel

an architectural pattern in computer software that facilitates the separation of the development of a GUI (the view) from the development of the business logic or back-end logic (the model).

Also referred as to:

  • MVVM in short.

  • model–view–binder

https://upload.wikimedia.org/wikipedia/commons/d/d5/MVVMPattern.svg

Fig. 24 Three components of MVVM. The return line from model to viewmodel is dashed because usually the viewmodel calls the model.
CC BY-SA 4.0. By Uncopy. Source: Wikimedia Commons
#

model

business logic and data

view

what the user sees and interacts with (structure, layout, controls etc)

viewmodel

abstraction (e.g., OOP code) of the view. It uses the binder to synchronize GUI elements on the view with the model.

binder

the data binding system in XAML (Binding ...) which connects the view and the viewmodel.

Remember that XAML defines both user interface elements and data-binding.

Creating an MVVM-based project#

  1. New Solution

  2. Select Avalonia .NET MVVM App from the list with the name, e.g., MVVMExample.

    After the project is opened you should see more directories compared to previous projects you created before, e.g., Models, ViewModels, Views which correspond to the MVVM components we introduced before.

  3. Run the project.

    It should show you the message Welcome to Avalonia!.

Let us analyze how this message is presented to the user.

  1. Open MainWindow.axaml and look at the TextBlock.

    You should see that Text uses a Binding to the path Greeting, which is an object in our code. But where?

    According to Fig. 24, data binding happens between the view and the viewmodel. The XAML describes the view, so Greeting must be in the viewmodel.

  2. Open MainWindowViewModel.cs.

    You should see the property Greeting initialized with the welcome message.

Integrating a DataGrid into an MVVM-based template#

  1. Make sure you have installed the DataGrid control.

  2. Open App.axaml and append the <Styleinclude line after <FluentTheme>. After the modification the Styleinclude line should look as follows:

    <Application.Styles>
        <FluentTheme />
        <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />
    </Application.Styles>
    

    We need this, because DataGrid uses additional styles compared to the standard GUI elements.

  3. Under the directory Models, create a new file called Models.cs that will contain our model classes using right-click on Models and then Add -> File.

    In C# projects, usually we create a new file for each class, but to see all related classes at once, we will put them in a single file. It will be possible to separate them later.

  4. You will be asked whether you want to add the file to Git. Add it.

  5. Paste the following into Models.cs:

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    

    Notice the get and set keywords for each field that we did not use before. These keywords introduce methods in the background that are used to get & set FirstName and LastName. We need them; otherwise the GUI framework does not show any data. For example, if you remove {get; set;} from LastName, then you will see no LastName field later when you run the program.

    We will call a field with get or set property.

    The reason for the behavior in the last paragraph could be that C# has a feature which allows to get the fields with get and set methods automatically, which in turn is used to show these data in DataGrid.

  6. After the last step, you should see that Person is gray underlined. Select the line with Person and click to choose context actions.

  7. Click Move to ... namespace.

    You will see that a namespace line is added.

    Namespaces avoid naming clashes between models, viewmodels and views. Even the probability for naming clashes is very low in a small project, let us follow the structure of other classes in our MVVM template – also the classes in ViewModels and Views have namespaces.

  8. Open MainWindow.axaml.

    Replace the TextBlock with the following:

    <DataGrid Margin="20" ItemsSource="{Binding People}" 
              AutoGenerateColumns="True" 
              GridLinesVisibility="All"
              BorderThickness="1" BorderBrush="Gray">
    </DataGrid>
    

    These lines ensure the following:

    • Binding People binds People to the DataGrid so that the data on the GUI is updated automatically, when the data changes and vice-versa.

    • AutoGenerateColumns generates columns automatically by reading the properties of a class, e.g., FirstName and LastName in Person.

    • Other four attributes including Margin configure the style.

    For more information about the attributes, refer to [DataGrid documentation][datagrid-docs].

    In the XAML, we see that the data is provided by the binding People, which must be an object in the viewmodel.

  9. Open MainWindowViewModel.cs.

    Add the following line on the top of the file:

    using System.Collections.ObjectModel;
    

    This library is required to make the ObservableCollection available. ObservableCollection is a special List that can communicate with the GUI when data in the list is modified.

    Warning

    Use ObservableCollection instead of List if you plan to present a list on the GUI.

  10. Delete the definition of the string Greeting and replace it with the following:

 public ObservableCollection<Person> People { get; }

 public MainWindowViewModel()
 {
     var people = new List<Person> 
     {
         new()
         {
             FirstName = "Melitta",
             LastName = "Lipp"
         },
         new()
         {
             FirstName = "Heilos",
             LastName = "König"
         },
         new()
         {
             FirstName = "Onur",
             LastName = "KuĹźtepe"
         }
     };
     People = new ObservableCollection<Person>(people);
 }

This method creates an example dataset to show on the grid.

  1. Run your project.

  2. Try:

    • modifying data

    • changing sort order

This control is useful for editing data structured in the instances of a class.