2
0

Restructured sample pages folder

This commit is contained in:
2026-01-11 10:37:01 -05:00
parent 518434bc4e
commit f87ea17a6f
8 changed files with 301 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
<?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="#F5F7FA">
<ContentPage.Resources>
<Color x:Key="PrimaryColor">#5C6BC0</Color>
<Color x:Key="AccentColor">#26A69A</Color>
<Color x:Key="TextPrimary">#212121</Color>
<Color x:Key="TextSecondary">#757575</Color>
<Color x:Key="CardBackground">#FFFFFF</Color>
<Color x:Key="BorderColor">#E8EAF6</Color>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="Save" Clicked="OnSaveClicked" />
</ContentPage.ToolbarItems>
<Grid RowDefinitions="Auto,Auto,Auto,*" RowSpacing="16" Padding="20">
<!-- Header -->
<VerticalStackLayout Grid.Row="0" Spacing="4">
<Label Text="Create a new task"
FontSize="24"
TextColor="{StaticResource TextPrimary}" />
<Label Text="Fill in the details below"
FontSize="14"
TextColor="{StaticResource TextSecondary}" />
</VerticalStackLayout>
<!-- Title Section -->
<VerticalStackLayout Grid.Row="1" Spacing="8">
<Label Text="TITLE"
FontSize="13"
FontAttributes="Bold"
TextColor="{StaticResource PrimaryColor}" />
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16,12">
<Border.StrokeShape>
<RoundRectangle CornerRadius="10" />
</Border.StrokeShape>
<Entry x:Name="TitleEntry"
Placeholder="What needs to be done?"
FontSize="18"
TextColor="{StaticResource TextPrimary}"
PlaceholderColor="{StaticResource TextSecondary}" />
</Border>
</VerticalStackLayout>
<!-- Notes Label -->
<Label Grid.Row="2"
Text="NOTES"
FontSize="13"
FontAttributes="Bold"
TextColor="{StaticResource PrimaryColor}" />
<!-- Notes Section (fills remaining space) -->
<Border Grid.Row="3"
BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16,12">
<Border.StrokeShape>
<RoundRectangle CornerRadius="10" />
</Border.StrokeShape>
<Editor x:Name="NotesEditor"
Placeholder="Add notes (optional)..."
FontSize="14"
TextColor="{StaticResource TextPrimary}"
PlaceholderColor="{StaticResource TextSecondary}"
VerticalOptions="Fill" />
</Border>
</Grid>
</ContentPage>

View File

@@ -0,0 +1,31 @@
// NewTodoPage - Create a new todo item
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();
}
}

View File

@@ -0,0 +1,106 @@
<?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="#F5F7FA">
<ContentPage.Resources>
<Color x:Key="PrimaryColor">#5C6BC0</Color>
<Color x:Key="AccentColor">#26A69A</Color>
<Color x:Key="DangerColor">#EF5350</Color>
<Color x:Key="TextPrimary">#212121</Color>
<Color x:Key="TextSecondary">#757575</Color>
<Color x:Key="CardBackground">#FFFFFF</Color>
<Color x:Key="BorderColor">#E8EAF6</Color>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="Delete" Clicked="OnDeleteClicked" />
<ToolbarItem Text="Save" 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="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16,12">
<Border.StrokeShape>
<RoundRectangle CornerRadius="10" />
</Border.StrokeShape>
<Entry x:Name="TitleEntry"
Placeholder="Task title"
FontSize="18"
TextColor="{StaticResource TextPrimary}"
PlaceholderColor="{StaticResource TextSecondary}" />
</Border>
</VerticalStackLayout>
<!-- Notes Label -->
<Label Grid.Row="1"
Text="NOTES"
FontSize="13"
FontAttributes="Bold"
TextColor="{StaticResource PrimaryColor}" />
<!-- Notes Section (fills remaining space) -->
<Border Grid.Row="2"
BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16,12">
<Border.StrokeShape>
<RoundRectangle CornerRadius="10" />
</Border.StrokeShape>
<Editor x:Name="NotesEditor"
Placeholder="Add notes here..."
FontSize="14"
TextColor="{StaticResource TextPrimary}"
PlaceholderColor="{StaticResource TextSecondary}"
VerticalOptions="Fill" />
</Border>
<!-- Status Section -->
<VerticalStackLayout Grid.Row="3" Spacing="8">
<Label Text="STATUS"
FontSize="13"
FontAttributes="Bold"
TextColor="{StaticResource PrimaryColor}" />
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16,12">
<Border.StrokeShape>
<RoundRectangle CornerRadius="10" />
</Border.StrokeShape>
<HorizontalStackLayout Spacing="12">
<CheckBox x:Name="CompletedCheckBox"
Color="{StaticResource AccentColor}"
VerticalOptions="Center"
CheckedChanged="OnCompletedChanged" />
<Label x:Name="StatusLabel"
Text="In Progress"
FontSize="16"
TextColor="{StaticResource TextPrimary}"
VerticalOptions="Center" />
</HorizontalStackLayout>
</Border>
</VerticalStackLayout>
<!-- Created Date -->
<Label Grid.Row="4"
x:Name="CreatedLabel"
FontSize="13"
TextColor="{StaticResource TextSecondary}"
HorizontalOptions="Center"
HorizontalTextAlignment="Center" />
</Grid>
</ContentPage>

View File

@@ -0,0 +1,91 @@
// TodoDetailPage - View and edit a todo item
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform;
namespace TodoApp;
public partial class TodoDetailPage : ContentPage
{
private readonly TodoItem _todo;
private readonly TodoService _service = TodoService.Instance;
// Colors for status label
private static readonly Color AccentColor = Color.FromArgb("#26A69A");
private static readonly Color TextPrimary = Color.FromArgb("#212121");
public TodoDetailPage(TodoItem todo)
{
try
{
Console.WriteLine($"[TodoDetailPage] Constructor starting for: {todo.Title}");
InitializeComponent();
Console.WriteLine($"[TodoDetailPage] InitializeComponent complete");
_todo = todo;
// Populate fields
Console.WriteLine($"[TodoDetailPage] Setting TitleEntry.Text");
TitleEntry.Text = _todo.Title;
Console.WriteLine($"[TodoDetailPage] Setting NotesEditor.Text");
NotesEditor.Text = _todo.Notes;
Console.WriteLine($"[TodoDetailPage] Setting CompletedCheckBox.IsChecked");
CompletedCheckBox.IsChecked = _todo.IsCompleted;
Console.WriteLine($"[TodoDetailPage] Calling UpdateStatusLabel");
UpdateStatusLabel(_todo.IsCompleted);
Console.WriteLine($"[TodoDetailPage] Setting CreatedLabel.Text");
CreatedLabel.Text = $"Created {_todo.CreatedAt:MMMM d, yyyy} at {_todo.CreatedAt:h:mm tt}";
Console.WriteLine($"[TodoDetailPage] Constructor complete");
}
catch (Exception ex)
{
Console.WriteLine($"[TodoDetailPage] EXCEPTION in constructor: {ex.GetType().Name}: {ex.Message}");
Console.WriteLine($"[TodoDetailPage] Stack trace: {ex.StackTrace}");
throw;
}
}
private void OnCompletedChanged(object? sender, Microsoft.Maui.Controls.CheckedChangedEventArgs e)
{
Console.WriteLine($"[TodoDetailPage] OnCompletedChanged: {e.Value}");
UpdateStatusLabel(e.Value);
}
private void UpdateStatusLabel(bool isCompleted)
{
if (StatusLabel == null)
{
Console.WriteLine($"[TodoDetailPage] UpdateStatusLabel: StatusLabel is null, skipping");
return;
}
Console.WriteLine($"[TodoDetailPage] UpdateStatusLabel: setting to {(isCompleted ? "Completed" : "In Progress")}");
StatusLabel.Text = isCompleted ? "Completed" : "In Progress";
StatusLabel.TextColor = isCompleted ? AccentColor : TextPrimary;
}
private async void OnSaveClicked(object? sender, EventArgs e)
{
_todo.Title = TitleEntry.Text ?? "";
_todo.Notes = NotesEditor.Text ?? "";
_todo.IsCompleted = CompletedCheckBox.IsChecked;
await Navigation.PopAsync();
}
private async void OnDeleteClicked(object? sender, EventArgs e)
{
// Show confirmation dialog
var confirmed = await LinuxDialogService.ShowAlertAsync(
"Delete Task",
$"Are you sure you want to delete \"{_todo.Title}\"? This action cannot be undone.",
"Delete",
"Cancel");
if (confirmed)
{
_service.DeleteTodo(_todo);
await Navigation.PopAsync();
}
}
}

View File

@@ -0,0 +1,129 @@
<?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="#F5F7FA">
<ContentPage.Resources>
<ResourceDictionary>
<!-- Colors -->
<Color x:Key="PrimaryColor">#5C6BC0</Color>
<Color x:Key="PrimaryDark">#3949AB</Color>
<Color x:Key="AccentColor">#26A69A</Color>
<Color x:Key="TextPrimary">#212121</Color>
<Color x:Key="TextSecondary">#757575</Color>
<Color x:Key="CardBackground">#FFFFFF</Color>
<Color x:Key="DividerColor">#E0E0E0</Color>
<Color x:Key="CompletedColor">#9E9E9E</Color>
<!-- Converters -->
<local:AlternatingRowColorConverter x:Key="AlternatingRowColorConverter" />
<local:CompletedToColorConverter x:Key="CompletedToColorConverter" />
<local:CompletedToTextDecorationsConverter x:Key="CompletedToTextDecorationsConverter" />
<local:CompletedToOpacityConverter x:Key="CompletedToOpacityConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="+ Add" Clicked="OnAddClicked" />
</ContentPage.ToolbarItems>
<Grid RowDefinitions="*,Auto" Padding="0">
<!-- Task List -->
<CollectionView Grid.Row="0"
x:Name="TodoCollectionView"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged"
VerticalOptions="FillAndExpand"
BackgroundColor="Transparent"
Margin="16,16,16,0">
<CollectionView.EmptyView>
<VerticalStackLayout VerticalOptions="Center"
HorizontalOptions="Center"
Padding="40">
<Label Text="No tasks yet"
FontSize="22"
TextColor="{StaticResource TextSecondary}"
HorizontalOptions="Center" />
<Label Text="Tap '+ Add' to create your first task"
FontSize="14"
TextColor="{StaticResource TextSecondary}"
HorizontalOptions="Center"
Margin="0,8,0,0" />
</VerticalStackLayout>
</CollectionView.EmptyView>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="local:TodoItem">
<!-- Card-style item -->
<Grid Padding="0,6" BackgroundColor="Transparent">
<Border StrokeThickness="0"
BackgroundColor="{StaticResource CardBackground}"
Padding="16,14"
Opacity="{Binding IsCompleted, Converter={StaticResource CompletedToOpacityConverter}}">
<Border.StrokeShape>
<RoundRectangle CornerRadius="12" />
</Border.StrokeShape>
<Grid ColumnDefinitions="Auto,*" ColumnSpacing="16">
<!-- Completion indicator -->
<Border Grid.Column="0"
WidthRequest="8"
HeightRequest="44"
Margin="0"
BackgroundColor="{Binding IsCompleted, Converter={StaticResource CompletedToColorConverter}, ConverterParameter=indicator}"
VerticalOptions="Center">
<Border.StrokeShape>
<RoundRectangle CornerRadius="4" />
</Border.StrokeShape>
</Border>
<!-- Content -->
<VerticalStackLayout Grid.Column="1"
Spacing="4"
VerticalOptions="Center">
<!-- Title -->
<Label Text="{Binding Title}"
FontSize="16"
TextColor="{Binding IsCompleted, Converter={StaticResource CompletedToColorConverter}}"
TextDecorations="{Binding IsCompleted, Converter={StaticResource CompletedToTextDecorationsConverter}}" />
<!-- Notes preview -->
<Label Text="{Binding Notes}"
FontSize="13"
TextColor="{Binding IsCompleted, Converter={StaticResource CompletedToColorConverter}, ConverterParameter=notes}"
MaxLines="2"
LineBreakMode="TailTruncation" />
</VerticalStackLayout>
</Grid>
</Border>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- Footer Stats -->
<Border Grid.Row="1"
BackgroundColor="{StaticResource PrimaryColor}"
StrokeThickness="0"
Padding="24,14"
Margin="0">
<Grid ColumnDefinitions="Auto,Auto" ColumnSpacing="6" HorizontalOptions="Center" VerticalOptions="Center">
<Label Grid.Column="0"
Text="Tasks:"
FontSize="15"
TextColor="White" />
<Label Grid.Column="1"
x:Name="StatsLabel"
FontSize="15"
TextColor="White"
Opacity="0.9" />
</Grid>
</Border>
</Grid>
</ContentPage>

View File

@@ -0,0 +1,174 @@
// TodoListPage - Main page for viewing todos with XAML support
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
using System.Globalization;
namespace TodoApp;
public partial class TodoListPage : ContentPage
{
private readonly TodoService _service = TodoService.Instance;
public TodoListPage()
{
Console.WriteLine("[TodoListPage] Constructor starting");
InitializeComponent();
TodoCollectionView.ItemsSource = _service.Todos;
UpdateStats();
Console.WriteLine("[TodoListPage] Constructor finished");
}
protected override void OnAppearing()
{
Console.WriteLine("[TodoListPage] OnAppearing called - refreshing CollectionView");
base.OnAppearing();
// Refresh indexes for alternating row colors
_service.RefreshIndexes();
// Refresh the collection view
TodoCollectionView.ItemsSource = null;
TodoCollectionView.ItemsSource = _service.Todos;
Console.WriteLine($"[TodoListPage] ItemsSource set with {_service.Todos.Count} items");
UpdateStats();
}
private async void OnAddClicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new NewTodoPage());
}
private async void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
try
{
Console.WriteLine($"[TodoListPage] OnSelectionChanged: {e.CurrentSelection.Count} items selected");
if (e.CurrentSelection.FirstOrDefault() is TodoItem todo)
{
Console.WriteLine($"[TodoListPage] Navigating to TodoDetailPage for: {todo.Title}");
TodoCollectionView.SelectedItem = null; // Deselect
var detailPage = new TodoDetailPage(todo);
Console.WriteLine($"[TodoListPage] Created TodoDetailPage, pushing...");
await Navigation.PushAsync(detailPage);
Console.WriteLine($"[TodoListPage] Navigation complete");
}
}
catch (Exception ex)
{
Console.WriteLine($"[TodoListPage] EXCEPTION in OnSelectionChanged: {ex.GetType().Name}: {ex.Message}");
Console.WriteLine($"[TodoListPage] Stack trace: {ex.StackTrace}");
}
}
private void UpdateStats()
{
var completed = _service.CompletedCount;
var total = _service.TotalCount;
if (total == 0)
{
StatsLabel.Text = "";
}
else
{
StatsLabel.Text = $"{completed} of {total} completed";
}
}
}
/// <summary>
/// Converter for alternating row background colors.
/// </summary>
public class AlternatingRowColorConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is int index)
{
return index % 2 == 0 ? Colors.White : Color.FromArgb("#F5F5F5");
}
return Colors.White;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Converter for completed task text color and indicator color.
/// </summary>
public class CompletedToColorConverter : IValueConverter
{
// Define colors
private static readonly Color PrimaryColor = Color.FromArgb("#5C6BC0");
private static readonly Color AccentColor = Color.FromArgb("#26A69A");
private static readonly Color CompletedColor = Color.FromArgb("#9E9E9E");
private static readonly Color TextPrimary = Color.FromArgb("#212121");
private static readonly Color TextSecondary = Color.FromArgb("#757575");
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
bool isCompleted = value is bool b && b;
string param = parameter as string ?? "";
// Indicator bar color
if (param == "indicator")
{
return isCompleted ? CompletedColor : AccentColor;
}
// Text colors
if (isCompleted)
{
return CompletedColor;
}
else
{
return param == "notes" ? TextSecondary : TextPrimary;
}
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Converter for completed task text decorations (strikethrough).
/// </summary>
public class CompletedToTextDecorationsConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
bool isCompleted = value is bool b && b;
return isCompleted ? TextDecorations.Strikethrough : TextDecorations.None;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Converter for completed task opacity (slightly faded when complete).
/// </summary>
public class CompletedToOpacityConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
bool isCompleted = value is bool b && b;
return isCompleted ? 0.7 : 1.0;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}