Getting a property value from a ContentView (child view) to the ContentPage (parent)

751 Views Asked by At

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;
            }

        }


    }
}
0

There are 0 best solutions below