Lägga till data och beständighet

I det här steget ber du assistenten om de icke-visningsbara delarna av appen. Genom att hålla den här ändringen åtskild från XAML blir det enklare att isolera kompilatorfel och designproblem.

Be om en modell och lagringstjänst

Använd den här uppmaningen:

Add the data layer for TaskTally.

- Create a TaskItem model with an ID, title, and completion state.
- Use MVVM Toolkit observable properties.
- Create a TaskStorage service that serializes the task list to JSON.
- Store tasks in ApplicationData.Current.LocalFolder because this is
  a packaged WinUI 3 app.
- Return an empty list when the file doesn't exist.
- Serialize save operations so rapid changes don't write the file concurrently.
- Surface real read and write errors to the caller.
- Build the project after the change.

Jämför den genererade modellen med den här implementeringen:

using CommunityToolkit.Mvvm.ComponentModel;

namespace TaskTally.Models;

public partial class TaskItem : ObservableObject
{
    public Guid Id { get; init; } = Guid.NewGuid();

    [ObservableProperty]
    public partial string Title { get; set; } = string.Empty;

    [ObservableProperty]
    public partial bool IsComplete { get; set; }
}

Attributet [ObservableProperty] genererar offentliga egenskaper och ändrar meddelanden. Klassen måste vara partial så att MVVM Toolkit-källgeneratorn kan lägga till koden.

Lagringstjänsten bör se ut så här:

using System.Text.Json;
using System.Threading;
using TaskTally.Models;
using Windows.Storage;

namespace TaskTally.Services;

internal static class TaskStorage
{
    private const string FileName = "tasks.json";
    private static readonly SemaphoreSlim SaveLock = new(1, 1);

    public static async Task<IReadOnlyList<TaskItem>> LoadAsync()
    {
        StorageFile? file =
            await ApplicationData.Current.LocalFolder.TryGetItemAsync(FileName) as StorageFile;

        if (file is null)
        {
            return [];
        }

        string json = await FileIO.ReadTextAsync(file);
        return JsonSerializer.Deserialize<List<TaskItem>>(json) ?? [];
    }

    public static async Task SaveAsync(IEnumerable<TaskItem> tasks)
    {
        await SaveLock.WaitAsync();
        try
        {
            string json = JsonSerializer.Serialize(tasks, new JsonSerializerOptions
            {
                WriteIndented = true
            });

            StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
                FileName,
                CreationCollisionOption.ReplaceExisting);

            await FileIO.WriteTextAsync(file, json);
        }
        finally
        {
            SaveLock.Release();
        }
    }
}

ApplicationData.Current.LocalFolder är lämpligt här eftersom appen har paketidentitet. SaveLock förhindrar att åtgärder för att lägga till, ta bort och slutföra samma fil skrivs samtidigt. Om du senare ändrar distributionsmodellen kontrollerar du paketidentitetsantagandet igen i stället för att kopiera lagringsvalet automatiskt.

Läs mer: Lagra och hämta inställningar och andra appdata och ApplicationData.LocalFolder.

Fråga efter vymodellen

Använd sedan den här prompten:

Add MainPageViewModel.

- Expose an ObservableCollection<TaskItem>.
- Add observable properties for the new-task text and status message.
- Add asynchronous commands to add and delete tasks.
- Disable AddTaskCommand when the title is blank.
- Load tasks once when the page starts.
- Save after add, delete, or completion changes.
- Expose a summary and empty-state property for x:Bind.
- Catch storage exceptions only where the UI can report them.
- Build and fix all warnings.

De huvudsakliga mönster som ska kontrolleras i svaret är:

public ObservableCollection<TaskItem> Tasks { get; } = [];

[ObservableProperty]
public partial string NewTaskTitle { get; set; } = string.Empty;

public string Summary =>
    Tasks.Count == 0
        ? "No tasks yet"
        : $"{Tasks.Count(task => !task.IsComplete)} of {Tasks.Count} remaining";

[RelayCommand(CanExecute = nameof(CanAddTask))]
private async Task AddTaskAsync()
{
    string title = NewTaskTitle.Trim();
    Tasks.Add(new TaskItem { Title = title });
    NewTaskTitle = string.Empty;
    RefreshTaskState();
    await SaveAsync($"Added \"{title}\".");
}

[RelayCommand]
private async Task DeleteTaskAsync(TaskItem task)
{
    Tasks.Remove(task);
    RefreshTaskState();
    await SaveAsync($"Deleted \"{task.Title}\".");
}

private bool CanAddTask() => !string.IsNullOrWhiteSpace(NewTaskTitle);

partial void OnNewTaskTitleChanged(string value)
{
    AddTaskCommand.NotifyCanExecuteChanged();
}

Kontrollera att assistenten inte blockerar asynkront arbete med .Wait() eller .Result. Lagringsåtgärder bör inväntas.

Vymodellen kan fånga upp lagringsfel när den konverterar dem till ett meddelande för sidan:

private async Task SaveAsync(string successMessage)
{
    try
    {
        await TaskStorage.SaveAsync(Tasks);
        ShowStatus(successMessage);
    }
    catch (Exception ex)
    {
        ShowStatus($"Changes couldn't be saved: {ex.Message}");
    }
}

Det här är inte en tyst reservlösning: användaren får ett felmeddelande, och visningsmodellen påstår inte att sparandet lyckades.

Skapa och inspektera

Skapa för x64. Behandla varningar som arbetsuppgifter, särskilt varningar om bindning och nullbara referenser:

dotnet build -p:Platform=x64

Be assistenten att förklara varje paket som lagts till. För den här appen ska inget nytt paket behövas utöver referenserna Windows App SDK och MVVM Toolkit i mallen.