Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym kroku poproś asystenta o niewizualne części aplikacji. Zachowanie tej zmiany niezależnie od języka XAML sprawia, że błędy kompilatora i problemy projektowe są łatwiejsze do izolowania.
Poproś o model i usługę przechowywania
Użyj tego monitu:
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.
Porównaj wygenerowany model z tą implementacją:
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; }
}
Atrybut [ObservableProperty] generuje właściwości publiczne i powiadomienia o zmianie. Klasa musi być partial taka, aby generator źródłowy zestawu narzędzi MVVM Toolkit mógł dodać ten kod.
Usługa magazynu powinna wyglądać następująco:
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 jest odpowiedni tutaj, ponieważ aplikacja ma tożsamość pakietu.
SaveLock uniemożliwia równoczesne zapisywanie tego samego pliku, usuwanie i wykonywanie operacji dodawania, usuwania i uzupełniania. Jeśli później zmienisz model dystrybucji, ponownie zweryfikuj założenie dotyczące tożsamości pakietu, zamiast automatycznie powielać ten wybór magazynu.
Dowiedz się więcej: Przechowywanie i pobieranie ustawień oraz innych danych aplikacji oraz applicationData.LocalFolder.
Poproś o model widoku
Następnie użyj tego monitu:
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.
Główne wzorce do sprawdzenia w odpowiedzi to:
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();
}
Sprawdź, czy asystent nie blokuje pracy asynchronicznej za pomocą .Wait() lub .Result. Należy oczekiwać na operacje magazynu.
Model widoku może przechwytywać wyjątki związane z magazynem danych podczas przekształcania ich w komunikat dla strony:
private async Task SaveAsync(string successMessage)
{
try
{
await TaskStorage.SaveAsync(Tasks);
ShowStatus(successMessage);
}
catch (Exception ex)
{
ShowStatus($"Changes couldn't be saved: {ex.Message}");
}
}
To nie jest ciche rozwiązanie awaryjne: użytkownik otrzymuje komunikat o błędzie, a model widoku nie wskazuje, że zapis się powiódł.
Kompilowanie i inspekcja
Kompilacja dla architektury x64. Traktuj ostrzeżenia jako zadania robocze, zwłaszcza ostrzeżenia dotyczące powiązań i referencji dopuszczających wartość null:
dotnet build -p:Platform=x64
Poproś asystenta o wyjaśnienie każdego dodanego pakietu. W przypadku tej aplikacji nie powinno być potrzebne żadne nowe pakiety poza odwołaniami do zestawu narzędzi Zestaw SDK do aplikacji systemu Windows i MVVM Toolkit w szablonie.