Jimmy
Jimmy

Reputation: 135

How to change color of Frame control when clicked in Xamarin.Forms with MVVM

I know this question has been asked many times but I haven't been able to make it work with the solutions provided in the other questions.

Here's the problem: I want to change the BackgroundColor of a frame when it is clicked and come back to it's color when another frame is clicked.

Page.xaml

<ScrollView Orientation="Horizontal" Grid.Row="1" HorizontalScrollBarVisibility="Never" BackgroundColor="Transparent">
        <StackLayout Orientation="Horizontal" 
                     Margin="15, 8, 0, 0">
            <StackLayout Orientation="Horizontal" BindableLayout.ItemsSource="{Binding Categories}">
                <BindableLayout.ItemTemplate>
                    <DataTemplate x:DataType="model:CategoryDTO">
                        <Frame BackgroundColor="{Binding Source={RelativeSource AncestorType={x:Type vm:PageViewModel}}, Path=FrameColor}"
                       CornerRadius="15"
                       VerticalOptions="Center"
                       HasShadow="False"
                       Padding="15, 8">
                            <Label Text="{Binding Name}"
                               FontSize="Micro"
                               HorizontalOptions="Center"
                               VerticalOptions="Center"
                               TextColor="White"
                               FontAttributes="Bold" />
                            <Frame.GestureRecognizers>
                                <TapGestureRecognizer
                                    NumberOfTapsRequired="1"
                                    Command="{Binding Source={RelativeSource AncestorType={x:Type vm:PageViewModel}}, Path=CategoryTapped}"
                                    CommandParameter="{Binding .}">
                                </TapGestureRecognizer>
                            </Frame.GestureRecognizers>
                        </Frame>
                    </DataTemplate>
                </BindableLayout.ItemTemplate>
            </StackLayout>

PageViewModel.cs

public class PageViewModel : BaseViewModel
{
    public ObservableCollection<CategoryDTO> Categories { get; }

    public string FrameColor { get; set; } = "#3d3d3d";

    public PageViewModel()
    {
        Categories = new ObservableCollection<CategoryDTO>();

        LoadCategoriesCommand = new Command(async () => await ExecuteLoadCategoriesCommand(), () => false);
        CategoryTapped = new Command<CategoryDTO>(async (categoryId) => await OnCategoryTapped(categoryId));
    }

    public Command LoadCategoriesCommand { get; }
    public Command<CategoryDTO> CategoryTapped { get; }

    private async Task OnCategoryTapped(CategoryDTO categoryDTO)
    {
        IsBusy = true;

        try
        {
            await ExecuteSomethingCommand(id);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }
        finally
        {
            IsBusy = false;
        }
    }
}

Thank you so much for your help.

Upvotes: 0

Views: 1231

Answers (3)

Denys Tsurkanovski
Denys Tsurkanovski

Reputation: 67

Your categoriesDTO should to realize INotifyPropertyChanged and in there include an IsSelected public property.

<Frame.Triggers>                                                            
   <DataTrigger TargetType="Frame"                                                                                 
               Binding="{Binding IsSelected}"                                                                                 
               Value="True">                                                                        
      <Setter Property ="BackgroundColor" Value ="Yellow"/>                                                                   
   </DataTrigger>                                                                  
   <DataTrigger TargetType="Frame"                                                                              
               Binding="{Binding IsSelected}"                                                                              
               Value="False">                                                                      
      <Setter Property ="BackgroundColor" Value ="White"/>                                                                
   </DataTrigger>                                                            
</Frame.Triggers>

Upvotes: 1

user16304318
user16304318

Reputation:

You can add focused event on Frame, like:

   <Frame x:Name="frame1" 
    BackgroundColor="Blue"
               Focused="onfocus">
        </Frame>
        <Frame x:Name="frame2" 
    BackgroundColor="Blue"
                Focused="onfocus">

code behind:

  public void onfocus(object obj, EventArgs e)
    {
        var myframe = obj as Frame;
        myframe.BackgroundColor = Color.Red;
    }

Upvotes: 0

Kudzai Dube
Kudzai Dube

Reputation: 101

  1. Your categoriesDTO should inherit from BindableBase and in there include an IsSelected public property.
  2. Create a converter class that inherits from IValueConverter this will return a color depending on whether or not an item has been selected
  3. In your .Xaml frame background color Bind that to the IsSelectedProperty and Assign the converter property to the converter created above
  4. Then in your tap gesture you want to bind that to the CategorySelectedCommand
  5. Finally in your ViewModel in the CategorySelectedCommand you want to reset the isSelected property for all list items to false except the selected item
  6. Then boom all should work as expected

I have created a sample app using prism MVVM that you can clone and run locally on your machine here

Upvotes: 0

Related Questions