C
C#13mo ago
Jochem

✅ 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
Jochem
Jochem13mo ago
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>
Vi Ness
Vi Ness13mo ago
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>
Jochem
Jochem13mo ago
thank you! 👍