Skip to content

Commit

Permalink
(#209) Fixed the MAUI view model and rendering code (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianhall authored Jan 31, 2025
1 parent e418536 commit c2bc2e8
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 41 deletions.
8 changes: 7 additions & 1 deletion docs/content/samples/todoapp/maui.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ Before you begin adjusting the application for offline usage, you must [deploy a

## Update the application for datasync operations

All the changes are isolated to the `Database/AppDbContext.cs` file.
All the changes are isolated to the `Models/AppDbContext.cs` file. You can change the definition of `OFFLINE_SYNC_ENABLED` at the top of the file to make all the changes.

```csharp
#define OFFLINE_SYNC_ENABLED
```

The following changes are made to the database context:

1. Change the definition of the class so that it inherits from `OfflineDbContext`:

Expand Down
41 changes: 24 additions & 17 deletions samples/todoapp/TodoApp.MAUI/MainPage.xaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:TodoApp.MAUI.Models"
xmlns:viewmodels="clr-namespace:TodoApp.MAUI.ViewModels"
x:DataType="viewmodels:MainViewModel"
x:Class="TodoApp.MAUI.MainPage"
Title="TodoApp">
<ContentPage
x:Class="TodoApp.MAUI.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:TodoApp.MAUI.Models"
xmlns:viewmodels="clr-namespace:TodoApp.MAUI.ViewModels"
Title="TodoApp"
x:DataType="viewmodels:MainViewModel">
<NavigationPage.TitleView>
<StackLayout Style="{StaticResource titleViewContainer}">
<Label Style="{StaticResource titleViewLabel}">TodoApp</Label>
<ImageButton Style="{StaticResource titleViewRefreshIcon}" Command="{Binding RefreshItemsCommand}" />
<ImageButton Command="{Binding RefreshItemsCommand}" Style="{StaticResource titleViewRefreshIcon}" />
</StackLayout>
</NavigationPage.TitleView>

<Grid AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" BackgroundColor="Azure">
<Grid
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
BackgroundColor="Azure">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
Expand All @@ -22,8 +26,8 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<!-- List of items -->
<ListView ItemsSource="{Binding Items}" ItemTapped="OnListItemTapped">
<!-- List of items -->
<ListView ItemTapped="OnListItemTapped" ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:TodoItem">
<ViewCell>
Expand All @@ -37,23 +41,26 @@
</Grid.ColumnDefinitions>

<Label Style="{StaticResource listItemTitle}" Text="{Binding Title}" />
<Image Grid.Column="1" IsVisible="{Binding IsComplete}" Style="{StaticResource listItemIcon}" />
<Image
Grid.Column="1"
IsVisible="{Binding IsComplete}"
Style="{StaticResource listItemIcon}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

<!-- Entry box -->
<!-- Entry box -->
<Frame Grid.Row="1" Style="{StaticResource roundedCornerFrame}">
<HorizontalStackLayout>
<Image Style="{StaticResource addItemIcon}" />
<Entry
x:Name="addItemEntry"
Style="{StaticResource addItemEntry}"
<Entry
x:Name="addItemEntry"
Placeholder="Enter Todo Item Text"
ReturnCommand="{Binding AddItemCommand}"
ReturnCommandParameter="{Binding Source={x:Reference addItemEntry}}" />
ReturnCommandParameter="{Binding Text, Source={x:Reference addItemEntry}}"
Style="{StaticResource addItemEntry}" />
</HorizontalStackLayout>
</Frame>
</Grid>
Expand Down
4 changes: 2 additions & 2 deletions samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ public MainPage()
protected override void OnAppearing()
{
base.OnAppearing();
this._viewModel.RefreshItemsCommand.Execute();
this._viewModel.RefreshItemsCommand.Execute(null);
}

public void OnListItemTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item is TodoItem item)
{
this._viewModel.UpdateItemCommand.Execute(item);
this._viewModel.UpdateItemCommand.Execute(item.Id);
}

if (sender is ListView itemList)
Expand Down
53 changes: 33 additions & 20 deletions samples/todoapp/TodoApp.MAUI/Models/AppDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,50 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#undef OFFLINE_SYNC_ENABLED

using CommunityToolkit.Datasync.Client.Http;
using CommunityToolkit.Datasync.Client.Offline;
using Microsoft.EntityFrameworkCore;
using TodoApp.MAUI.Services;

namespace TodoApp.MAUI.Models;

#if OFFLINE_SYNC_ENABLED
public class AppDbContext(DbContextOptions<AppDbContext> options) : OfflineDbContext(options)
#else
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
#endif
{
public DbSet<TodoItem> TodoItems => Set<TodoItem>();

//protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder)
//{
// HttpClientOptions clientOptions = new()
// {
// Endpoint = new Uri("https://YOURSITEHERE.azurewebsites.net/"),
// HttpPipeline = [new LoggingHandler()]
// };
// _ = optionsBuilder.UseHttpClientOptions(clientOptions);
//}
#if OFFLINE_SYNC_ENABLED
protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder)
{
HttpClientOptions clientOptions = new()
{
Endpoint = new Uri("https://YOUR_SITE_HERE.azurewebsites.net/"),
HttpPipeline = [new LoggingHandler()]
};
_ = optionsBuilder.UseHttpClientOptions(clientOptions);
}
#endif

public async Task SynchronizeAsync(CancellationToken cancellationToken = default)
{
//PushResult pushResult = await this.PushAsync(cancellationToken);
//if (!pushResult.IsSuccessful)
//{
// throw new ApplicationException($"Push failed: {pushResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}");
//}

//PullResult pullResult = await this.PullAsync(cancellationToken);
//if (!pullResult.IsSuccessful)
//{
// throw new ApplicationException($"Pull failed: {pullResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}");
//}
#if OFFLINE_SYNC_ENABLED
PushResult pushResult = await this.PushAsync(cancellationToken);
if (!pushResult.IsSuccessful)
{
throw new ApplicationException($"Push failed: {pushResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}");
}

PullResult pullResult = await this.PullAsync(cancellationToken);
if (!pullResult.IsSuccessful)
{
throw new ApplicationException($"Pull failed: {pullResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}");
}
#endif
}
}

Expand Down
43 changes: 43 additions & 0 deletions samples/todoapp/TodoApp.MAUI/Services/LoggingHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;

namespace TodoApp.MAUI.Services;

/// <summary>
/// A delegating handler that logs the request/response to stdout.
/// </summary>
public class LoggingHandler : DelegatingHandler
{
public LoggingHandler() : base()
{
}

public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Debug.WriteLine($"[HTTP] >>> {request.Method} {request.RequestUri}");
await WriteContentAsync(request.Content, cancellationToken);

HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

Debug.WriteLine($"[HTTP] <<< {response.StatusCode} {response.ReasonPhrase}");
await WriteContentAsync(response.Content, cancellationToken);

return response;
}

private static async Task WriteContentAsync(HttpContent? content, CancellationToken cancellationToken = default)
{
if (content != null)
{
Debug.WriteLine($"[HTTP] >>> {await content.ReadAsStringAsync(cancellationToken)}");
}
}
}

6 changes: 5 additions & 1 deletion samples/todoapp/TodoApp.MAUI/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@

using CommunityToolkit.Datasync.Client;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.EntityFrameworkCore;
using TodoApp.MAUI.Models;
using TodoApp.MAUI.Services;

namespace TodoApp.MAUI.ViewModels;

public class MainViewModel(AppDbContext context, IAlertService alertService) : ObservableRecipient
public partial class MainViewModel(AppDbContext context, IAlertService alertService) : ObservableRecipient
{
[ObservableProperty]
private bool _isRefreshing = false;

[ObservableProperty]
private ConcurrentObservableCollection<TodoItem> items = [];

[RelayCommand]
public async Task RefreshItemsAsync(CancellationToken cancellationToken = default)
{
if (IsRefreshing)
Expand All @@ -41,6 +43,7 @@ public async Task RefreshItemsAsync(CancellationToken cancellationToken = defaul
}
}

[RelayCommand]
public async Task UpdateItemAsync(string itemId, CancellationToken cancellationToken = default)
{
try
Expand All @@ -60,6 +63,7 @@ public async Task UpdateItemAsync(string itemId, CancellationToken cancellationT
}
}

[RelayCommand]
public async Task AddItemAsync(string text, CancellationToken cancellationToken = default)
{
try
Expand Down

0 comments on commit c2bc2e8

Please sign in to comment.