Implement a ToDo overview based on a JSON file
This commit is contained in:
parent
37f27fa415
commit
468f66ab59
8 changed files with 219 additions and 40 deletions
|
@ -2,46 +2,62 @@
|
||||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
xmlns:model="clr-namespace:TodoDetails.Model"
|
xmlns:model="clr-namespace:TodoDetails.Model"
|
||||||
x:Class="TodoDetails.MainPage">
|
xmlns:viewmodel="clr-namespace:TodoDetails.ViewModel"
|
||||||
|
x:DataType="viewmodel:TodosViewModel"
|
||||||
<ScrollView>
|
x:Class="TodoDetails.MainPage"
|
||||||
<CollectionView>
|
Title="{Binding Title}">
|
||||||
<CollectionView.ItemsSource>
|
<Grid ColumnSpacing="5"
|
||||||
<x:Array Type="{x:Type model:Todo}">
|
RowSpacing="0">
|
||||||
<model:Todo Title="Create ViewModel"
|
<Grid.ColumnDefinitions>
|
||||||
Description="Create a ViewModel in the next step to learn MVVM."
|
<ColumnDefinition/>
|
||||||
Category="Default"
|
<ColumnDefinition/>
|
||||||
IsDone="False"/>
|
</Grid.ColumnDefinitions>
|
||||||
<model:Todo Title="Add Theming"
|
<Grid.RowDefinitions>
|
||||||
Description="Integrate your own theme in the app."
|
<RowDefinition/>
|
||||||
Category="Default"
|
<RowDefinition Height="Auto"/>
|
||||||
IsDone="False"/>
|
</Grid.RowDefinitions>
|
||||||
<model:Todo Title="Add local database"
|
<CollectionView ItemsSource="{Binding Todos}"
|
||||||
Description="Learn how to add a local database to work with (optional)."
|
SelectionMode="None"
|
||||||
Category="Default"
|
Grid.ColumnSpan="2">
|
||||||
IsDone="False"/>
|
|
||||||
</x:Array>
|
|
||||||
</CollectionView.ItemsSource>
|
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="model:Todo">
|
<DataTemplate x:DataType="model:Todo">
|
||||||
<HorizontalStackLayout Padding="10"
|
<Grid Padding="10">
|
||||||
Spacing="10"
|
<Border HeightRequest="125">
|
||||||
HorizontalOptions="FillAndExpand">
|
<Grid Padding="0">
|
||||||
<CheckBox IsChecked="{Binding IsDone}"
|
<Grid.ColumnDefinitions>
|
||||||
VerticalOptions="Start"/>
|
<ColumnDefinition/>
|
||||||
<VerticalStackLayout VerticalOptions="Center">
|
<ColumnDefinition/>
|
||||||
<Label Text="{Binding Title}"
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<CheckBox IsChecked="{Binding IsDone}"/>
|
||||||
|
<VerticalStackLayout Grid.Column="1"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
FontSize="16"
|
Padding="10">
|
||||||
TextColor="Gray"/>
|
<Label Text="{Binding Title}"
|
||||||
|
FontSize="16"/>
|
||||||
<Label Text="{Binding Category}"
|
<Label Text="{Binding Category}"
|
||||||
FontSize="12"
|
FontSize="12"/>
|
||||||
TextColor="Gray"/>
|
|
||||||
</VerticalStackLayout>
|
</VerticalStackLayout>
|
||||||
</HorizontalStackLayout>
|
<Label Text="{Binding DueDate}"
|
||||||
|
Grid.Column="2"
|
||||||
|
VerticalOptions="Center"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</CollectionView.ItemTemplate>
|
</CollectionView.ItemTemplate>
|
||||||
</CollectionView>
|
</CollectionView>
|
||||||
</ScrollView>
|
<Button Text="Fetch Todos"
|
||||||
|
Command="{Binding GetTodosCommand}"
|
||||||
|
IsEnabled="{Binding IsNotBusy}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="8"/>
|
||||||
|
<ActivityIndicator IsVisible="{Binding IsBusy}"
|
||||||
|
IsRunning="{Binding IsBusy}"
|
||||||
|
HorizontalOptions="Fill"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
Color="{DynamicResource Primary}"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
Grid.ColumnSpan="2"/>
|
||||||
|
</Grid>
|
||||||
</ContentPage>
|
</ContentPage>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
using TodoDetails.ViewModel;
|
||||||
|
|
||||||
namespace TodoDetails
|
namespace TodoDetails
|
||||||
{
|
{
|
||||||
public partial class MainPage : ContentPage
|
public partial class MainPage : ContentPage
|
||||||
{
|
{
|
||||||
int count = 0;
|
public MainPage(TodosViewModel viewModel)
|
||||||
|
|
||||||
public MainPage()
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
BindingContext = viewModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using TodoDetails.Services;
|
||||||
|
using TodoDetails.ViewModel;
|
||||||
|
|
||||||
namespace TodoDetails
|
namespace TodoDetails
|
||||||
{
|
{
|
||||||
|
@ -18,6 +20,9 @@ namespace TodoDetails
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
builder.Logging.AddDebug();
|
builder.Logging.AddDebug();
|
||||||
#endif
|
#endif
|
||||||
|
builder.Services.AddSingleton<TodoService>();
|
||||||
|
builder.Services.AddSingleton<TodosViewModel>();
|
||||||
|
builder.Services.AddSingleton<MainPage>();
|
||||||
|
|
||||||
return builder.Build();
|
return builder.Build();
|
||||||
}
|
}
|
||||||
|
|
23
Tasks/Lab12/TodoDetails/Resources/Raw/tododata.json
Normal file
23
Tasks/Lab12/TodoDetails/Resources/Raw/tododata.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Title": "Create ViewModel",
|
||||||
|
"Description": "Create a ViewModel in the next step to learn MVVM.",
|
||||||
|
"IsDone": true,
|
||||||
|
"Category": "Default",
|
||||||
|
"DueDate": "2023-05-31T00:00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "Add Themeing",
|
||||||
|
"Description": "Integrate your own theme in the app.",
|
||||||
|
"IsDone": false,
|
||||||
|
"Category": "Default",
|
||||||
|
"DueDate": "2023-05-31T00:00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "Add local database",
|
||||||
|
"Description": "Learn how to add a local database.",
|
||||||
|
"IsDone": false,
|
||||||
|
"Category": "Default",
|
||||||
|
"DueDate": "2023-05-31T00:00:00"
|
||||||
|
}
|
||||||
|
]
|
40
Tasks/Lab12/TodoDetails/Services/TodoService.cs
Normal file
40
Tasks/Lab12/TodoDetails/Services/TodoService.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TodoDetails.Model;
|
||||||
|
|
||||||
|
namespace TodoDetails.Services
|
||||||
|
{
|
||||||
|
public class TodoService
|
||||||
|
{
|
||||||
|
Lazy<Task<List<Todo>>> todoList;
|
||||||
|
|
||||||
|
public TodoService()
|
||||||
|
{
|
||||||
|
todoList = new Lazy<Task<List<Todo>>>(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
List<Todo>? result = null;
|
||||||
|
|
||||||
|
using var stream = await FileSystem.OpenAppPackageFileAsync("tododata.json");
|
||||||
|
using var reader = new StreamReader(stream);
|
||||||
|
var contents = await reader.ReadToEndAsync();
|
||||||
|
|
||||||
|
if (contents != null)
|
||||||
|
{
|
||||||
|
result = JsonSerializer.Deserialize<List<Todo>>(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result ?? new();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Todo>> GetTodos()
|
||||||
|
{
|
||||||
|
return await todoList.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,6 +57,11 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Remove="Resources\raw\tododata.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
|
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
|
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||||
|
|
21
Tasks/Lab12/TodoDetails/ViewModel/BaseViewModel.cs
Normal file
21
Tasks/Lab12/TodoDetails/ViewModel/BaseViewModel.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TodoDetails.ViewModel
|
||||||
|
{
|
||||||
|
public partial class BaseViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsNotBusy))]
|
||||||
|
bool isBusy;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
string? title;
|
||||||
|
|
||||||
|
public bool IsNotBusy => !IsBusy;
|
||||||
|
}
|
||||||
|
}
|
68
Tasks/Lab12/TodoDetails/ViewModel/TodosViewModel.cs
Normal file
68
Tasks/Lab12/TodoDetails/ViewModel/TodosViewModel.cs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TodoDetails.Model;
|
||||||
|
using TodoDetails.Services;
|
||||||
|
|
||||||
|
namespace TodoDetails.ViewModel
|
||||||
|
{
|
||||||
|
public partial class TodosViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
public ObservableCollection<Todo> Todos { get; } = new ObservableCollection<Todo>();
|
||||||
|
TodoService todoService;
|
||||||
|
|
||||||
|
public TodosViewModel(TodoService todoService)
|
||||||
|
{
|
||||||
|
Title = "Todos";
|
||||||
|
this.todoService = todoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
async Task GetTodosAsync()
|
||||||
|
{
|
||||||
|
if (IsBusy)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
var todos = await todoService.GetTodos();
|
||||||
|
|
||||||
|
if (Todos.Count != 0)
|
||||||
|
{
|
||||||
|
Todos.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var todo in todos)
|
||||||
|
{
|
||||||
|
Todos.Add(todo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"UInable to get todos: {e.Message}");
|
||||||
|
await Shell.Current.DisplayAlert("Error!", e.Message, "OK");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
async Task GoToDetails(Todo todo)
|
||||||
|
{
|
||||||
|
if (todo == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue