# Apprentice Dev Blog: Weeks 7 and 8 - .NET Maui and MVVMS
> As we come to the end Alex's time with us, they took some time to clean things up and look at James Montemagno's MVVMS architecture.

*11 July 2022*


The cover image for this post is by [Domenico Loia](https://unsplash.com/photos/hGV2TfOh0ns)


---

This blog post was written by Alex..  Alex is our apprentice for 2022, and this will be their final dev blog entry with us.


Jamie here - to jump directly to Alex's log for this week, click [here](#alex's-devlog)

I wanted to hold off publishing the penultimate dev blog by Alex

remember that Alex is NOT their real name


as the final few weeks of their project have been largely clearing things up and looking at design patterns. But this week marks the final one that we'll be spending with Alex, and I wanted to let them talk about how they've been wrapping things up.

---

## Alex's Devlog


Hi, I'm Alex and am currently studying a Digital T-Level course, am taking my industry experience with RJJ Software.

Part of these final few weeks has been spent cleaning up the code base (removing unused NuGet packages, optimising some code, etc.), and getting the final few parts of functionality in place.

When I started this project Jamie had told me not to expect to complete it. He'd said that this wasn't meant to be a mark against me or my abilities, but more because he wanted to cover a lot of things that they didn't have much time to cover on the T-Level course, and that we wouldn't have the time to do that _and_ complete the project. Some of these things where:

- [SOLID](https://en.wikipedia.org/wiki/SOLID)
- git and source control
- Unit testing
- .NET Maui

This last item was really quite a large ask, because .NET Maui was released two weeks into my time at RJJ, which meant that it was so new that the documentation hadn't caught up yet. Thankfully, Jamie was able to share two video series with me which helped make things make sense:

- [Gerald Versluis' .NET Maui Crash Course](https://www.youtube.com/watch?v=mgW6xviirQk&list=PLfbOp004UaYVt1En4WW3pVuM-vm66OqZe)
- [James Montemagno's Learn .NET MAUI course](https://www.youtube.com/watch?v=DuNLR_NJv8U)

These are indispensible resources to anyone who is new to .NET Maui, and my project was built primarily from these two sources. Whenever Jamie wasn't available to help

Jamie here: this wasn't that often, but I sometimes had other responsibilities to take care of


I would consult my notes from these two resources.

That being said, there were a few times that boh Jamie and I were caught out, specifically around events and two-way binding and commands in .NET Maui.

## Binding Commands


As an example, all buttons have a `Command` property and these need to be mapped to a method which is decorated with the `[CommandRelay]` attribute. But something that we forgot a few times is that, regardless of you call the method in the code-behind, you have to refer to is with the `Command` suffix in the XAML `Command` property. Here's some code illustrating it:

```xml
<VerticalStackLayout Spacing="5">
    <StackLayout Grid.Row="1" Grid.Column="1">
        <Button
            Text="View"
            TextColor="White"
            FontAttributes="Bold"
            HorizontalOptions="Center"
            VerticalOptions="Center"
            HeightRequest="60"
            WidthRequest="150"
            BackgroundColor="MediumSlateBlue"
            Command="{Binding GetClientsCommand}"
            IsEnabled="{Binding IsNotBusy}"
            x:Name="btn_View"/>
    </StackLayout>
</VerticalStackLayout>
```

The important bit in the above code is `Command="{Binding GetClientsCommand}"`. This tells .NET Maui to call the `GetClients` command on the relevant model, which in our case looks like this:

```csharp
// Note the RelayCommand attribute
[RelayCommand]
// Also note the lack of "Command" in the name
public void GetClients()
{
    if (IsBusy)
    {
        return;
    }
    IsBusy = true;

    // call the relevant service and set up the clients list on the ViewModel
    
    IsRefreshing = false;
    IsBusy = false;
}
```

Yes the above code is synchronous, but it's serving a point rather than being an example of a best practice. The things that Jamie and I slipped up with here were:

- Not adding the `[RelayCommand]` attribute to the method
- Forgetting that the convention in the binding was to add the `Command` suffix

## Two Way Binding


Having missed out on XAML when it was first introduced in WPF and WinUI, Jamie came to XAML with an HTML mindset.

Jamie here: it's true. It also had it stuck on my head that XAML would do two-way binding automatically for us. Which I was wrong about


Having worked with things like React and [Knockout](https://knockoutjs.com/) (which take care of two-way binding), he had become convinced that two-way mapping was handled automatically. But that wasn't the truth, as it works a _LITTLE_ like WinForms - in that you need to tell .NET Maui/XAML that you want to call back when a value has changed.

Let's say that you have a text box that you want to map to a property on your ViewModel:

```xml
<StackLayout Grid.Row="5" Grid.Column="1">
    <Entry
        Placeholder="Enter here..."
        PlaceholderColor="White"
        TextColor="White"
        FontSize="16"
        Keyboard="Numeric"
        TextChanged="txt_lineItemCost_TextChanged"
        HorizontalOptions="Center"
        VerticalOptions="Center"
        x:Name="txt_lineItemCost">
    </Entry>
</StackLayout>
```

In the XAML above, we have a text input box called `txt_lineItemCost` which isn't bound to a value, but we have set up a binding to a method called `txt_lineItemCost_TextChanged` when the value changes (i.e. when the user types into the text box).

This method looks a little like the following:

```csharp
private void txt_lineItemCost_TextChanged(object sender, TextChangedEventArgs e)
{
    var newValue = txt_lineItemCost.Text;
    if (CheckEmpty(newValue) && TryParseToDouble(newValue))
    {
        ((InvoiceGeneratorViewModel)this.BindingContext)._lineItemVm.Cost = Convert.ToDouble(newValue);
    }
}
```

Where `CheckEmpty` is a method which wraps around `string.IsNullOrWhiteSpace` for readability and [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) reasons.

---

Jamie here.

One thing to note is the use of the `Keyboard="Numeric"` in the XAML. This ensures that the keyboard shown to the user in a context where they will be served a software keyboard (i.e. a mobile phone, a tablet, or in an accessibility situation) that they will be given a numeric keyboard. This will stop the user from entering non-numerical values in those contexts.

However, it will not stop the user outside of that context from entering arbitrary text (i.e. in a desktop application where a hardware keyboard is used). Just something to be aware of.

---

## MVVMS


This is a wonderful design pattern that James Montemagno introduced in the following video:

[Watch on YouTube](https://www.youtube.com/watch?v=ve0DFu-arD8)


He admits that it's not an "official" design pattern, and that it's "just" MVVM with a service added in." I've been using this pattern throughout the development of this app without really knowing it. MVVMS stands for:

- Model
- View
- View-Model
- Service

It's a relatively simple pattern to follow in that you continue to use the MVVM pattern, but inject one of more services to use. James says that he prefers to inject concrete implementations of services (i.e. classes) rather than interfaces, but concedes that it's also possible (and might be preferable, depending on your use-case) to use interfaces instead.

The example that James gives is if you have a Model which uses an instance of the `HttpClient`. In that instance, it _could_ be cleaner to inject a class which wraps around the `HttpClient`. That way, the Model doesn't need to be concerned with the details of how the `HttpClient` class is set up

Jamie here: something something Single-responsibility Principle


And it becomes almost trivial to change in the future.

Suppose you decide that you want to move away from the `HttpClient` class because you're no longer talking to a WebApi using Http and that you decide to move over to gRPC or some other technology. You only need to change the implementation of your service, rather than having to change all of your Models. The Model only needs to know about the method that it's calling, the arguments, and it's return type.

Let's see that in code:

```csharp
public class SuperAmazingModel
{

    private readonly IClientService _clientService;
    
    public SuperAmazingModel (IClientService clientService)
    {
        _clientService = clientService;
    }
    
    // Note the RelayCommand attribute
    [RelayCommand]
    // Also note the lack of "Command" in the name
    public void GetClients()
    {
        if (IsBusy)
        {
            return;
        }
        IsBusy = true;
    
        var clients = _clientService.GetPagedClients();
        
        IsRefreshing = false;
        IsBusy = false;
    }
}

public interface IClientService
{
    Paged<ClientViewModel> GetPagedClients(int pageNumber, int perPage);
}

public class ClientService : IClientService
{
    private readonly HttpClient _httpClient;

    public ClientService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public Paged<ClientViewModel> GetPagedClients(int pageNumber, int perPage)
    {
        // Do something with _httpClient_
    }
}
```

And when we migrate away from `HttpClient`, we only need to change the `ClientService` because `SuperAmazingModel` doesn't need to know about how classes which inherit from `IClientService` work, just that they return a `Paged<ClientViewModel>` when `GetPagedClients` is called with two ints.

## In Closing


I've really enjoyed my time with RJJ and have had experience playing with some bleeding edge technologies whilst working alongside Jamie. Working with him on building something with .NET Maui has given me a legitimate chance to play around with some technology that I don't think I'd have had the chance to otherwise.

I've also picked up a lot of information and skills along the way which should help me in my career. From SOLID to DRY vs WET ("write everything twice", the opposite of DRY), and from git and [gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) to a little on [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) and Domain-Driven design. Some of these points have been shared during pair programming sessions, and some where shared during downtime between sessions - whilst taking a break or getting lunch, for instance.

There's a lot to learn with .NET Maui, and a few things which caused both Jamie and me to "fall into the pit of failure", but that was mostly based on our misunderstanding. Most of it is actually designed to let you "[fall into the pit of success](https://blog.codinghorror.com/falling-into-the-pit-of-success/)," which has been refreshing. In some ways .NET Maui is completely different to WinForms, yet very similar in others.

---

Jamie again.

I'm incredibly please with Alex's progress throughout their time with us. I knew going into the project that we wouldn't leave it with a full functional app. But what we _do_ have is a cross-platform application which scales from the desktop down to a mobile phone - it will scale down to a smart watch too, but we didn't have a chance to try that out. The code is hosted in GitHub, it has a CI/CD build pipeline, which produces:

- A Windows binary
- An iOS app
- An Android App
- A MacOS desktop app

Each of which can be downloaded and installed onto a device of your choice.

The application was built using gitflow and PRs, meaning that we could both review each others work as the features for the application was built up. This gave both Alex and myself a chance to learn from each other.

it's also worth noting that Alex taught me a few things about .NET Maui thorugh [lunch-and-learn](https://blog.hubspot.com/service/lunch-and-learn) sessions.


The application communicates with a series of WebAPIs using the `HttpClient` class, and uses dependency injection to inject a service into each relevant Model - meaning that we only have one place where the `HttpClient` will need to be updated, should it ever need to be updated.

The WebApis communicate with several SQL databases and use Entity Framework Core to do that communication. We haven't had much of a chance to talk about this part of the application, but I'm planning a companion piece which talks about the full stack.

I'm incredibly excited to see where Alex's career takes them, and I'm hopeful that their time with us has been productive, educating, and exciting.

