Search code examples
imagetextxamarin.formshighlight

Xamarin Forms: Flowlistview item tapping issue


I am implementing a game in my xamarin forms application, a name match game. It has two lists: one for showing the images and the other one for showing the names. The player taps the image from the top list and then taps the name from the bottom list(or name first then image). If they match the player gets points and tapped image and name will be removed from lists.

I am using flowlistview for showing the image list and name list because I need to show multiple items in a row. When tapping the image and name I have done the matching and remove the image and name if they matched. But I need to highlight the selected image or selected name when tapping and disable selection for other items. I have done highlighting feature using this thread, but it is not working perfectly. Sometimes multiple images are highlighting and sometimes when selecting an image on the top name on the below list is automatically highlighting.

I have created a sample project and uploaded it here. Please help me to complete this game. We have this game on our website, https://www.catholicbrain.com/edu-namematch/39524/1/the-two-great-commandments , please have a look at it for the working of the game. I will give you login details by DM.

Edit 1

@LucasZhang-MSFT I am accepting that, but the current question is different. It has 2 different flowlistviews. On the top an image list and on the bottom a name list. This is a simple game for kids, the player taps the image from the top list and then taps the name from the bottom list(or name first then image). If they match the player gets points and tapped image and name will be removed from lists. When not match I reset the items background colors like below:

foreach (var item1 in ImageItems)
{
    item.BGColor = Color.White;
}

foreach (var item2 in NameItems)
{
    item.BGColor = Color.White;
}
OnPropertyChanged("NameMatchImagItems");
OnPropertyChanged("NameMatchNameItems");

After this point, multiple images are highlighting and sometimes when selecting an image on the top name on the below list is automatically highlighting. If you have time, can you please download the sample and have a look? I tried my best, but no luck.


Solution

  • Cause:

    You set the ItemsSource of two flowlistview with the same source _allItems !!

    Solution:

    in xaml

    <ContentPage.Content>
        <StackLayout Orientation="Vertical">
    
            <!--imageflowlistview-->
            <flv:FlowListView 
                x:Name="NameMatchImageList"
                FlowItemTappedCommand="{Binding ImageItemTappedCommand}"
                FlowItemsSource="{Binding ImageItems}"
                FlowColumnCount="2"
                FlowLastTappedItem="{Binding LastImageTappedItem}"
                HasUnevenRows="True">
                <flv:FlowListView.FlowColumnTemplate>
                    <DataTemplate>
                            <StackLayout BackgroundColor="{Binding BGColor}"  Orientation="Vertical">
                                <Frame 
                                    Padding="5"
                                    Margin="5"
                                    HasShadow="False"
                                    BorderColor="#a4e6f9"
                                    CornerRadius="15">
    
                                <ffimageloading:CachedImage 
                                        Source="{Binding imageUrl, Converter={StaticResource urlJoinConverter}}"
                                        HorizontalOptions="FillAndExpand"
                                        VerticalOptions="FillAndExpand"
                                        HeightRequest="100"
                                        Aspect="AspectFill"/>
                                </Frame>
                            </StackLayout>
                    </DataTemplate>
                </flv:FlowListView.FlowColumnTemplate>
            </flv:FlowListView>
    
            <!--NamesFlowlistview-->
            <flv:FlowListView 
                    x:Name="NameMatchNameList"
                    FlowItemTappedCommand="{Binding NameItemTappedCommand}"
                    FlowItemsSource="{Binding NameItems}"
                    FlowColumnCount="2"
                    FlowLastTappedItem="{Binding LastNameTappedItem}"
                    HasUnevenRows="True">
                <flv:FlowListView.FlowColumnTemplate>
                    <DataTemplate>
                        <StackLayout Orientation="Vertical">
                            <Label 
                                TextColor="Black"
                                FontSize="Large"
                                BackgroundColor="{Binding BGColor}"
                                HorizontalTextAlignment="Center"
                                VerticalTextAlignment="Center"
                                Text="{Binding name}"/>
                        </StackLayout>
                    </DataTemplate>
                </flv:FlowListView.FlowColumnTemplate>
            </flv:FlowListView>
        </StackLayout>
    </ContentPage.Content>
    

    in code behind

    namespace FlowListView_Tap
    {
        class NameMatchViewModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            public ObservableCollection<NameMatchList> imageItems;
            public ObservableCollection<NameMatchList> ImageItems { get
                {
                    return imageItems;
                }
                set {
                   if(value!=null)
                    {
                        imageItems = value;
                        OnPropertyChanged("ImageItems");
                    }
                }
            }
            public ObservableCollection<NameMatchList> nameItems;
            public ObservableCollection<NameMatchList> NameItems
            {
                get
                {
                    return nameItems;
                }
                set
                {
                    if (value != null)
                    {
                        nameItems = value;
                        OnPropertyChanged("NameItems");
                    }
                }
            }
    
    
    
            public bool isImageSelected = false;
            public bool isNameSelected = false;
            public ICommand NameItemTappedCommand { get; set; }
            public ICommand ImageItemTappedCommand { get; set; }
    
    
            private NameMatchList lastImageTappedItem;
            public NameMatchList LastImageTappedItem
            {
                get
                {
                    return lastImageTappedItem;
                }
    
                set
                {
                    if(value!=null)
                    {
                        lastImageTappedItem = value;
                        OnPropertyChanged("LastImageTappedItem");
                    }
                }
            }
    
    
            private NameMatchList lastNameTappedItem;
            public NameMatchList LastNameTappedItem
            {
                get
                {
                    return lastNameTappedItem;
                }
    
                set
                {
                    if (value != null)
                    {
                        lastNameTappedItem = value;
                        OnPropertyChanged("LastNameTappedItem");
                    }
                }
            }
    
            public NameMatchViewModel()
            {
                ImageItemTappedCommand = new Command((obj) => {
                    try
                    {
                        //reset the bg color 
                        foreach (var item in ImageItems)
                        {
                            item.BGColor = Color.White;
                        }
                        NameMatchList imageList = obj as NameMatchList;
                        int index = ImageItems.IndexOf(imageList);
                        imageList.BGColor = Color.Red;
                        ///ImageItems.RemoveAt(index);
                        //ImageItems.Insert(index, imageList);
    
                        //Storing name and imageurl to local db
                        Application.Current.Properties["NameMatchImageList_Image"] = imageList.imageUrl;
                        Application.Current.Properties["NameMatchImageList_Name"] = imageList.name;
                        Application.Current.Properties["ImageItem"] = imageList;
                        isImageSelected = true;
    
                        if (isImageSelected && isNameSelected)
                        {
                            //If both image and name selected by player startes checking the matching
                            StartNameMatchCheck(imageList);
                        }
                    }
                    catch (Exception imagetapEx)
                    {
                        Debug.WriteLine("imagetapEx:>>" + imagetapEx);
                    }
                });
    
                NameItemTappedCommand = new Command((obj) => {
                    try
                    {
                        //reset the bg color 
                        foreach (var item in NameItems)
                        {
                            item.BGColor = Color.White;
                        }
                        NameMatchList nameList = obj as NameMatchList;
                        int index = NameItems.IndexOf(nameList);
                        nameList.BGColor = Color.Red;
                        //NameItems.RemoveAt(index);
                        //NameItems.Insert(index, nameList);
    
                        //Storing name and imageurl to local db
                        Application.Current.Properties["NameMatchNameList_Image"] = nameList.imageUrl;
                        Application.Current.Properties["NameMatchNameList_Name"] = nameList.name;
                        Application.Current.Properties["NameItem"] = nameList;
                        isNameSelected = true;
    
                        if (isImageSelected && isNameSelected)
                        {
                            //If both image and name selected by player startes checking the matching
                            StartNameMatchCheck(nameList);
                        }
                    }
                    catch (Exception nametapEx)
                    {
                        Debug.WriteLine("nametapEx:>>" + nametapEx);
                    }
                });
            }
    
            public async void StartNameMatchCheck(NameMatchList item)
            {
                isImageSelected = false;
                isNameSelected = false;
    
                //Fetching data from local db
                string NameMatchImageListImage = Application.Current.Properties["NameMatchImageList_Image"].ToString();
                string NameMatchImageListName = Application.Current.Properties["NameMatchImageList_Name"].ToString();
    
                string NameMatchNameListImage = Application.Current.Properties["NameMatchNameList_Image"].ToString();
                string NameMatchNameListName = Application.Current.Properties["NameMatchNameList_Name"].ToString();
    
                //Match check
                if ((NameMatchImageListImage == NameMatchNameListImage) && (NameMatchImageListName == NameMatchNameListName))
                {
                    await Application.Current.MainPage.DisplayAlert("Alert", "Success", "Ok");
                    //Removing the items from list if they match
    
    
    
                    ImageItems.Remove(LastImageTappedItem);
                    NameItems.Remove(LastNameTappedItem);
                    
                    LastImageTappedItem = null;
                    LastNameTappedItem = null;
                }
                else
                {
                    await Application.Current.MainPage.DisplayAlert("Alert", "Failed", "Ok");
                    //resetting the colors
                    LastImageTappedItem.BGColor = Color.White;
                    LastNameTappedItem.BGColor = Color.White;
    
    
                }
            }
    
            public async void CallNameMatch()
            {
                try
                {
                    //HttpClient client = new HttpClient();
                    //var nameMatchResponse = await client.GetAsync("");
                    //if (nameMatchResponse.IsSuccessStatusCode)
                    //{
                    //    var Response = await nameMatchResponse.Content.ReadAsStringAsync();
                    //    var imageResponse = JsonConvert.DeserializeObject<Games>(Response.ToString());
                    //    var namematch = JsonConvert.DeserializeObject<Games>(Response.ToString());
                    ImageItems = new ObservableCollection<NameMatchList>();
                    ImageItems.Add(new NameMatchList() { name = "Comfort the Sorrowing", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/06/971/head/Comfort the Sorrowing.png" });
                    ImageItems.Add(new NameMatchList() { name = "Giving Food To The Hungry", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/23/784/head/Giving Food To The Hungry.png" });
                    ImageItems.Add(new NameMatchList() { name = "Pray for the Living and The Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/39/707/head/Pray for the Living and The Dead.png" });
                    ImageItems.Add(new NameMatchList() { name = "To bury the Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/54/828/head/To bury the Dead.png" });
                    
                    //shuffling image list
                    //Random r1 = new Random();
                    //int randomIndex1 = 0;
                    //while (ImageItems.Count > 0)
                    //{
                    //    randomIndex1 = r1.Next(0, ImageItems.Count);
                    //    ImageItems[randomIndex1].BGColor = Color.White;
                    //    ImageItems.Add(ImageItems[randomIndex1]);
                    //    ImageItems.RemoveAt(randomIndex1);
                    //}
                    //NameMatchImagItems = new ObservableCollection<NameMatchList>(ImageItems);
                    NameItems = new ObservableCollection<NameMatchList>();
                    NameItems.Add(new NameMatchList() { name = "To bury the Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/54/828/head/To bury the Dead.png" });
                    NameItems.Add(new NameMatchList() { name = "Pray for the Living and The Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/39/707/head/Pray for the Living and The Dead.png" });
                    NameItems.Add(new NameMatchList() { name = "Comfort the Sorrowing", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/06/971/head/Comfort the Sorrowing.png" });
                    NameItems.Add(new NameMatchList() { name = "Giving Food To The Hungry", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/23/784/head/Giving Food To The Hungry.png" });
    
                    //shuffling name list
                    //Random r2 = new Random();
                    //int randomIndex2 = 0;
                    //while (NameItems.Count > 0)
                    //{
                    //    randomIndex2 = r2.Next(0, NameItems.Count);
                    //    NameItems[randomIndex2].BGColor = Color.White;
                    //    NameItems.Add(NameItems[randomIndex2]);
                    //    NameItems.RemoveAt(randomIndex2);
                    //}
                   // NameMatchNameItems = new ObservableCollection<NameMatchList>(NameItems);
                    //}
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("NMException:>" + ex);
                }
            }
    
            
    
            
            protected void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
    
            public void ShowAlert(string message)
            {
                Device.BeginInvokeOnMainThread(async () => {
                    await Application.Current.MainPage.DisplayAlert("Alert", message, "Ok");
                });
            }
        }
    }