question

skyfire-0325 avatar image
0 Votes"
skyfire-0325 asked JessieZhang-2116 commented

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

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)

dotnet-xamarin
· 5
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I tried to reproduce this problem according to the code you posted, but failed. What's the code of CaseDropZone_PropertyChanged , TapGestureRecognizer_Tapped and BaseVM? If it is convinient for you, could you please post a basic demo to github or onedriver so that we can test on our side?

In addition, can you elaborate on what features you want to implement? I'm not quite sure what you mean.

0 Votes 0 ·

Hi, the code is already posted and the CaseDropZone_PropertyChanged and TapGestureRecognizer_Tapped is under the View Code (.xaml.cs) code as shown on my question.

BaseVM is an empty class that inherits from BaseViewModel of MvvmHelpers by JamesMontemagno


I'll try uploading a copy of my code later today on GitHub for you to test, but the idea is also as said:

I'm working on a really simple app to simulate the assembly of a Desktop Computer by dragging an image of the component (Case, Motherboard, Processor, etc) into another Image Object. This has to be done in order of Case > Motherboard > Screw > Processor > Heat Sink > Memory Module > Hard Disk Drive > Power Supply > Case Cover > Screw. If the Heat Sink is dragged to an Image Control without the Processor being placed there first, the Image Control should have its .Source property reset to null or empty and this is where my issue occurs.

0 Votes 0 ·

Hello again, this is a copy of my code uploaded on GitHub: https://github.com/SkyEngine-OSP/PC-Trainer-App


0 Votes 0 ·

Sorry, I just saw your reply because the PT tool could not refresh in time.I will try myself and come back asap.

0 Votes 0 ·

Hi @skyfire-0325 ,I could run your code on my emulator. But I couldn't find the Screw image you mentioned above.

0 Votes 0 ·

0 Answers