Skip to content

Commit c2bc2e8

Browse files
authored
(#209) Fixed the MAUI view model and rendering code (#243)
1 parent e418536 commit c2bc2e8

File tree

6 files changed

+114
-41
lines changed

6 files changed

+114
-41
lines changed

docs/content/samples/todoapp/maui.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ Before you begin adjusting the application for offline usage, you must [deploy a
2222

2323
## Update the application for datasync operations
2424

25-
All the changes are isolated to the `Database/AppDbContext.cs` file.
25+
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.
26+
27+
```csharp
28+
#define OFFLINE_SYNC_ENABLED
29+
```
30+
31+
The following changes are made to the database context:
2632

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

samples/todoapp/TodoApp.MAUI/MainPage.xaml

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
<?xml version="1.0" encoding="utf-8" ?>
2-
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3-
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4-
xmlns:models="clr-namespace:TodoApp.MAUI.Models"
5-
xmlns:viewmodels="clr-namespace:TodoApp.MAUI.ViewModels"
6-
x:DataType="viewmodels:MainViewModel"
7-
x:Class="TodoApp.MAUI.MainPage"
8-
Title="TodoApp">
2+
<ContentPage
3+
x:Class="TodoApp.MAUI.MainPage"
4+
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
5+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
6+
xmlns:models="clr-namespace:TodoApp.MAUI.Models"
7+
xmlns:viewmodels="clr-namespace:TodoApp.MAUI.ViewModels"
8+
Title="TodoApp"
9+
x:DataType="viewmodels:MainViewModel">
910
<NavigationPage.TitleView>
1011
<StackLayout Style="{StaticResource titleViewContainer}">
1112
<Label Style="{StaticResource titleViewLabel}">TodoApp</Label>
12-
<ImageButton Style="{StaticResource titleViewRefreshIcon}" Command="{Binding RefreshItemsCommand}" />
13+
<ImageButton Command="{Binding RefreshItemsCommand}" Style="{StaticResource titleViewRefreshIcon}" />
1314
</StackLayout>
1415
</NavigationPage.TitleView>
1516

16-
<Grid AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" BackgroundColor="Azure">
17+
<Grid
18+
AbsoluteLayout.LayoutBounds="0,0,1,1"
19+
AbsoluteLayout.LayoutFlags="All"
20+
BackgroundColor="Azure">
1721
<Grid.RowDefinitions>
1822
<RowDefinition Height="*" />
1923
<RowDefinition Height="Auto" />
@@ -22,8 +26,8 @@
2226
<ColumnDefinition Width="*" />
2327
</Grid.ColumnDefinitions>
2428

25-
<!-- List of items -->
26-
<ListView ItemsSource="{Binding Items}" ItemTapped="OnListItemTapped">
29+
<!-- List of items -->
30+
<ListView ItemTapped="OnListItemTapped" ItemsSource="{Binding Items}">
2731
<ListView.ItemTemplate>
2832
<DataTemplate x:DataType="models:TodoItem">
2933
<ViewCell>
@@ -37,23 +41,26 @@
3741
</Grid.ColumnDefinitions>
3842

3943
<Label Style="{StaticResource listItemTitle}" Text="{Binding Title}" />
40-
<Image Grid.Column="1" IsVisible="{Binding IsComplete}" Style="{StaticResource listItemIcon}" />
44+
<Image
45+
Grid.Column="1"
46+
IsVisible="{Binding IsComplete}"
47+
Style="{StaticResource listItemIcon}" />
4148
</Grid>
4249
</ViewCell>
4350
</DataTemplate>
4451
</ListView.ItemTemplate>
4552
</ListView>
4653

47-
<!-- Entry box -->
54+
<!-- Entry box -->
4855
<Frame Grid.Row="1" Style="{StaticResource roundedCornerFrame}">
4956
<HorizontalStackLayout>
5057
<Image Style="{StaticResource addItemIcon}" />
51-
<Entry
52-
x:Name="addItemEntry"
53-
Style="{StaticResource addItemEntry}"
58+
<Entry
59+
x:Name="addItemEntry"
5460
Placeholder="Enter Todo Item Text"
5561
ReturnCommand="{Binding AddItemCommand}"
56-
ReturnCommandParameter="{Binding Source={x:Reference addItemEntry}}" />
62+
ReturnCommandParameter="{Binding Text, Source={x:Reference addItemEntry}}"
63+
Style="{StaticResource addItemEntry}" />
5764
</HorizontalStackLayout>
5865
</Frame>
5966
</Grid>

samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ public MainPage()
2121
protected override void OnAppearing()
2222
{
2323
base.OnAppearing();
24-
this._viewModel.RefreshItemsCommand.Execute();
24+
this._viewModel.RefreshItemsCommand.Execute(null);
2525
}
2626

2727
public void OnListItemTapped(object sender, ItemTappedEventArgs e)
2828
{
2929
if (e.Item is TodoItem item)
3030
{
31-
this._viewModel.UpdateItemCommand.Execute(item);
31+
this._viewModel.UpdateItemCommand.Execute(item.Id);
3232
}
3333

3434
if (sender is ListView itemList)

samples/todoapp/TodoApp.MAUI/Models/AppDbContext.cs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,50 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
#undef OFFLINE_SYNC_ENABLED
6+
7+
using CommunityToolkit.Datasync.Client.Http;
8+
using CommunityToolkit.Datasync.Client.Offline;
59
using Microsoft.EntityFrameworkCore;
10+
using TodoApp.MAUI.Services;
611

712
namespace TodoApp.MAUI.Models;
813

14+
#if OFFLINE_SYNC_ENABLED
15+
public class AppDbContext(DbContextOptions<AppDbContext> options) : OfflineDbContext(options)
16+
#else
917
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
18+
#endif
1019
{
1120
public DbSet<TodoItem> TodoItems => Set<TodoItem>();
1221

13-
//protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder)
14-
//{
15-
// HttpClientOptions clientOptions = new()
16-
// {
17-
// Endpoint = new Uri("https://YOURSITEHERE.azurewebsites.net/"),
18-
// HttpPipeline = [new LoggingHandler()]
19-
// };
20-
// _ = optionsBuilder.UseHttpClientOptions(clientOptions);
21-
//}
22+
#if OFFLINE_SYNC_ENABLED
23+
protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder)
24+
{
25+
HttpClientOptions clientOptions = new()
26+
{
27+
Endpoint = new Uri("https://YOUR_SITE_HERE.azurewebsites.net/"),
28+
HttpPipeline = [new LoggingHandler()]
29+
};
30+
_ = optionsBuilder.UseHttpClientOptions(clientOptions);
31+
}
32+
#endif
2233

2334
public async Task SynchronizeAsync(CancellationToken cancellationToken = default)
2435
{
25-
//PushResult pushResult = await this.PushAsync(cancellationToken);
26-
//if (!pushResult.IsSuccessful)
27-
//{
28-
// throw new ApplicationException($"Push failed: {pushResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}");
29-
//}
30-
31-
//PullResult pullResult = await this.PullAsync(cancellationToken);
32-
//if (!pullResult.IsSuccessful)
33-
//{
34-
// throw new ApplicationException($"Pull failed: {pullResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}");
35-
//}
36+
#if OFFLINE_SYNC_ENABLED
37+
PushResult pushResult = await this.PushAsync(cancellationToken);
38+
if (!pushResult.IsSuccessful)
39+
{
40+
throw new ApplicationException($"Push failed: {pushResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}");
41+
}
42+
43+
PullResult pullResult = await this.PullAsync(cancellationToken);
44+
if (!pullResult.IsSuccessful)
45+
{
46+
throw new ApplicationException($"Pull failed: {pullResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}");
47+
}
48+
#endif
3649
}
3750
}
3851

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Diagnostics;
6+
7+
namespace TodoApp.MAUI.Services;
8+
9+
/// <summary>
10+
/// A delegating handler that logs the request/response to stdout.
11+
/// </summary>
12+
public class LoggingHandler : DelegatingHandler
13+
{
14+
public LoggingHandler() : base()
15+
{
16+
}
17+
18+
public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
19+
{
20+
}
21+
22+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
23+
{
24+
Debug.WriteLine($"[HTTP] >>> {request.Method} {request.RequestUri}");
25+
await WriteContentAsync(request.Content, cancellationToken);
26+
27+
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
28+
29+
Debug.WriteLine($"[HTTP] <<< {response.StatusCode} {response.ReasonPhrase}");
30+
await WriteContentAsync(response.Content, cancellationToken);
31+
32+
return response;
33+
}
34+
35+
private static async Task WriteContentAsync(HttpContent? content, CancellationToken cancellationToken = default)
36+
{
37+
if (content != null)
38+
{
39+
Debug.WriteLine($"[HTTP] >>> {await content.ReadAsStringAsync(cancellationToken)}");
40+
}
41+
}
42+
}
43+

samples/todoapp/TodoApp.MAUI/ViewModels/MainViewModel.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,22 @@
44

55
using CommunityToolkit.Datasync.Client;
66
using CommunityToolkit.Mvvm.ComponentModel;
7+
using CommunityToolkit.Mvvm.Input;
78
using Microsoft.EntityFrameworkCore;
89
using TodoApp.MAUI.Models;
910
using TodoApp.MAUI.Services;
1011

1112
namespace TodoApp.MAUI.ViewModels;
1213

13-
public class MainViewModel(AppDbContext context, IAlertService alertService) : ObservableRecipient
14+
public partial class MainViewModel(AppDbContext context, IAlertService alertService) : ObservableRecipient
1415
{
1516
[ObservableProperty]
1617
private bool _isRefreshing = false;
1718

1819
[ObservableProperty]
1920
private ConcurrentObservableCollection<TodoItem> items = [];
2021

22+
[RelayCommand]
2123
public async Task RefreshItemsAsync(CancellationToken cancellationToken = default)
2224
{
2325
if (IsRefreshing)
@@ -41,6 +43,7 @@ public async Task RefreshItemsAsync(CancellationToken cancellationToken = defaul
4143
}
4244
}
4345

46+
[RelayCommand]
4447
public async Task UpdateItemAsync(string itemId, CancellationToken cancellationToken = default)
4548
{
4649
try
@@ -60,6 +63,7 @@ public async Task UpdateItemAsync(string itemId, CancellationToken cancellationT
6063
}
6164
}
6265

66+
[RelayCommand]
6367
public async Task AddItemAsync(string text, CancellationToken cancellationToken = default)
6468
{
6569
try

0 commit comments

Comments
 (0)