Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questo passaggio, chiedere all'assistente le parti nonvisuali dell'app. Mantenere questa modifica separata da XAML rende gli errori del compilatore e i problemi di progettazione più facili da isolare.
Richiedere un modello e un servizio di archiviazione
Usare questa richiesta:
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.
Confrontare il modello generato con questa implementazione:
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; }
}
L'attributo [ObservableProperty] genera proprietà pubbliche e notifiche di modifica. La classe deve essere partial in modo che il generatore di origine MVVM Toolkit possa aggiungere tale codice.
Il servizio di archiviazione dovrebbe essere simile al seguente:
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 è appropriato qui perché l'app dispone di un'identità pacchetto.
SaveLock impedisce l'aggiunta, l'eliminazione e il completamento di operazioni di scrittura simultanea dello stesso file. Se in un secondo momento si modifica il modello di distribuzione, verificare di nuovo il presupposto dell'identità del pacchetto invece di copiare automaticamente questa scelta di archiviazione.
Altre informazioni: Archiviare e recuperare le impostazioni e altri dati dell'app e ApplicationData.LocalFolder.
Richiedere il modello di visualizzazione
Successivamente, usa questo prompt:
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.
I modelli principali da esaminare nella risposta sono:
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();
}
Verificare che l'assistente non blocchi il funzionamento asincrono con .Wait() o .Result. Le operazioni di archiviazione devono essere attese.
Il modello di visualizzazione può intercettare le eccezioni di archiviazione quando le converte in un messaggio per la pagina:
private async Task SaveAsync(string successMessage)
{
try
{
await TaskStorage.SaveAsync(Tasks);
ShowStatus(successMessage);
}
catch (Exception ex)
{
ShowStatus($"Changes couldn't be saved: {ex.Message}");
}
}
Questo non è un fallback invisibile all'utente: l'utente riceve un errore e il modello di visualizzazione non dichiara che il salvataggio è riuscito.
Compilare ed esaminare
Compila per x64. Considera gli avvisi come elementi di lavoro, in particolare gli avvisi relativi al binding e ai riferimenti nullable:
dotnet build -p:Platform=x64
Chiedere all'assistente di spiegare ogni pacchetto aggiunto. Per questa app non è necessario alcun nuovo pacchetto oltre i riferimenti SDK per app di Windows e MVVM Toolkit nel modello.