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