Add TodoApp sample with reconstructed XAML
Complete TodoApp sample application with: - App.xaml/cs: Colors and styles for light/dark themes - TodoListPage: Task list with theme toggle switch - NewTodoPage: Form to create new tasks - TodoDetailPage: Edit task details with delete option - TodoItem.cs/TodoService.cs: Data model and service - SVG icons for save, delete, and add actions Theme switching via toggle on main page applies app-wide. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
40
samples/TodoApp/App.xaml
Normal file
40
samples/TodoApp/App.xaml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
x:Class="TodoApp.App">
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<!-- Primary Colors -->
|
||||||
|
<Color x:Key="PrimaryColor">#5C6BC0</Color>
|
||||||
|
<Color x:Key="PrimaryDarkColor">#3949AB</Color>
|
||||||
|
<Color x:Key="AccentColor">#26A69A</Color>
|
||||||
|
<Color x:Key="DangerColor">#EF5350</Color>
|
||||||
|
|
||||||
|
<!-- Page Backgrounds -->
|
||||||
|
<Color x:Key="PageBackgroundLight">#F5F5F5</Color>
|
||||||
|
<Color x:Key="PageBackgroundDark">#1E1E1E</Color>
|
||||||
|
|
||||||
|
<!-- Card/Surface Backgrounds -->
|
||||||
|
<Color x:Key="CardBackgroundLight">#FFFFFF</Color>
|
||||||
|
<Color x:Key="CardBackgroundDark">#2D2D2D</Color>
|
||||||
|
|
||||||
|
<!-- Input Backgrounds -->
|
||||||
|
<Color x:Key="InputBackgroundLight">#FFFFFF</Color>
|
||||||
|
<Color x:Key="InputBackgroundDark">#3D3D3D</Color>
|
||||||
|
|
||||||
|
<!-- Text Colors -->
|
||||||
|
<Color x:Key="TextPrimaryLight">#212121</Color>
|
||||||
|
<Color x:Key="TextPrimaryDark">#FFFFFF</Color>
|
||||||
|
<Color x:Key="TextSecondaryLight">#757575</Color>
|
||||||
|
<Color x:Key="TextSecondaryDark">#B0B0B0</Color>
|
||||||
|
|
||||||
|
<!-- Border Colors -->
|
||||||
|
<Color x:Key="BorderLight">#E0E0E0</Color>
|
||||||
|
<Color x:Key="BorderDark">#424242</Color>
|
||||||
|
|
||||||
|
<!-- Footer/Status Bar -->
|
||||||
|
<Color x:Key="FooterBackgroundLight">#5C6BC0</Color>
|
||||||
|
<Color x:Key="FooterBackgroundDark">#3949AB</Color>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
14
samples/TodoApp/App.xaml.cs
Normal file
14
samples/TodoApp/App.xaml.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.Maui.ApplicationModel;
|
||||||
|
using Microsoft.Maui.Controls;
|
||||||
|
|
||||||
|
namespace TodoApp;
|
||||||
|
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
public App()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
UserAppTheme = AppTheme.Light;
|
||||||
|
MainPage = new NavigationPage(new TodoListPage());
|
||||||
|
}
|
||||||
|
}
|
||||||
24
samples/TodoApp/MauiProgram.cs
Normal file
24
samples/TodoApp/MauiProgram.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.Maui;
|
||||||
|
using Microsoft.Maui.Controls.Hosting;
|
||||||
|
using Microsoft.Maui.Hosting;
|
||||||
|
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||||
|
|
||||||
|
namespace TodoApp;
|
||||||
|
|
||||||
|
public static class MauiProgram
|
||||||
|
{
|
||||||
|
public static MauiApp CreateMauiApp()
|
||||||
|
{
|
||||||
|
var builder = MauiApp.CreateBuilder();
|
||||||
|
builder
|
||||||
|
.UseMauiApp<App>()
|
||||||
|
.UseLinuxPlatform()
|
||||||
|
.ConfigureFonts(fonts =>
|
||||||
|
{
|
||||||
|
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||||
|
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
64
samples/TodoApp/NewTodoPage.xaml
Normal file
64
samples/TodoApp/NewTodoPage.xaml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?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"
|
||||||
|
x:Class="TodoApp.NewTodoPage"
|
||||||
|
Title="New Task"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<ToolbarItem IconImageSource="icon_save_light.svg" Clicked="OnSaveClicked" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*"
|
||||||
|
RowSpacing="16"
|
||||||
|
Padding="20">
|
||||||
|
|
||||||
|
<!-- Title Section -->
|
||||||
|
<VerticalStackLayout Grid.Row="0" Spacing="8">
|
||||||
|
<Label Text="TITLE"
|
||||||
|
FontSize="13"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
TextColor="{StaticResource PrimaryColor}" />
|
||||||
|
|
||||||
|
<Border BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||||
|
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Padding="16,12">
|
||||||
|
<Border.StrokeShape>
|
||||||
|
<RoundRectangle CornerRadius="10" />
|
||||||
|
</Border.StrokeShape>
|
||||||
|
<Entry x:Name="TitleEntry"
|
||||||
|
Placeholder="What needs to be done?"
|
||||||
|
FontSize="18"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource InputBackgroundLight}, Dark={StaticResource InputBackgroundDark}}"
|
||||||
|
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||||
|
PlaceholderColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||||
|
</Border>
|
||||||
|
</VerticalStackLayout>
|
||||||
|
|
||||||
|
<!-- Notes Label -->
|
||||||
|
<Label Grid.Row="1"
|
||||||
|
Text="NOTES"
|
||||||
|
FontSize="13"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
TextColor="{StaticResource PrimaryColor}" />
|
||||||
|
|
||||||
|
<!-- Notes Section -->
|
||||||
|
<Border Grid.Row="2"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||||
|
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Padding="16,12">
|
||||||
|
<Border.StrokeShape>
|
||||||
|
<RoundRectangle CornerRadius="10" />
|
||||||
|
</Border.StrokeShape>
|
||||||
|
<Editor x:Name="NotesEditor"
|
||||||
|
Placeholder="Add notes (optional)..."
|
||||||
|
FontSize="14"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource InputBackgroundLight}, Dark={StaticResource InputBackgroundDark}}"
|
||||||
|
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||||
|
PlaceholderColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||||
|
VerticalOptions="Fill" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</ContentPage>
|
||||||
29
samples/TodoApp/NewTodoPage.xaml.cs
Normal file
29
samples/TodoApp/NewTodoPage.xaml.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Maui.Controls;
|
||||||
|
using Microsoft.Maui.Graphics;
|
||||||
|
|
||||||
|
namespace TodoApp;
|
||||||
|
|
||||||
|
public partial class NewTodoPage : ContentPage
|
||||||
|
{
|
||||||
|
private readonly TodoService _service = TodoService.Instance;
|
||||||
|
|
||||||
|
public NewTodoPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnSaveClicked(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var title = TitleEntry.Text?.Trim();
|
||||||
|
if (string.IsNullOrEmpty(title))
|
||||||
|
{
|
||||||
|
TitleEntry.Placeholder = "Title is required!";
|
||||||
|
TitleEntry.PlaceholderColor = Colors.Red;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_service.AddTodo(title, NotesEditor.Text ?? "");
|
||||||
|
await Navigation.PopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
samples/TodoApp/Program.cs
Normal file
12
samples/TodoApp/Program.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||||
|
|
||||||
|
namespace TodoApp;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
LinuxProgramHost.Run(args, MauiProgram.CreateMauiApp);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
samples/TodoApp/Resources/Images/icon_add_light.svg
Normal file
4
samples/TodoApp/Resources/Images/icon_add_light.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#5C6BC0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 278 B |
6
samples/TodoApp/Resources/Images/icon_delete_light.svg
Normal file
6
samples/TodoApp/Resources/Images/icon_delete_light.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#E57373" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<polyline points="3 6 5 6 21 6"></polyline>
|
||||||
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||||
|
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||||
|
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 425 B |
3
samples/TodoApp/Resources/Images/icon_save_light.svg
Normal file
3
samples/TodoApp/Resources/Images/icon_save_light.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#5C6BC0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<polyline points="20 6 9 17 4 12"></polyline>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 232 B |
24
samples/TodoApp/TodoApp.csproj
Normal file
24
samples/TodoApp/TodoApp.csproj
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>TodoApp</RootNamespace>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseMaui>true</UseMaui>
|
||||||
|
<ApplicationTitle>Todo App</ApplicationTitle>
|
||||||
|
<ApplicationId>com.openmaui.todoapp</ApplicationId>
|
||||||
|
<ApplicationVersion>1.0</ApplicationVersion>
|
||||||
|
<SupportedOSPlatformVersion>9.0</SupportedOSPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\OpenMaui.Controls.Linux.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<MauiImage Include="Resources\Images\*" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
93
samples/TodoApp/TodoDetailPage.xaml
Normal file
93
samples/TodoApp/TodoDetailPage.xaml
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?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"
|
||||||
|
x:Class="TodoApp.TodoDetailPage"
|
||||||
|
Title="Task Details"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<ToolbarItem IconImageSource="icon_delete_light.svg" Clicked="OnDeleteClicked" />
|
||||||
|
<ToolbarItem IconImageSource="icon_save_light.svg" Clicked="OnSaveClicked" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*,Auto,Auto"
|
||||||
|
RowSpacing="16"
|
||||||
|
Padding="20">
|
||||||
|
|
||||||
|
<!-- Title Section -->
|
||||||
|
<VerticalStackLayout Grid.Row="0" Spacing="8">
|
||||||
|
<Label Text="TITLE"
|
||||||
|
FontSize="13"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
TextColor="{StaticResource PrimaryColor}" />
|
||||||
|
|
||||||
|
<Border BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||||
|
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Padding="16,12">
|
||||||
|
<Border.StrokeShape>
|
||||||
|
<RoundRectangle CornerRadius="10" />
|
||||||
|
</Border.StrokeShape>
|
||||||
|
<Entry x:Name="TitleEntry"
|
||||||
|
FontSize="18"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource InputBackgroundLight}, Dark={StaticResource InputBackgroundDark}}"
|
||||||
|
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||||
|
</Border>
|
||||||
|
</VerticalStackLayout>
|
||||||
|
|
||||||
|
<!-- Notes Label -->
|
||||||
|
<Label Grid.Row="1"
|
||||||
|
Text="NOTES"
|
||||||
|
FontSize="13"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
TextColor="{StaticResource PrimaryColor}" />
|
||||||
|
|
||||||
|
<!-- Notes Section -->
|
||||||
|
<Border Grid.Row="2"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||||
|
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Padding="16,12">
|
||||||
|
<Border.StrokeShape>
|
||||||
|
<RoundRectangle CornerRadius="10" />
|
||||||
|
</Border.StrokeShape>
|
||||||
|
<Editor x:Name="NotesEditor"
|
||||||
|
FontSize="14"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource InputBackgroundLight}, Dark={StaticResource InputBackgroundDark}}"
|
||||||
|
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||||
|
VerticalOptions="Fill" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Status Section -->
|
||||||
|
<VerticalStackLayout Grid.Row="3" Spacing="8">
|
||||||
|
<Label Text="STATUS"
|
||||||
|
FontSize="13"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
TextColor="{StaticResource PrimaryColor}" />
|
||||||
|
|
||||||
|
<Border BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||||
|
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Padding="16,12">
|
||||||
|
<Border.StrokeShape>
|
||||||
|
<RoundRectangle CornerRadius="10" />
|
||||||
|
</Border.StrokeShape>
|
||||||
|
<HorizontalStackLayout Spacing="12">
|
||||||
|
<CheckBox x:Name="CompletedCheckBox"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
<Label Text="In Progress"
|
||||||
|
FontSize="16"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||||
|
</HorizontalStackLayout>
|
||||||
|
</Border>
|
||||||
|
</VerticalStackLayout>
|
||||||
|
|
||||||
|
<!-- Created Date -->
|
||||||
|
<Label x:Name="CreatedLabel"
|
||||||
|
Grid.Row="4"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
FontSize="12"
|
||||||
|
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||||
|
</Grid>
|
||||||
|
</ContentPage>
|
||||||
42
samples/TodoApp/TodoDetailPage.xaml.cs
Normal file
42
samples/TodoApp/TodoDetailPage.xaml.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Maui.Controls;
|
||||||
|
|
||||||
|
namespace TodoApp;
|
||||||
|
|
||||||
|
public partial class TodoDetailPage : ContentPage
|
||||||
|
{
|
||||||
|
private readonly TodoService _service = TodoService.Instance;
|
||||||
|
private readonly TodoItem _item;
|
||||||
|
|
||||||
|
public TodoDetailPage(TodoItem item)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_item = item;
|
||||||
|
|
||||||
|
TitleEntry.Text = item.Title;
|
||||||
|
NotesEditor.Text = item.Notes;
|
||||||
|
CompletedCheckBox.IsChecked = item.IsCompleted;
|
||||||
|
CreatedLabel.Text = $"Created {item.CreatedAt:MMMM d, yyyy} at {item.CreatedAt:h:mm tt}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnSaveClicked(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_item.Title = TitleEntry.Text ?? "";
|
||||||
|
_item.Notes = NotesEditor.Text ?? "";
|
||||||
|
_item.IsCompleted = CompletedCheckBox.IsChecked;
|
||||||
|
await Navigation.PopAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnDeleteClicked(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
bool confirm = await DisplayAlert("Delete Task",
|
||||||
|
"Are you sure you want to delete this task?",
|
||||||
|
"Delete", "Cancel");
|
||||||
|
|
||||||
|
if (confirm)
|
||||||
|
{
|
||||||
|
_service.RemoveTodo(_item);
|
||||||
|
await Navigation.PopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
samples/TodoApp/TodoItem.cs
Normal file
47
samples/TodoApp/TodoItem.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace TodoApp;
|
||||||
|
|
||||||
|
public class TodoItem : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private string _title = "";
|
||||||
|
private string _notes = "";
|
||||||
|
private bool _isCompleted;
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int Index
|
||||||
|
{
|
||||||
|
get => _index;
|
||||||
|
set { if (_index != value) { _index = value; OnPropertyChanged(nameof(Index)); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => _title;
|
||||||
|
set { if (_title != value) { _title = value; OnPropertyChanged(nameof(Title)); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Notes
|
||||||
|
{
|
||||||
|
get => _notes;
|
||||||
|
set { if (_notes != value) { _notes = value; OnPropertyChanged(nameof(Notes)); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted
|
||||||
|
{
|
||||||
|
get => _isCompleted;
|
||||||
|
set { if (_isCompleted != value) { _isCompleted = value; OnPropertyChanged(nameof(IsCompleted)); } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
protected void OnPropertyChanged(string propertyName)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
83
samples/TodoApp/TodoListPage.xaml
Normal file
83
samples/TodoApp/TodoListPage.xaml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?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:local="clr-namespace:TodoApp"
|
||||||
|
x:Class="TodoApp.TodoListPage"
|
||||||
|
Title="My Tasks"
|
||||||
|
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<ToolbarItem Text="+" Clicked="OnAddClicked" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
<!-- Task List -->
|
||||||
|
<CollectionView x:Name="TodoCollectionView"
|
||||||
|
Grid.Row="0"
|
||||||
|
SelectionMode="None">
|
||||||
|
<CollectionView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="local:TodoItem">
|
||||||
|
<Grid Padding="16,8">
|
||||||
|
<Border BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||||
|
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||||
|
StrokeThickness="0"
|
||||||
|
Padding="0">
|
||||||
|
<Border.StrokeShape>
|
||||||
|
<RoundRectangle CornerRadius="8" />
|
||||||
|
</Border.StrokeShape>
|
||||||
|
<Grid ColumnDefinitions="4,*">
|
||||||
|
<!-- Accent Border -->
|
||||||
|
<BoxView Grid.Column="0"
|
||||||
|
Color="{StaticResource PrimaryColor}"
|
||||||
|
WidthRequest="4" />
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<VerticalStackLayout Grid.Column="1"
|
||||||
|
Padding="16,12"
|
||||||
|
Spacing="4">
|
||||||
|
<Label Text="{Binding Title}"
|
||||||
|
FontSize="16"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||||
|
<Label Text="{Binding Notes}"
|
||||||
|
FontSize="14"
|
||||||
|
MaxLines="2"
|
||||||
|
LineBreakMode="TailTruncation"
|
||||||
|
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||||
|
</VerticalStackLayout>
|
||||||
|
</Grid>
|
||||||
|
<Border.GestureRecognizers>
|
||||||
|
<TapGestureRecognizer Tapped="OnItemTapped" CommandParameter="{Binding .}" />
|
||||||
|
</Border.GestureRecognizers>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</CollectionView.ItemTemplate>
|
||||||
|
</CollectionView>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<Grid Grid.Row="1"
|
||||||
|
ColumnDefinitions="*,Auto,Auto"
|
||||||
|
Padding="16,12"
|
||||||
|
ColumnSpacing="16"
|
||||||
|
BackgroundColor="{StaticResource PrimaryColor}">
|
||||||
|
|
||||||
|
<Label x:Name="StatsLabel"
|
||||||
|
Grid.Column="0"
|
||||||
|
Text="Tasks: 0 of 3"
|
||||||
|
TextColor="White"
|
||||||
|
FontSize="14"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
|
||||||
|
<Label Grid.Column="1"
|
||||||
|
Text="💡"
|
||||||
|
FontSize="20"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
|
||||||
|
<Switch x:Name="ThemeSwitch"
|
||||||
|
Grid.Column="2"
|
||||||
|
Toggled="OnThemeToggled"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ContentPage>
|
||||||
69
samples/TodoApp/TodoListPage.xaml.cs
Normal file
69
samples/TodoApp/TodoListPage.xaml.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Maui.ApplicationModel;
|
||||||
|
using Microsoft.Maui.Controls;
|
||||||
|
|
||||||
|
namespace TodoApp;
|
||||||
|
|
||||||
|
public partial class TodoListPage : ContentPage
|
||||||
|
{
|
||||||
|
private readonly TodoService _service = TodoService.Instance;
|
||||||
|
private bool _isNavigating;
|
||||||
|
|
||||||
|
public TodoListPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
TodoCollectionView.ItemsSource = _service.Todos;
|
||||||
|
UpdateStats();
|
||||||
|
ThemeSwitch.IsToggled = Application.Current?.UserAppTheme == AppTheme.Dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnAppearing()
|
||||||
|
{
|
||||||
|
base.OnAppearing();
|
||||||
|
_isNavigating = false;
|
||||||
|
_service.RefreshIndexes();
|
||||||
|
TodoCollectionView.ItemsSource = null;
|
||||||
|
TodoCollectionView.ItemsSource = _service.Todos;
|
||||||
|
UpdateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnThemeToggled(object? sender, ToggledEventArgs e)
|
||||||
|
{
|
||||||
|
if (Application.Current != null)
|
||||||
|
{
|
||||||
|
Application.Current.UserAppTheme = e.Value ? AppTheme.Dark : AppTheme.Light;
|
||||||
|
// Refresh to apply theme
|
||||||
|
var items = TodoCollectionView.ItemsSource;
|
||||||
|
TodoCollectionView.ItemsSource = null;
|
||||||
|
TodoCollectionView.ItemsSource = items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnAddClicked(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
await Navigation.PushAsync(new NewTodoPage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnItemTapped(object? sender, TappedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_isNavigating || e.Parameter is not TodoItem todoItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isNavigating = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Navigation.PushAsync(new TodoDetailPage(todoItem));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_isNavigating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStats()
|
||||||
|
{
|
||||||
|
int completed = _service.CompletedCount;
|
||||||
|
int total = _service.TotalCount;
|
||||||
|
StatsLabel.Text = total == 0 ? "" : $"Tasks: {completed} of {total}";
|
||||||
|
}
|
||||||
|
}
|
||||||
59
samples/TodoApp/TodoService.cs
Normal file
59
samples/TodoApp/TodoService.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace TodoApp;
|
||||||
|
|
||||||
|
public class TodoService
|
||||||
|
{
|
||||||
|
private static TodoService? _instance;
|
||||||
|
public static TodoService Instance => _instance ??= new TodoService();
|
||||||
|
|
||||||
|
public ObservableCollection<TodoItem> Todos { get; } = new();
|
||||||
|
|
||||||
|
private int _nextId = 1;
|
||||||
|
|
||||||
|
private TodoService()
|
||||||
|
{
|
||||||
|
// Add sample data
|
||||||
|
AddTodo("Learn OpenMaui Linux",
|
||||||
|
"Explore the SkiaSharp-based rendering engine for .NET MAUI on Linux desktop. " +
|
||||||
|
"This is a very long description that should wrap to multiple lines and demonstrate " +
|
||||||
|
"the ellipsis truncation feature when MaxLines is set to 2.");
|
||||||
|
AddTodo("Build amazing apps",
|
||||||
|
"Create cross-platform applications that run on Windows, macOS, iOS, Android, and Linux! " +
|
||||||
|
"With OpenMaui, you can write once and deploy everywhere.");
|
||||||
|
AddTodo("Share with the community",
|
||||||
|
"Contribute to the open-source project and help others build great Linux apps. " +
|
||||||
|
"Join our growing community of developers who are passionate about bringing .NET MAUI to Linux.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddTodo(string title, string notes)
|
||||||
|
{
|
||||||
|
var todo = new TodoItem
|
||||||
|
{
|
||||||
|
Id = _nextId++,
|
||||||
|
Title = title,
|
||||||
|
Notes = notes,
|
||||||
|
Index = Todos.Count
|
||||||
|
};
|
||||||
|
Todos.Add(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveTodo(TodoItem item)
|
||||||
|
{
|
||||||
|
Todos.Remove(item);
|
||||||
|
RefreshIndexes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshIndexes()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Todos.Count; i++)
|
||||||
|
{
|
||||||
|
Todos[i].Index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompletedCount => Todos.Count(t => t.IsCompleted);
|
||||||
|
public int TotalCount => Todos.Count;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user