1

As a not so experienced Xamarin developer I am trying to retrieve a property value from a child page (ContentView) in my Parent page (ContentPage). I can find quite some examples how to get / set the value from the parent page to the child page but not the other way around.

Some more details:

In my ContentPage I have a CarouselView, this CarouselView has a DataTemplate which contains a ContentView, this ContentView has also a CarouselView inside it with 2 layers / 2 vertical carousel items.

When the position of the CarouselView inside the ContentView (the child page), changes position to the second item, the IndicatorView in the parent page should be set to invisible.

I am not so experienced using a BindableProperty but I think that is the way to go. I got it setup as following for now:

The parent page / ContentPage:

<local:ExtendedCarouselView
    x:Name="carousel"
    HorizontalScrollBarVisibility="Never"
    IndicatorView="activityIndicatorView"
    IsScrollAnimated="False"
    ItemsSource="{Binding ActivityData}"
    Position="{Binding Position, Mode=TwoWay}"
    VerticalOptions="FillAndExpand">
    <local:ExtendedCarouselView.ItemTemplate>
        <DataTemplate>
            <Frame Style="{StaticResource CarouselWorkaround}">
                <local:PCSActivityOverviewTemplate x:Name="testy" />
            </Frame>
        </DataTemplate>
    </local:ExtendedCarouselView.ItemTemplate>
</local:ExtendedCarouselView>

<IndicatorView
    x:Name="activityIndicatorView"
    Padding="0,0,0,30"
    IndicatorColor="{DynamicResource TranslucidBlack}"
    IsVisible="{Binding InnerCarouselViewPosition, Converter={StaticResource IndicatorVisibilityConverter}, Mode=TwoWay}"
    SelectedIndicatorColor="{DynamicResource BaseTextColor}"
    VerticalOptions="Start" />

The child page / ContenView (XAML):

<ContentView.Content>

    <CarouselView
        x:Name="carousel"

        ItemsSource="{Binding ., Converter={StaticResource OneToManyConverter}, ConverterParameter=2}"
        VerticalOptions="FillAndExpand"
        VerticalScrollBarVisibility="Never"
        PositionChanged="carousel_PositionChanged"> <!-- The event which should change the property 'InnerCarouselViewPosition' -->

        <CarouselView.ItemTemplate>
            <grial:IntMemberTemplateSelector MemberName="Position">
                <grial:IntMemberTemplateSelector.Items>

                    <!--  CAROUSEL'S PAGE 0  -->
                    <grial:IntMemberTemplateSelectorItem Value="0">
                        <DataTemplate>

                            <!-- Other elements... -->

                        </DataTemplate>
                    </grial:IntMemberTemplateSelectorItem>


                    <!--  CAROUSEL'S PAGE 1  -->
                    <grial:IntMemberTemplateSelectorItem Value="1">
                        <DataTemplate>

                            <!-- Other elements... -->

                        </DataTemplate>
                    </grial:IntMemberTemplateSelectorItem>

                </grial:IntMemberTemplateSelector.Items>
            </grial:IntMemberTemplateSelector>
        </CarouselView.ItemTemplate>

    </CarouselView>

</ContentView.Content>

The ContenView (C#/.cs):

public partial class PCSActivityOverviewTemplate : ContentView
{
    public static BindableProperty CurrentChildCarouselViewLocationProperty =
       BindableProperty.Create(
           nameof(CurrentChildCarouselViewLocationProperty),
           typeof(int),
           typeof(CarouselView),
           defaultValue: 1);

    public int CurrentChildCarouselViewLocation
    {
        get { return (int)GetValue(CurrentChildCarouselViewLocationProperty); }
        set { SetValue(CurrentChildCarouselViewLocationProperty, value); }
    }

    private void carousel_PositionChanged(object sender, PositionChangedEventArgs e)
    {
        CarouselView _carouselView = (CarouselView)sender;
        CurrentChildCarouselViewLocationProperty = _carouselView.Position;  
    }

    ... code omitted

}

When the inner carousel view position's changes then the bindable property should be set, this property should be used in the parent page to set the indicator view visible / invisible using a converter (position 0 = visible, position 1 = invisible). For some, probably a quite obvious, reason the above is not working.

The Visibility converter:

public class CarouselIndicatorVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value != 1; 
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 0 : 1;
    }
}

UPDATE*

I also tried to use a binding as the Position Property in my ViewModel, while the binding value changes, I cant access it in the Parent page nothing happens, the converter is not triggered), I removed the bindable property.

The new Content page XAML (the parent Carousel):

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    ...
    >
    <ContentPage.Resources>
        <ResourceDictionary>
            <local:CarouselIndicatorVisibilityConverter x:Key="IndicatorVisibilityConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.Content>

        <Grid>
            
            <Grid>
               
                <local:ExtendedCarouselView
                    x:Name="carousel"
                    HorizontalScrollBarVisibility="Never"
                    IndicatorView="activityIndicatorView"
                    IsScrollAnimated="False"
                    ItemsSource="{Binding ActivityData}"
                    Position="{Binding Position, Mode=TwoWay}"
                    VerticalOptions="FillAndExpand">
                    <local:ExtendedCarouselView.ItemTemplate>
                        <DataTemplate>
                            <Frame Style="{StaticResource CarouselWorkaround}">
                                <local:PCSActivityOverviewTemplate x:Name="testy" />
                            </Frame>
                        </DataTemplate>
                    </local:ExtendedCarouselView.ItemTemplate>
                </local:ExtendedCarouselView>

                <IndicatorView
                    x:Name="activityIndicatorView"
                    Padding="0,0,0,30"
                    IndicatorColor="{DynamicResource TranslucidBlack}"
                    IsVisible="{Binding BindingContext.CurrentChildCarouselViewLocation, Source={x:Reference carousel}, Converter={StaticResource IndicatorVisibilityConverter}, Mode=TwoWay}"
                    SelectedIndicatorColor="{DynamicResource BaseTextColor}"
                    VerticalOptions="Start" />


            </Grid>
        </Grid>

    </ContentPage.Content>
</ContentPage>

XAML of the child page:

<?xml version="1.0" encoding="UTF-8" ?>
<ContentView
    ...
    >
    
    <ContentView.Content>

        <CarouselView
            x:Name="carousel"
            IsBounceEnabled="False"
            ItemsSource="{Binding ., Converter={StaticResource OneToManyConverter}, ConverterParameter=2}"
            Position="{Binding CurrentCarouselViewLocation}"
            PositionChanged="carousel_PositionChanged"
            VerticalOptions="FillAndExpand"
            VerticalScrollBarVisibility="Never">
            <CarouselView.ItemsLayout>
                <LinearItemsLayout
                    ItemSpacing="0"
                    Orientation="Vertical"
                    SnapPointsAlignment="Start"
                    SnapPointsType="MandatorySingle" />
            </CarouselView.ItemsLayout>

            <CarouselView.ItemTemplate>
                <grial:IntMemberTemplateSelector MemberName="Position">
                    <grial:IntMemberTemplateSelector.Items>

                        <!--  CAROUSEL'S PAGE 0  -->
                        <grial:IntMemberTemplateSelectorItem Value="0">
                            <DataTemplate>
                               
                               .. more elements omitted
                               
                            </DataTemplate>
                        </grial:IntMemberTemplateSelectorItem>

                        <!--  CAROUSEL'S PAGE 1  -->
                        <grial:IntMemberTemplateSelectorItem Value="1">
                            <DataTemplate>
                                
                                .. more elements omitted                                
                                      
                            </DataTemplate>
                        </grial:IntMemberTemplateSelectorItem>

                    </grial:IntMemberTemplateSelector.Items>
                </grial:IntMemberTemplateSelector>
            </CarouselView.ItemTemplate>

        </CarouselView>

    </ContentView.Content>
</ContentView>

The ViewModel

namespace PCS2.APP.ViewModels
{
    public class ActivityOverviewViewModel : ObservableObject
    {
        private List<ActivityLocation> activityData;
        private readonly IRoutingService _routingService;
        private double _screenOpacity;
        private bool _showLoadingAnimation;
        private int? _clientId;
        private int _position;
        private int _innerCarouselPosition;

        // Position of the Parent page CarouselView
        public int Position
        {
            get { return _position; }
            set { SetProperty(ref _position, value); }
        }

        // Data source for the child data
        public List<ActivityLocation> ActivityData
        {
            get { return activityData; }
            set { SetProperty(ref activityData, value); }
        }

    
        public double ScreenOpacity
        {
            get { return _screenOpacity; }
            set { SetProperty(ref _screenOpacity, value); }
        }

        public bool ShowLoadingAnimation
        {
            get { return _showLoadingAnimation; }
            set { SetProperty(ref _showLoadingAnimation, value); }
        }

        public ActivityOverviewViewModel(int? clientId = null, IRoutingService routingService = null)
            : base(listenCultureChanges: true)
        {
            _clientId = clientId;
            _routingService = routingService ?? Locator.Current.GetService<IRoutingService>();
            LoadData();
        }

        private async void LoadData()
        {
            try
            {
                ShowLoadingAnimation = true;
                ScreenOpacity = 0.1;

                // Getting the data
                var _activitiesData = await App.Database.GetActivityDataAsync(_clientId, DateTime.UtcNow);
                ActivityData = _activitiesData;

            }
            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                ShowLoadingAnimation = false;
                ScreenOpacity = 1.0;
            }

        }


    }
}
Nicolas
  • 2,277
  • 5
  • 36
  • 82
  • I don't think it is a good approach to have a CarouselView with a DataTemplate that contains another CarouselView. `ContentPage->CarouselView.DataTemplate->ContentView->CarouselView` – Cfun Feb 14 '21 at 12:43
  • The `IndicatorVisibilityConverter` is never triggered. Added the converter in the main post. – Nicolas Feb 14 '21 at 13:14
  • Yep, like so: ` ` but it looks like the bindableproperty is just not 'bubbled' back to the Parent page from the child page. – Nicolas Feb 14 '21 at 13:22
  • @Cfun Sadly this generates the error `Can not find the object referenced by 'testy'` – Nicolas Feb 14 '21 at 20:00
  • @Nicolas How do you get this error? We need more details. – Wendy Zang - MSFT Feb 15 '21 at 08:44
  • @WendyZang-MSFT also added my ViewModel (updated the main post), don't know what more details I can give you now, practically the entire page and it's contents are posted.. Also tried a few other ways, like adding the child carousel position as a ViewModel Binding and use that binding back in the Parent page, this also is not working. It has to be some binding issue I suppose.. – Nicolas Feb 15 '21 at 09:27
  • Have you used the same viewmodel for the parent and child page? – Wendy Zang - MSFT Feb 25 '21 at 09:40

0 Answers0