.NET MAUI – Forget Me Not – Part 4

This is part 4 in an ongoing series in which I will build and dissect a non-trivial app. For details, please see the first in this series.

Part 3 ended with a teaser about the Preferences Page. As you’ll remember, we start with the Preference Model class:

namespace ForgetMeNot.Model;

[ObservableObject]
public partial class Preference
{
    [ObservableProperty] private string preferencePrompt;
    [ObservableProperty] private string preferenceValue;

}

I explained about ObservableProperties, so I won’t review that here. Let’s see how this model class is used. There are two important related classes: Preferences.xaml and PreferencesViewModel.cs

The Preferences View

The view/page starts with a label explaining that you can add as many types of preferences as you like. We start you with a set of preference prompts (e.g., favorite music, favorite books, etc.) but you are not only free to add your own, you are free to modify ours. All the fields, both prompt and value are free form editable fields.

We’ll start with a couple styles

<Style x:Key="PreferenceStyle" TargetType="Entry">
    <Setter Property="BackgroundColor" Value="AntiqueWhite" />
    <Setter Property="HorizontalOptions" Value="Start" />
    <Setter Property="VerticalOptions" Value="Center" />
    <Setter Property="FontSize" Value="10" />
    <Setter Property="TextColor" Value="Black" />
    <Setter Property="VerticalTextAlignment" Value="Center" />
    <Setter Property="WidthRequest" Value="400" />
</Style>

This first one should be pretty straightforward if you are a Xamarin.Forms programmer. Our Entries will use this style. The second one may be a bit new, even though VisualStates are now available in XF.

<Style TargetType="Entry">
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="White" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Focused">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="Wheat" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

The short version of this is that the appearance of the entry will change based on the “state” of the entry. If it is in normal state, the background is white, if it is has focus, however, the background is wheat.

The body of the page has a label that explains how the page works, and a button to save your preferences (the button is at both the top and bottom of the page for the user’s convenience). This is all followed by a CollectionView. Let’s focus on that:

<CollectionView
    Margin="20,20,10,10"
    ItemsSource="{Binding Preferences}"
    SelectionMode="None">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid ColumnDefinitions="*,2*">
                <Entry
                    Grid.Column="0"
                    FontSize="10"
                    HorizontalOptions="Start"
                    HorizontalTextAlignment="Start"
                    Text="{Binding PreferencePrompt, Mode=TwoWay }"
                    TextColor="{OnPlatform Black, iOS=White}" />
                <Entry
                    Grid.Column="1"
                    FontSize="10"
                    HeightRequest="32"
                    HorizontalOptions="Start"
                    HorizontalTextAlignment="Start"
                    Text="{Binding PreferenceValue, Mode=TwoWay}"
                    TextColor="{OnPlatform Black, iOS=White}" 
                    WidthRequest="350" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

The ItemsSource for the collection view is a property in the ViewModel. The DataTemplate defines that each entry in the ItemsSource collection will be displayed in a pair of Entry fields. The first will use the item’s PreferencePrompt property and the second will use the PreferenceValue property. Again, all of this will be very familiar if you are an XF programmer. In fact, most of what you’ll see in MAUI will be very familiar — .NET MAUI really is the next incarnation of Xamarin.Forms.

The save button invokes the SavePreferencesCommand, which we’ll also see in the ViewModel.

The ViewModel

PreferencesViewModel has an ObservableProperty and RelayCommands so we’ll mark it as an ObservableObject

[ObservableObject]
public partial class PreferencesViewModel
{
    [ObservableProperty] private List<Preference> preferences;

    public PreferencesViewModel()
    {
        Preferences = PreferencesService.GetPreferences();
    }

    [RelayCommand]
    private async Task SavePreferencesAsync()
    {
        PreferencesService.Save(preferences);
    }
    
}

Nothing surprising or new here except the RelayCommand and the contents of the constructor.

The RelayCommand attributes marks SavePreferencesAsync() as the method called by the SavePreferencesCommand command. The naming is by convention and that is how MAUI associates the bound command in the XAML with the implementing method in the ViewModel. Again, a huge reduction in boilerplate code.

The constructor sets Preferences (which as you see is a list of Preference objects) by invoking GetPreferences on the PreferenceService.

GetPreferences will return a list of Preference objects by going to the client API service. For now, I’m just mocking that the easy way:

 public static  List<Preference> GetPreferences()
 {
     return GetPreferencesMock();
 }

GetPreferencesMock does not use a “real” mocking object, it just spins up a few preferences and returns them in a list.

private static List<Preference> GetPreferencesMock()
{
    List<Preference> preferences  = new();
    Preference preference = new Preference
    {
        PreferencePrompt = "Shirt size",
        PreferenceValue = "XXL"
    };
    preferences.Add(preference);

    preference = new()
    {
        PreferencePrompt = "Pants size",
        PreferenceValue = "40"
    };
    preferences.Add(preference);

Not elegant, I admit, but it works and it is temporary. Once we have the API we’ll junk the Mock (or comment it out in case we need it for testing later).

We’re using tabs in this app, and when you click on the Preferences tab you go to the Preferences page:

Note that the user can fill in any answer they want in response to the prompt, and they can even edit the prompt!

Unknown's avatar

About Jesse Liberty

** Note ** Jesse is currently looking for a new position. You can learn more about him at https://jesseliberty.bio Thank you. Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET, is now available wherever you buy your books. Liberty was a Team Lead and Senior Software Engineer for various corporations, a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a 13 year Microsoft MVP.
This entry was posted in Essentials. Bookmark the permalink.

80 Responses to .NET MAUI – Forget Me Not – Part 4

  1. Right now it appears like Movable Type is the top blogging platform out there right now. (from what I’ve read) Is that what you’re using on your blog?

  2. Can I simply just say what a comfort to discover somebody who truly knows what they are talking about on the internet. You actually understand how to bring an issue to light and make it important. A lot more people really need to read this and understand this side of the story. I can’t believe you’re not more popular since you definitely possess the gift.

  3. Can I simply say what a relief to uncover an individual who really knows what they are discussing online. You actually realize how to bring a problem to light and make it important. More and more people should look at this and understand this side of the story. It’s surprising you’re not more popular since you most certainly have the gift.

  4. Aw, this was an exceptionally nice post. Taking the time and actual effort to generate a top notch article… but what can I say… I put things off a lot and never manage to get anything done.

  5. 乐动网站's avatar 乐动网站 says:

    At this time it sounds like BlogEngine is the best blogging platform available right now. (from what I’ve read) Is that what you’re using on your blog?

  6. May I just say what a relief to discover someone that truly knows what they are talking about over the internet. You certainly understand how to bring an issue to light and make it important. A lot more people have to check this out and understand this side of the story. I was surprised that you are not more popular given that you definitely possess the gift.

  7. Hello there, You have performed an excellent job. I’ll certainly digg it and in my opinion recommend to my friends. I’m sure they’ll be benefited from this website.

  8. Greate pieces. Keep writing such kind of information on your blog. Im really impressed by your site.

  9. Yesterday, while I was at work, my sister stole my iphone and tested to see if it can survive a thirty foot drop, just so she can be a youtube sensation. My iPad is now destroyed and she has 83 views. I know this is totally off topic but I had to share it with someone!

  10. Just want to say your article is as astounding. The clearness in your post is just nice and i can assume you’re an expert on this subject. Fine with your permission allow me to grab your feed to keep up to date with forthcoming post. Thanks a million and please continue the gratifying work.

  11. Simply wish to say your article is as astonishing. The clarity in your post is simply great and i could assume you are an expert on this subject. Fine with your permission allow me to grab your feed to keep updated with forthcoming post. Thanks a million and please keep up the rewarding work.

  12. Just wish to say your article is as amazing. The clearness in your post is just cool and i could assume you’re an expert on this subject. Well with your permission let me to grab your feed to keep up to date with forthcoming post. Thanks a million and please continue the gratifying work.

  13. Excellent post. Keep posting such kind of info on your blog. Im really impressed by your blog.

  14. Right now it seems like Expression Engine is the top blogging platform out there right now. (from what I’ve read) Is that what you are using on your blog?

  15. At this time it sounds like BlogEngine is the top blogging platform out there right now. (from what I’ve read) Is that what you’re using on your blog?

  16. Hi there, You’ve performed an incredible job. I’ll definitely digg it and individually suggest to my friends. I’m confident they’ll be benefited from this web site.

  17. binance's avatar binance says:

    Your point of view caught my eye and was very interesting. Thanks. I have a question for you.

  18. Thank you for your sharing. I am worried that I lack creative ideas. It is your article that makes me full of hope. Thank you. But, I have a question, can you help me? https://accounts.binance.com/register/person?ref=IHJUI7TF

  19. I think the admin of this website is really working hard for his site, for the reason that here every stuff is quality based information.

  20. References:

    Cherokee casino siloam springs canadiannewcomerjobs.ca

  21. 0ahukewixpkubzmjnahxdgs0khugka8kq4dudcao|side effects definition

    References:
    sciencewiki.science

  22. Your article helped me a lot, is there any more related content? Thanks!

  23. gk8login's avatar gk8login says:

    Just logged into gk8login and it’s pretty straightforward. Easy peasy to navigate, which is a huge plus in my book. If you’re looking for something user-friendly, give it a shot: gk8login

  24. pg777slot's avatar pg777slot says:

    Alright, alright, alright! Gave pg777slot a whirl and gotta say, the slots are pretty slick. Nothing groundbreaking, but a solid option for a quick spin. Check it out for yourself: pg777slot

  25. w777game's avatar w777game says:

    W777game promises a winning play time. I found a few unique games I hadn’t seen elsewhere…so they get points for originality. Check them out at w777game for more!

  26. Thanks for sharing. I read many of your blog posts, cool, your blog is very good. https://generations808.com/aging-with-aloha-caring-for-your-eyes/#comment-29367

  27. Your point of view caught my eye and was very interesting. Thanks. I have a question for you.

  28. Das im japanischen Stil gehaltene Wellnesscenter bietet
    neben verschiedenen Massagen auch Air-Bräunung, Gesichtsbehandlungen, Maniküre und Pediküre an. Nach einem Abend
    am Spieltisch bietet sich der Club Hakkasan zum ausgiebigen Feiern an. Moderne Hotelzimmer, einen riesigen Wellnessbereich, das größte Casino der Stadt, zahlreiche Restaurants, Cafés und Bars.

    Die Zimmer waren der erste Bereich des Hotels, der renoviert wurde, was mit den 160 Millionen Dollar Kosten ein weiterer großer Zug des Megahotels war.

    Das MGM Grand befindet sich am südlichen Teil des Las Vegas Strip und bietet eine hervorragende Lage für Unterhaltung, Shopping und Sightseeing.
    Das MGM Grand Hotel & Casino in Las Vegas bietet 23
    Restaurants und 7 Lounges/Bars. Eine Monorail-Station im Gebäude bietet Verbindungen zu anderen Hotels
    am Strip und zum Convention Center. Erinnerungen an das Feuer verfolgen auch heute noch die Einwohner der Wüstenstadt.
    Eure Abende könnt ihr im hoteleigenen Casino oder in den Nachtclubs des Resorts verbringen.
    Das MGM Grand bietet eine beeindruckende Auswahl an gastronomischen Einrichtungen, darunter preisgekrönte Restaurants und verschiedene Bars.
    Die berühmte Fremont Street Experience ist etwa 12 km entfernt und
    die University of Nevada in Las Vegas erreichen Sie in nur 10 Fahrminuten. Der Flughafen Harry Reid International ist nur 5,35 km entfernt, während Einkaufsmöglichkeiten und Restaurants nur wenige Gehminuten vom Resort entfernt
    sind. Das Löwen-Logo findet sich noch einmal oben am Gebäude.

    References:
    https://online-spielhallen.de/umfassende-lapalingo-casino-bewertung-ein-tiefer-einblick/

  29. Your point of view caught my eye and was very interesting. Thanks. I have a question for you.

  30. Your point of view caught my eye and was very interesting. Thanks. I have a question for you.

  31. binance's avatar binance says:

    I don’t think the title of your article matches the content lol. Just kidding, mainly because I had some doubts after reading the article. https://www.binance.com/ka-GE/join?ref=RQUR4BEO

  32. Can you be more specific about the content of your article? After reading it, I still have some doubts. Hope you can help me.

  33. Your point of view caught my eye and was very interesting. Thanks. I have a question for you.

  34. I don’t think the title of your article matches the content lol. Just kidding, mainly because I had some doubts after reading the article.

Leave a Reply

Your email address will not be published. Required fields are marked *