PropertyChanged Event doesn't reflect new Image.Source on first run

asked 2021-05-03T02:28:58.637+00:00
SkyFire 26 Reputation points

I'm working on mobile app that kinda simulates how to build a Desktop Computer by dragging and dropping an image in order. I work with DropGestureRecognizer with AllowDrop set to True for the Drop Zone Image controls and DragGestureRecognizer with CanDrag set to True for the Drag Image Objects.

The idea is the Drag Image Objects will get dragged and dropped to a Drop Zone Image Control and if they dropped the correct Drag Image, the Drop Zone will accept this as the Image.Source and another Drop Zone will be stacked in front of it to work with the next Desktop Computer component. Otherwise, it would get rejected and the Image.Source will be set to empty or null.

However, i'm encountering a weird issue that, on the first incorrect image dragged, the Image.Source gets reset to empty or null by property but on the actual UI, it doesn't. On the succeeding EventHandler execution, it does get reset. I have a TapGestureRecognizer on the Drop Zone to check if there is an Image.Source set or not.

I'm sorry if my explanation is a bit confusing because English is not my native language and i'm having a hard time explaining it. So please refer to below example in GIF and my code:

View (.xaml):

<ContentPage.Content>
    <Grid RowDefinitions="Auto,60,*"
          ColumnDefinitions="*">

        <RelativeLayout Grid.Row="0" Grid.Column="0" HorizontalOptions="Center">
            <Image x:Name="imgCaseDropZone"
                   RelativeLayout.XConstraint="0"
                   RelativeLayout.YConstraint="0"
                   HeightRequest="{Binding ScreenWidth}"
                   WidthRequest="{Binding ScreenWidth}"
                   PropertyChanged="CaseDropZone_PropertyChanged"
                   BackgroundColor="LightGray">
                <Image.GestureRecognizers>
                    <DropGestureRecognizer AllowDrop="True" />
                    <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
                </Image.GestureRecognizers>
            </Image>
        </RelativeLayout>

        <Label Grid.Row="1" Grid.Column="0"
               x:Name="lblDirections"
               Text="Directions: Drag the components below in its proper order to the drop zone above."
               TextColor="Black"
               Padding="10"
               VerticalOptions="CenterAndExpand" 
               HorizontalOptions="CenterAndExpand" />

        <ScrollView Grid.Row="2"
                    Orientation="Horizontal"
                    Margin="5">
            <StackLayout Orientation="Horizontal">

                <!--#region Case -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding CaseIsVisible}">
                    <Image Grid.Row="0"
                           x:Name="imgCaseDragObject"
                           Source="{Binding CaseImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding CaseLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Case Cover -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding CaseCoverIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding CaseCoverImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding CaseCoverLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Case Screw -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding CaseScrewIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding CaseScrewImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding CaseScrewLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Hard Disk Drive -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding HardDiskDriveIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding HardDiskDriveImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding HardDiskDriveLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Heatsink -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding HeatsinkIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding HeatsinkImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding HeatsinkLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Memory Module -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding MemoryModuleIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding MemoryModuleImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding MemoryModuleLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Motherboard -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding MotherboardIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding MotherboardImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding MotherboardLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Motherboard Screw -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding MotherboardScrewIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding MotherboardScrewImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding MotherboardScrewLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Power Supply -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding PowerSupplyIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding PowerSupplyImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding PowerSupplyLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

                <!--#region Processor -->
                <Grid RowDefinitions="*,Auto"
                      IsVisible="{Binding ProcessorIsVisible}">
                    <Image Grid.Row="0"
                           Source="{Binding ProcessorImgSource}">
                        <Image.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" />
                        </Image.GestureRecognizers>
                    </Image>

                    <Label Grid.Row="1"
                           Text="{Binding ProcessorLabel}"
                           TextColor="White"
                           FontSize="Medium"
                           BackgroundColor="Gray"
                           HorizontalTextAlignment="Center" />
                </Grid>
                <!--#endregion-->

            </StackLayout>
        </ScrollView>
    </Grid>


</ContentPage.Content>

View Code (.xaml.cs):

 [XamlCompilation(XamlCompilationOptions.Compile)]
 public partial class AssemblyPage : ContentPage
 {
      string caseSource;


      public AssemblyPage()
      {
           InitializeComponent();
      }

      int dragCount = 0;
      private void CaseDropZone_PropertyChanged(object sender, PropertyChangedEventArgs e)
      {
           if (e.PropertyName == "Source")
           {
                caseSource = imgCaseDropZone.Source.ToString().Split(':').Last().Trim();

                if (string.IsNullOrEmpty(caseSource))
                {
                     return;
                }

                if (caseSource != "img_assembly_case.png")
                {
                     imgCaseDropZone.Source = string.Empty;

                     lblDirections.Text = $"Drag Count: {++dragCount}";
                }
           }
      }

      private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
      {
           Application.Current.MainPage.DisplayAlert("", $"{imgCaseDropZone.Source}", "OK");
      }
 }

ViewModel (.cs):

 public class AssemblyViewModel : BaseVM
 {
      public double ScreenWidth => Xamarin.Forms.Application.Current.MainPage.Width;

      #region Case
      bool caseIsVisible;
      public bool CaseIsVisible
      {
           get => caseIsVisible;
           set => SetProperty(ref caseIsVisible, value);
      }

      public string CaseImgSource { get; }
      public string CaseLabel { get; }
      #endregion

      #region CaseCover
      bool caseCoverIsVisible;
      public bool CaseCoverIsVisible
      {
           get => caseCoverIsVisible;
           set => SetProperty(ref caseCoverIsVisible, value);
      }

      public string CaseCoverImgSource { get; }
      public string CaseCoverLabel { get; }
      #endregion

      #region HardDiskDrive
      bool hardDiskDriveIsVisible;
      public bool HardDiskDriveIsVisible
      {
           get => hardDiskDriveIsVisible;
           set => SetProperty(ref hardDiskDriveIsVisible, value);
      }

      public string HardDiskDriveImgSource { get; }
      public string HardDiskDriveLabel { get; }
      #endregion

      #region CaseScrew
      bool caseScrewIsVisible;
      public bool CaseScrewIsVisible
      {
           get => caseScrewIsVisible;
           set => SetProperty(ref caseScrewIsVisible, value);
      }

      public string CaseScrewImgSource { get; }
      public string CaseScrewLabel { get; }
      #endregion

      #region Heatsink
      bool heatsinkIsVisible;
      public bool HeatsinkIsVisible
      {
           get => heatsinkIsVisible;
           set => SetProperty(ref heatsinkIsVisible, value);
      }

      public string HeatsinkImgSource { get; }
      public string HeatsinkLabel { get; }
      #endregion

      #region MemoryModule
      bool memoryModuleIsVisible;
      public bool MemoryModuleIsVisible
      {
           get => memoryModuleIsVisible;
           set => SetProperty(ref memoryModuleIsVisible, value);
      }

      public string MemoryModuleImgSource { get; }
      public string MemoryModuleLabel { get; }
      #endregion

      #region Motherboard
      bool motherboardIsVisible;
      public bool MotherboardIsVisible
      {
           get => motherboardIsVisible;
           set => SetProperty(ref motherboardIsVisible, value);
      }

      public string MotherboardImgSource { get; }
      public string MotherboardLabel { get; }
      #endregion

      #region MotherboardScrew
      bool motherboardScrewIsVisible;
      public bool MotherboardScrewIsVisible
      {
           get => motherboardScrewIsVisible;
           set => SetProperty(ref motherboardScrewIsVisible, value);
      }

      public string MotherboardScrewImgSource { get; }
      public string MotherboardScrewLabel { get; }
      #endregion

      #region PowerSupply
      bool powerSupplyIsVisible;
      public bool PowerSupplyIsVisible
      {
           get => powerSupplyIsVisible;
           set => SetProperty(ref powerSupplyIsVisible, value);
      }

      public string PowerSupplyImgSource { get; }
      public string PowerSupplyLabel { get; }
      #endregion

      #region Processor
      bool processorIsVisible;
      public bool ProcessorIsVisible
      {
           get => processorIsVisible;
           set => SetProperty(ref processorIsVisible, value);
      }

      public string ProcessorImgSource { get; }
      public string ProcessorLabel { get; }
      #endregion


      public AssemblyViewModel()
      {
           CaseIsVisible = true;
           CaseImgSource = "img_assembly_case.png";
           CaseLabel = "Case";

           CaseCoverIsVisible = true;
           CaseCoverImgSource = "img_assembly_case_cover.png";
           CaseCoverLabel = "Case Cover";

           CaseScrewIsVisible = true;
           CaseScrewImgSource = "img_assembly_case_screw.png";
           CaseScrewLabel = "Case Screw";

           HardDiskDriveIsVisible = true;
           HardDiskDriveImgSource = "img_assembly_hard_disk_drive.png";
           HardDiskDriveLabel = "Hard Disk Drive";

           HeatsinkIsVisible = true;
           HeatsinkImgSource = "img_assembly_heat_sink.png";
           HeatsinkLabel = "Heatsink";

           MemoryModuleIsVisible = true;
           MemoryModuleImgSource = "img_assembly_memory_module.png";
           MemoryModuleLabel = "Memory Module";

           MotherboardIsVisible = true;
           MotherboardImgSource = "img_assembly_motherboard.png";
           MotherboardLabel = "Motherboard";

           MotherboardScrewIsVisible = true;
           MotherboardScrewImgSource = "img_assembly_motherboard_screw.png";
           MotherboardScrewLabel = "Motherboard Screw";

           PowerSupplyIsVisible = true;
           PowerSupplyImgSource = "img_assembly_power_supply.png";
           PowerSupplyLabel = "Power Supply";

           ProcessorIsVisible = true;
           ProcessorImgSource = "img_assembly_processor.png";
           ProcessorLabel = "Processor";
      }
 }

Current Build demo in GIF: [https://imgur.com/a/oLeM9DV][1] (i can't directly link the GIF because its too big)

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
4,803 questions
{count} votes