Reputation: 5395
I have a .Net MAUI app that uses CommunityToolkit.Mvvm. I have a CollectionView of buttons bound to a list of DateTime. On pressing any button a command is called that sets SelectedTime property of the ViewModel to a corresponding value.
Here is some ViewModel code:
[ObservableProperty]
[Required(ErrorMessage = "Please select the time of your appointment.")]
private DateTime? _selectedTime;
public List<DateTime> Times { get; set; } = new ()
{ new DateTime(2000, 1, 1, 9, 0, 0), new DateTime(2000, 1, 1, 9, 30, 0), new DateTime(2000, 1, 1, 10, 0, 0), new DateTime(2000, 1, 1, 10, 30, 0),
new DateTime(2000, 1, 1, 11, 0, 0), new DateTime(2000, 1, 1, 11, 30, 0), new DateTime(2000, 1, 1, 12, 0, 0), new DateTime(2000, 1, 1, 12, 30, 0),
new DateTime(2000, 1, 1, 13, 0, 0), new DateTime(2000, 1, 1, 13, 30, 0), new DateTime(2000, 1, 1, 14, 0, 0), new DateTime(2000, 1, 1, 14, 30, 0),
new DateTime(2000, 1, 1, 15, 0, 0), new DateTime(2000, 1, 1, 15, 30, 0), new DateTime(2000, 1, 1, 16, 0, 0), new DateTime(2000, 1, 1, 16, 30, 0) };
[RelayCommand]
private void SelectTime(object parameter)
{
SelectedTime = (DateTime)parameter;
}
Here is my Page code:
<CollectionView ItemsSource="{Binding Times}" ItemsLayout="VerticalGrid, 3" Margin="5">
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView Padding="5">
<Button Text="{Binding ., StringFormat='{0:hh:mm tt}'}"
Command="{Binding BindingContext.SelectTimeCommand, Source={RelativeSource AncestorType={x:Type ContentPage}}}"
CommandParameter="{Binding .}">
</Button>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
The collection of buttons is displayes on the screen, but I cannot figure out how I can make a pressed button change its color, and restore it back when another button is pressed? I guess the color should be bound to the SelectedTime property, but cannot make it work.
Upvotes: 1
Views: 1438
Reputation: 1931
I understand you want each button to change color but is it not the complete row you want to change? I can't ask questions due to low rep. So my answer is to ditch the buttons and go with the SelectionChangedCommand, and the whole row will lit up when you click on it.
<CollectionView
x:Name="CollectionAppointment"
Margin="5"
ItemSizingStrategy="MeasureAllItems"
ItemsSource="{Binding Times, Mode=OneTime}"
SelectedItem="SelectedItem"
SelectionChangedCommand="{Binding SelectTimeCommand}"
SelectionChangedCommandParameter="{Binding SelectedItem, Source={RelativeSource Self}}"
SelectionMode="Single">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="3" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:Appointment">
<ContentView Padding="5">
<Label
BackgroundColor="Transparent"
Text="{Binding Time, StringFormat='{0:hh:mm tt}'}"
TextColor="Black" />
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
The ViewModel for clearification:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace MauiTest.ViewModels
{
public partial class TestViewModel : ObservableObject
{
[ObservableProperty]
private Appointment _selectedItem;
[ObservableProperty]
public ObservableCollection<Appointment> _times;
public TestViewModel()
{
Times = new()
{
new Appointment{ Time = new DateTime(2000, 1, 1, 9, 0, 0)}, new Appointment{ Time = new DateTime(2000, 1, 1, 9, 30, 0)},
new Appointment{ Time = new DateTime(2000, 1, 1, 10, 0, 0)},
new Appointment{ Time = new DateTime(2000, 1, 1, 10, 30, 0)}
};
}
[RelayCommand]
private void SelectTime(Appointment parameter)
{
}
}
public class Appointment
{
public DateTime Time { get; set; }
}
}
Upvotes: 1
Reputation: 13919
You can create a model for your each bound item and add a property (e.g.BgColor
) to set the background of the button, and implement interface INotifyPropertyChanged
for the model. Once you click the button, try to change the value of the property BgColor
, then the UI will refresh itself.
Based on your code, I created a demo and achieved this function.
You can refer to the following code:
1.create a class Item
and add the properties we need(Time
and BgColor
).
public class Item: INotifyPropertyChanged
{
// add a property Time
public DateTime Time { get; set; }
// add background color property
private Color _bgColor;
public Color BgColor
{
set { SetProperty(ref _bgColor, value); }
get { return _bgColor; }
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
2.create a viewmodel and initialize data
public partial class MyViewModel
{
public List<Item> Items { get; set; }
public ICommand SelectTimeCommand => new Command(selectedTime);
private void selectedTime(object obj)
{
if (obj != null )
{
Item item = obj is Item ? (Item)obj : null;
item.BgColor = Colors.Purple;
foreach (Item model in Items)
{
//here we can change the value of the passed Item
if (model != item)
{
model.BgColor = Colors.Blue;
}
}
}
}
public MyViewModel() {
Items = new List<Item>();
Items.Add(new Item { Time = new DateTime(2000, 1, 1, 9, 0, 0),BgColor= Colors.Blue });
Items.Add(new Item { Time = new DateTime(2000, 1, 1, 10, 0, 0), BgColor = Colors.Blue });
Items.Add(new Item { Time = new DateTime(2000, 1, 1, 11, 0, 0), BgColor = Colors.Blue });
Items.Add(new Item { Time = new DateTime(2000, 1, 1, 12, 0, 0), BgColor = Colors.Blue });
Items.Add(new Item { Time = new DateTime(2000, 1, 1, 13, 0, 0), BgColor = Colors.Blue });
Items.Add(new Item { Time = new DateTime(2000, 1, 1, 14, 0, 0), BgColor = Colors.Blue });
Items.Add(new Item { Time = new DateTime(2000, 1, 1, 15, 0, 0), BgColor = Colors.Blue });
}
}
3.bind the BgColor
on the yourpage.xaml
:
<?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"
x:Class="MauiCollectionViewApp.TestPage"
Title="TestPage"
xmlns:viewmodels="clr-namespace:MauiCollectionViewApp.ViewModels"
>
<ContentPage.BindingContext>
<viewmodels:MyViewModel></viewmodels:MyViewModel>
</ContentPage.BindingContext>
<VerticalStackLayout>
<CollectionView ItemsSource="{Binding Items}" ItemsLayout="VerticalGrid, 3" Margin="5"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView Padding="5">
<Button Text="{Binding Time, StringFormat='{0:hh:mm tt}'}" BackgroundColor="{Binding BgColor}"
Command="{Binding BindingContext.SelectTimeCommand, Source={RelativeSource AncestorType={x:Type ContentPage}}}"
CommandParameter="{Binding .}">
</Button>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
Upvotes: 1