3
votes

Am trying to Load a Content View in a Content page. When I run the code it doesn't hit my Content View. I am assigning two Bindable parameters from my content page.

Content Page:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:templates="clr-namespace:G2.Scanner.Views.Templates"           
         xmlns:viewModelBase="clr-namespace:G2.Scanner.ViewModels.Base;assembly=G2.Scanner"              
         xmlns:telerikPrimitives="clr-namespace:Telerik.XamarinForms.Primitives;assembly=Telerik.XamarinForms.Primitives"
         xmlns:local ="clr-namespace:G2.Scanner.Views.Transaction"
         x:Class="G2.Scanner.Views.Transaction.TransactionView"
         viewModelBase:ViewModelLocator.AutoWireViewModel="true">

        <local:GenerateScreen x:Name="generateScreen" ControllerName="{Binding ControllerName}" IsBusy="{Binding IsBusy}"/>

</ContentPage>

Content View:

public partial class GenerateScreen : ContentView
{
    #region BindableProperties
    public static readonly BindableProperty ControllerNameProperty = BindableProperty.Create("ControllerName", typeof(string), typeof(GenerateScreen));
    public static readonly BindableProperty IsBusyProperty = BindableProperty.Create("IsBusy", typeof(bool), typeof(GenerateScreen));
    #endregion

    #region Properties
    public string ControllerName
    {
        get { return (string)GetValue(ControllerNameProperty); }
        set { SetValue(ControllerNameProperty, value); }
    }

    public bool IsBusy
    {
        get { return (bool)GetValue(IsBusyProperty); }
        set { SetValue(IsBusyProperty, value); }
    }
    #endregion

    public GenerateScreen()
    {
        InitializeComponent();
        DesignScreenAsync();
    }

    public async void DesignScreenAsync()
    {
        IsBusy = true;

        // Some Design code..

        #region render
        scroll = new ScrollView
        {
            Content = layout
        };
        TransactionElements.Children.Add(scroll);
        #endregion
        IsBusy = false;
    }

Content View accepts two bindable property and is used at the time of load.

Content View Xaml:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="G2.Scanner.Views.Transaction.GenerateScreen">
  <ContentView.Content>
        <StackLayout x:Name="TransactionElements"/>
    </ContentView.Content>
</ContentView>

I want this view to be rendered at the time content page is loaded. I figured out that this works if I don't use bindable properties in content view. But as per My requirement ControllerName and IsBusy in ContentView Class Should be Bindable Property. Any Help why it's not hitting this View.

2
Did you debug your view? are the bindings even set when DesignScreenAsync is called? - Johannes
@Johannes it doesn't hit the content view class 1st of all. yes, am trying everything by debugging only. - Mohammed Ameen
the breakpoint was put into the constructor and/or the DesignScreenAsync method? No one of them was hitten? - Diego Rafael Souza
@DiegoRafaelSouza had put breakpoint in both place but non hits. - Mohammed Ameen

2 Answers

1
votes

Thank you all for taking your time. I actually found a solution for myself.

What actually went wrong was in two places.

  1. The bindable properties will be only set after the initialization of the class. So if we call the DesignScreenAsync method in the constructor which takes the value of ControllerName will obviously have null. the solution is to call the method in ControllerName's PropertyChanged method.

Here is the code:

public static readonly BindableProperty ControllerNameProperty = BindableProperty.Create(nameof(ControllerName), typeof(string), typeof(GenerateScreen),default(string), propertyChanged: OnControllerPropertyChange);

private static void OnControllerPropertyChange(BindableObject bindable, object oldValue, object newValue)
{
    ((GenerateScreen)bindable).DesignScreenAsync();
}

Notice that I have used BindableObject to call the method. This is to call the method from the same instence.

  1. The reason why contentView was not hitting, is that I didn't set a default value for the bool IsBusyProperty, so it is important to set the default value for a bindable property.

Here is the code:

public static readonly BindableProperty IsBusyProperty = BindableProperty.Create(nameof(IsBusy), typeof(bool), typeof(GenerateScreen), default(bool), defaultBindingMode: BindingMode.TwoWay);
0
votes

Defining an async void method is most of the time a bad practice. To avoid this and probably solve your problem, you have 2 solutions depending on your need.

1) If DesignScreen only builds your UI without intensive call, you must execute it synchronously:

  public GenerateScreen()
   {
    InitializeComponent();
    DesignScreenAsync();
   }

   public void DesignScreen()
   {
    IsBusy = true;

    // Some Design code..

    #region render
    scroll = new ScrollView
    {
        Content = layout
    };
    TransactionElements.Children.Add(scroll);
        #endregion
        IsBusy = false;
       }

2) If you have intensive calls that need to be awaited. Perform intensive calls and and update UI using Device.BeginInvokeOnMainThread:

 public async void DesignScreenAsync()
 {
     IsBusy = true;

    await ApiIntensiveCall();
    // Some Design code..

    #region render
    Device.BeginInvokeOnMainThread(() =>
    {
      scroll = new ScrollView
      {
        Content = layout
      };
      TransactionElements.Children.Add(scroll);
      #endregion
    });

    IsBusy = false;

}