Search code examples
xamarinxamarin.formsdata-binding

Xamarin Forms MVVM Show icon/image when clicking a row in the CollectionView


I would like to be able to show, ideally animate, the showing of an image/icon in the row when clicking the row and the after the selection has been made, navigate to another page while passing the selected row data id to next page.

enter image description here

I tried to use a Setter property CustomImageSource according to a sample not using mvvm, haven't been able to make it work,please help ( I know the code below is wrong).

The view:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">


    <ContentPage.Resources>       
        <Style TargetType="Grid">
            <Setter Property="VisualStateManager.VisualStateGroups">
                <VisualStateGroupList>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal" />
                        <VisualState x:Name="Selected">
                            <VisualState.Setters>

                                <Setter Property="BackgroundColor"
                                        Value="White" />

                                <Setter Property="CustomImageSource"
                                        Value="select.png" />
                               
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateGroupList>
            </Setter>
        </Style>
        
        <ResourceDictionary>
            <DataTemplate x:Key="MyCustomCellTemplate">
                <Grid>
                    <customControls:CustomFrame Padding="20" 
                                                Margin="20,10,20,10" 
                                                HeightRequest="50" 
                                                BackgroundColor="{StaticResource frameBackground}" 
                                                BorderColor="Transparent"
                                                CornerRadius="5"
                                                HasShadow="True">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="100*" />
                            </Grid.RowDefinitions>

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="20*" />
                                <ColumnDefinition Width="70*" />
                                <ColumnDefinition Width="10*" />
                            </Grid.ColumnDefinitions>

                            <Image Grid.Row="0"
                                   Grid.Column="0"
                                   Grid.ColumnSpan="1"
                                   Source="site_icon">
                            </Image>


                            <Label Grid.Row="0"
                                   Grid.Column="1"
                                   HorizontalOptions="Start"
                                   VerticalOptions="Center"
                                   FontSize="Small"
                                   FontAttributes="Bold"
                                   Text="{Binding Name}">
                            </Label>


                            <Image x:Name="SelectedIcon"
                                   Grid.Row="0"
                                   Grid.Column="2"
                                   Grid.ColumnSpan="1"
                                   HorizontalOptions="Center"
                                   Source="select.png">
                            </Image>

                        </Grid>

                    </customControls:CustomFrame>
                </Grid>
            </DataTemplate>
        </ResourceDictionary>
    </ContentPage.Resources>
    
    
    <customControls:GradientColorStack StartColor="{StaticResource gradientStartColor}"
                                       EndColor="{StaticResource gradientEndColor}">

        <Grid Margin="0" ColumnSpacing="0" RowSpacing="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">

            <Grid.RowDefinitions>
                <RowDefinition Height="8*"/>
                <RowDefinition Height="4*"/>
                <RowDefinition Height="3*"/>
                <RowDefinition Height="80*"/>
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20*" />
                <ColumnDefinition Width="80*" />
            </Grid.ColumnDefinitions>

            <Label Grid.Row="1"
                   Grid.ColumnSpan="2"
                   Text="Select Site"
                   FontSize="Medium"
                   TextColor="White"
                   VerticalOptions="Center"
                   HorizontalOptions="Center">
            </Label>

            <BoxView Grid.Row="3"
                     Grid.ColumnSpan="2"
                     Grid.RowSpan="4"
                     BackgroundColor="{StaticResource mainPageBackground}">
            </BoxView>

            <CollectionView
                            Grid.Row="4"
                            Grid.ColumnSpan="2"
                            EmptyView="No sites for the current user."
                            ItemsSource="{Binding ItemsCollection}"
                            ItemTemplate="{StaticResource MyCustomCellTemplate}"
                            SelectionMode="Single">
            </CollectionView>

        </Grid>
        
    </customControls:GradientColorStack>
    
</ContentPage>

the view model:

public class MyViewModelClass : BindableBase
    {
        private Page _currentPage;

        private IPageService _pageService;

        private List<CollectionItem> _source;

        public UserSitesVM(Page currentPage,                       
                           IPageService pageService)
        {
            _currentPage = currentPage;
            _pageService = pageService;
        }

        public override async Task InitializeAsync()
        {      
            await PopulateCollection();
        }

        private async Task PopulateCollection()
        {          
            // code to populate collection
            _source = await.........................
            ItemsCollection = new ObservableCollection<CollectionItem>(_source);
            
        }

        public ObservableCollection<CollectionItem> _itemsCollection
        public ObservableCollection<Site> ItemsCollection
        {
            get
            {
                return _itemsCollection;
            }
            set
            {
                _itemsCollection = value;
                RaisePropertyChanged();
            }
        }
        
         private string _customImageSource;
        public string CustomImageSource
        {
            get
            {
                return _customImageSource;
            }
            set
            {
                _customImageSource = value;
                RaisePropertyChanged();
            }

        }

        public static readonly BindableProperty CustomImageSourceProperty = BindableProperty.Create(nameof(CustomImageSource),
            typeof(string), typeof(Grid), defaultValue: string.Empty,
            propertyChanged: (SelectedIconSource, oldValue, newValue) =>
            {
                SelectedIconSource = ImageSource.FromFile((string)newValue);
            });
    }

Solution

  • Do you want to achieve the following result?

    enter image description here

    If so, I notice you used MVVM and want to add animate when showing image.

    You can add perperty in your ViewModel. I add Isfavourite property.

       public class MyModel: INotifyPropertyChanged
        {
            string name;
            public string Name
            {
                set
                {
                    if (name != value)
                    {
                        name = value;
                        OnPropertyChanged("Name");
    
                    }
                }
                get
                {
                    return name;
                }
            }
            bool _isfavourite = false;
            public bool Isfavourite
            {
                get
                {
                    return _isfavourite;
                }
    
                set
                {
                    if (_isfavourite != value)
                    {
                        _isfavourite = value;
                        OnPropertyChanged("Isfavourite");
    
                    }
                }
    
            }
    
           
            string _value;
            public string Value
            {
                set
                {
                    if (_value != value)
                    {
                        _value = value;
                        OnPropertyChanged("Value");
    
                    }
                }
                get
                {
                    return _value;
                }
            }
    
    
            private Color _textColor=Color.Green;
    
            public Color TextColor
            {
                get { return _textColor; }
                set
                {
                    _textColor = value;
    
                    OnPropertyChanged("TextColor");
    
                }
            }
    
         
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    

    Then you want to show the Image with animate when clicking the row. First of all, we should create an custom image. I add BindableProperty called Animate when the value is true, show the image and animate.

       public class CustomImage:Image
        {
            public static readonly BindableProperty AnimateProperty =
               BindableProperty.Create(nameof(Animate), typeof(bool), typeof(ImageButton), true, propertyChanged: OnEventNameChanged);
    
            private static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
            {
                if (newValue is bool)
                {
                    bool x = (bool)newValue;
                    if (x == true)
                    {
                        CustomImage customImage= bindable as CustomImage;
                        // you can add whatever animate here
                        customImage.FadeTo(1, 400);
                       // customImage.TranslateTo(-100, 0, 1000);
                    }
               }
            }
    
            public bool Animate
            {
                get => (bool)GetValue(AnimateProperty);
                set => SetValue(AnimateProperty, value);
            }
            public CustomImage()
            {
               
            }
        }
    }
    
    

    Then use this customImage to the CollectionView(bind same Isfavourite perperty Animate="{Binding Isfavourite}" IsVisible="{Binding Isfavourite}"). Note: I do not how is your achievement for your customView,So I delete it, And add SelectionChangedCommand and SelectionChangedCommandParameter for CollectionView

        <ContentPage.Resources>
            <Style TargetType="Grid">
                <Setter Property="VisualStateManager.VisualStateGroups">
                    <VisualStateGroupList>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />
                            <VisualState x:Name="Selected">
                                <VisualState.Setters>
    
                                    <Setter Property="BackgroundColor"
                                            Value="White" />
    
                                    <!--<Setter Property="CustomImageSource"
                                            Value="select.png" />-->
    
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateGroupList>
                </Setter>
            </Style>
    
            <ResourceDictionary>
                <DataTemplate x:Key="MyCustomCellTemplate">
                    <Grid>
                        <customControls:CustomFrame Padding="20" 
                                                    Margin="20,10,20,10" 
                                                    HeightRequest="50" 
                                                    BackgroundColor="Gray" 
                                                    BorderColor="Transparent"
                                                    CornerRadius="5"
                                                    HasShadow="True">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="100*" />
                                </Grid.RowDefinitions>
    
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="20*" />
                                    <ColumnDefinition Width="70*" />
                                    <ColumnDefinition Width="10*" />
                                </Grid.ColumnDefinitions>
    
                                <Image Grid.Row="0"
                                       Grid.Column="0"
                                       Grid.ColumnSpan="1"
                                       Source="site_icon">
                                </Image>
    
    
                                <Label Grid.Row="0"
                                       Grid.Column="1"
                                       HorizontalOptions="Start"
                                       VerticalOptions="Center"
                                       FontSize="Small"
                                       FontAttributes="Bold"
                                       Text="{Binding Name}">
                                </Label>
    
    
                                <customControls:CustomImage x:Name="SelectedIcon"
                                       Grid.Row="0"
                                       Grid.Column="2"
                                       Grid.ColumnSpan="1"
                                       HorizontalOptions="Center"
                                        Animate="{Binding Isfavourite}"
                                       IsVisible="{Binding Isfavourite}"
                                       Source="select.png" >
                                    
                                </customControls:CustomImage>
                               
    
                            </Grid>
    
                        </customControls:CustomFrame>
                    </Grid>
                </DataTemplate>
            </ResourceDictionary>
        </ContentPage.Resources>
    
    
        <!--<customControls:GradientColorStack StartColor="{StaticResource gradientStartColor}"
                                           EndColor="{StaticResource gradientEndColor}">-->
    
            <Grid Margin="0" ColumnSpacing="0" RowSpacing="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
    
                <Grid.RowDefinitions>
                    <RowDefinition Height="2*"/>
                    <RowDefinition Height="1*"/>
                    <RowDefinition Height="1*"/>
                    <RowDefinition Height="1*"/>
                    <RowDefinition Height="6*"/>
                </Grid.RowDefinitions>
    
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="20*" />
                    <ColumnDefinition Width="80*" />
                </Grid.ColumnDefinitions>
    
                <Label Grid.Row="1"
                       Grid.ColumnSpan="2"
                       Text="Select Site"
                       FontSize="Medium"
                       TextColor="Black"
                       VerticalOptions="Center"
                       HorizontalOptions="Center">
                </Label>
    
               <BoxView Grid.Row="3"
                         Grid.ColumnSpan="2"
                         Grid.RowSpan="4"
                         BackgroundColor="WhiteSmoke">
                </BoxView>
                <CollectionView
                    x:Name="MyCollectionView"
                                Grid.Row="4"
                                Grid.ColumnSpan="2"
                                EmptyView="No sites for the current user."
                                ItemsSource="{Binding Stats}"
                                ItemTemplate="{StaticResource MyCustomCellTemplate}"
                                SelectionChangedCommand="{Binding ColorChangeCommand}"
                                SelectionChangedCommandParameter="{Binding SelectedItem, Source={x:Reference MyCollectionView}}"
                                SelectionMode="Single">
                </CollectionView>
    
            </Grid>
    
        <!--</customControls:GradientColorStack>-->
    
    </ContentPage>
    
    

    Here is layout background code.

        public MainPage()
            {
                InitializeComponent();
    
                this.BindingContext = new MyViewModel(Navigation);
           
            }
    

    Here is my ViewModel. execute the ColorChangeCommand when click the item in the collectionview. Set the Isfavourite to true, show the Image. Then wait for 0.5 second, then navigate to the page1 that show the details information.

      public class MyViewModel
        {
           
            public ObservableCollection<MyModel> Stats { get; set; }
    
            public ICommand ColorChangeCommand { protected set; get; }
            public MyViewModel(INavigation navigation)
            {
    
                
                Stats = new ObservableCollection<MyModel>();
                Stats.Add(new MyModel() { Name="test1",  Value="1" });
                Stats.Add(new MyModel() { Name = "test2", Value = "2" });
                Stats.Add(new MyModel() { Name = "test3", Value = "3" });
    
    
    
                     ColorChangeCommand = new Command<MyModel>(async (key) =>
                     {
    
    
                         key.Isfavourite = !key.Isfavourite;
                         await Task.Delay(500);
                         await  navigation.PushModalAsync(new Page1(key));
                     });
    
            }
        }
    

    Here is Page1's backgroud code.

     [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class Page1 : ContentPage
        {
            public Page1(MyModel myModel)
            {
               
                InitializeComponent();
                MyLabel.Text = myModel.Name;
            }
        }