✅ MAUI UI won't update after calling OnPropertyChanged from Command
Hey there, I am working on a MAUI application and came across a problem where the UI won't update after calling
OnPropertyChanged
I know for sure that function gets called because I tested that using a debugger and break points. I made a small test project that displays three buttons. Whenever a button is pressed, the color of the button should change from red to green. However this does not happen. I just can't seem to figure out why :(.
I've pasted the code for my view model and view below.3 Replies
public class Item
{
public Color Color { get; set; } = Colors.Red;
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public List<Item> List { get; set; } = new();
public ICommand ButtonPressedCommand { get; set; }
public ViewModel()
{
List.Add(new Item());
List.Add(new Item());
List.Add(new Item());
ButtonPressedCommand = new Command<Item>(ButtonPressed);
}
public void ButtonPressed(Item item)
{
item.Color = Colors.Green;
OnPropertyChanged(nameof(List));
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Item
{
public Color Color { get; set; } = Colors.Red;
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public List<Item> List { get; set; } = new();
public ICommand ButtonPressedCommand { get; set; }
public ViewModel()
{
List.Add(new Item());
List.Add(new Item());
List.Add(new Item());
ButtonPressedCommand = new Command<Item>(ButtonPressed);
}
public void ButtonPressed(Item item)
{
item.Color = Colors.Green;
OnPropertyChanged(nameof(List));
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Test"
x:Class="Test.MainPage"
x:DataType="local:ViewModel">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<CollectionView
ItemsSource="{Binding List}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Button
x:DataType="local:Item"
Text="Press me"
BackgroundColor="{Binding Color}"
CommandParameter="{Binding .}"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ViewModel}}, Path=ButtonPressedCommand}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Test"
x:Class="Test.MainPage"
x:DataType="local:ViewModel">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<CollectionView
ItemsSource="{Binding List}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Button
x:DataType="local:Item"
Text="Press me"
BackgroundColor="{Binding Color}"
CommandParameter="{Binding .}"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ViewModel}}, Path=ButtonPressedCommand}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Your Item should implement INotifyPropertyChanged so that the UI can get the new Color directly from the Item. Here's the same test but in Avalonia
public partial class MainWindowViewModel : ViewModelBase
{
public List<Item> List { get; set; }
public IBrush BG => Brushes.BlueViolet;
public MainWindowViewModel()
{
List = new()
{
new Item(),
new Item(),
new Item()
};
}
[RelayCommand]
private void ButtonPressed(Item item)
{
item.Color = Brushes.Green;
}
}
public class Item : INotifyPropertyChanged
{
private IBrush color = Brushes.Red;
public IBrush Color
{
get => color;
set
{
color = value;
OnPropertyChanged(nameof(Color));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class MainWindowViewModel : ViewModelBase
{
public List<Item> List { get; set; }
public IBrush BG => Brushes.BlueViolet;
public MainWindowViewModel()
{
List = new()
{
new Item(),
new Item(),
new Item()
};
}
[RelayCommand]
private void ButtonPressed(Item item)
{
item.Color = Brushes.Green;
}
}
public class Item : INotifyPropertyChanged
{
private IBrush color = Brushes.Red;
public IBrush Color
{
get => color;
set
{
color = value;
OnPropertyChanged(nameof(Color));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ListItemChanged.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ListItemChanged.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="ListItemChanged">
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<StackPanel>
<ListBox ItemsSource="{Binding List}" Background="{Binding BG}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Background="{Binding Color}" Content="Button!"
Command="{Binding $parent[Window].DataContext.ButtonPressedCommand}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ListItemChanged.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ListItemChanged.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="ListItemChanged">
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<StackPanel>
<ListBox ItemsSource="{Binding List}" Background="{Binding BG}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Background="{Binding Color}" Content="Button!"
Command="{Binding $parent[Window].DataContext.ButtonPressedCommand}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
thank you! 👍