Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este paso, pida al asistente las partes no visuales de la aplicación. Mantener este cambio separado del XAML hace que los errores del compilador y los problemas de diseño sean más fáciles de aislar.
Solicitar un modelo y un servicio de almacenamiento
Utilice este indicador
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.
Compare el modelo generado con esta implementación:
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; }
}
El [ObservableProperty] atributo genera propiedades públicas y notificaciones de cambio. La clase debe ser partial para que el generador de código fuente de MVVM Toolkit pueda agregar ese código.
El servicio de almacenamiento debe tener este aspecto:
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 es adecuado aquí porque la aplicación tiene la identidad del paquete.
SaveLock impide que las operaciones de adición, eliminación y finalización escriban el mismo archivo simultáneamente. Si más adelante cambia el modelo de distribución, compruebe de nuevo la suposición de identidad del paquete en lugar de copiar esta opción de almacenamiento automáticamente.
Más información: Almacene y recupere la configuración y otros datos de la aplicación y ApplicationData.LocalFolder.
Solicitar el modelo de vista
A continuación, usa esta instrucción:
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.
Los patrones principales que se van a inspeccionar en la respuesta son:
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();
}
Compruebe que el asistente no bloquea el trabajo asincrónico con .Wait() o .Result. Se debe esperar a que finalicen las operaciones de almacenamiento.
El modelo de vista puede detectar excepciones de almacenamiento cuando las convierte en un mensaje para la página:
private async Task SaveAsync(string successMessage)
{
try
{
await TaskStorage.SaveAsync(Tasks);
ShowStatus(successMessage);
}
catch (Exception ex)
{
ShowStatus($"Changes couldn't be saved: {ex.Message}", hasError: true);
}
}
No se trata de una conmutación silenciosa: el usuario recibe un error y el modelo de vista no indica que el guardado se haya realizado con éxito.
Los comandos y SaveAsync llaman a dos asistentes pequeños.
RefreshTaskState genera una notificación de cambio para la propiedad calculada Summary para que la interfaz de usuario se actualice después de que cambie la lista de tareas y ShowStatus establezca las propiedades de estado observables a las que se enlaza la página:
[ObservableProperty]
public partial string StatusMessage { get; set; } = string.Empty;
[ObservableProperty]
public partial bool IsStatusOpen { get; set; }
[ObservableProperty]
public partial bool HasError { get; set; }
private void RefreshTaskState()
{
OnPropertyChanged(nameof(Summary));
OnPropertyChanged(nameof(HasNoTasks));
}
private void ShowStatus(string message, bool hasError = false)
{
StatusMessage = message;
HasError = hasError;
IsStatusOpen = true;
}
Como Summary es una propiedad calculada en lugar de una [ObservableProperty], debes generar manualmente su notificación de cambio cada vez que Tasks cambie.
Construir e inspeccionar
Compilación para x64. Trate las advertencias como elementos de trabajo, especialmente las advertencias de enlace y de referencias anulables:
dotnet build -p:Platform=x64
Pida al asistente que explique cada paquete que ha agregado. Para esta aplicación, no se debe necesitar ningún nuevo paquete más allá de las referencias de SDK de Aplicaciones para Windows y MVVM Toolkit en la plantilla.
Windows developer