I'm experiencing a strange behavior in my WinUI 3 application with a ListView displaying a collection of items. The CheckBox in each item template is bound to a bool property called IsComplete. This is a multi-client, server-synchronized application where data changes can occur from different clients and are synced through the server.
<ListView
Grid.Row="1"
Margin="10"
ItemsSource="{x:Bind ViewModel.Items}"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox
Margin="10"
Command="{Binding ViewModel.EditItemCommand, ElementName=ThisPage}"
CommandParameter="{Binding Id}"
Content="{Binding Title}"
IsChecked="{Binding IsComplete}" />
<TextBlock Margin="10" Text="Checked: " />
<TextBlock Text="{Binding IsComplete}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
[ObservableProperty]
private ConcurrentObservableCollection<TodoItem> items = [];
[RelayCommand]
public async Task RefreshItemsAsync(CancellationToken cancellationToken = default)
{
try
{
IsRefreshing = true;
// Synchronize data with the remote service (if any).
await service.SynchronizeAsync(cancellationToken);
// Pull all items from the database.
IEnumerable<TodoItem> itemsFromDatabase = await service.TodoItems.OrderBy(item => item.Id).ToListAsync(cancellationToken);
// Replace all the items in the collection.
Items.ReplaceAll(itemsFromDatabase);
//Items.Clear();
//_ = Items.AddRange(itemsFromDatabase);
}
catch (Exception ex)
{
NotificationHandler?.Invoke(this, new NotificationEventArgs(ex.GetType().Name, ex.Message, true));
}
finally
{
IsRefreshing = false;
NotificationHandler?.Invoke(this, new NotificationEventArgs("Items Refreshed", "", false));
}
}
public class TodoItem : OfflineClientEntity
{
private bool _isComplete = false;
public string Title { get; set; } = string.Empty;
public bool IsComplete { get; set; } = false;
public override string ToString()
=> JsonSerializer.Serialize(this);
}
using System;
using System.ComponentModel.DataAnnotations;
namespace TodoApp.WinUI3.Database;
/// <summary>
/// An abstract class for working with offline entities.
/// </summary>
public abstract class OfflineClientEntity
{
[Key]
public string Id { get; set; }
public DateTimeOffset? UpdatedAt { get; set; }
public string Version { get; set; }
public bool Deleted { get; set; }
}
First of all, I'm not familiar with ConcurrentObservableCollection
, so I'm assuming that it's based on ObservableCollection.
Now, when you apply changes in your todo items, usually you need to implement INotifyPropertyChanged. You can do it by yourself but since you are using the CommunityToolkit.Mvvm, you can create an ObservableObject base class for TodoItem
or just wrap it with a wrapper class.
public partial class TodoItemViewModel(TodoItem todoItem) : ObservableObject
{
private readonly TodoItem _todoItem = todoItem;
public string Title
{
get => _todoItem.Title;
set => SetProperty(_todoItem.Title, value, _todoItem, (model, value) => model.Title = value);
}
public bool IsComplete
{
get => _todoItem.IsComplete;
set => SetProperty(_todoItem.IsComplete, value, _todoItem, (model, value) => model.IsComplete = value);
}
}