2

I'm stuck trying to reuse an control template for a independent ContentPage as well as a ContentPage in a CarouselPage...

The main problem is that the CarouselPage doesn't support the ControlTemplate property. Therefore I'm forced to insert a ContentPage in the DataTemplate of the CarouselPage. This ContentPage then can get the ControlTemplate assigned but I run into the problem that the BindingContext is not the root of the ViewModel.

I'll also try to explain the issues with code:

I've create the template as shown below.

<!-- Loader view template -->
<ControlTemplate x:Key="LoaderViewTemplate">
    <AbsoluteLayout Padding="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">

        <!-- Content -->
        <ContentPresenter AbsoluteLayout.LayoutBounds="0, 0, 1, 1" AbsoluteLayout.LayoutFlags="All" />

        <!-- Loader -->
        <BoxView IsVisible="{TemplateBinding BindingContext.IsBusy}" BackgroundColor="Green" Opacity="0.5" AbsoluteLayout.LayoutBounds="0, 0, 1, 1" AbsoluteLayout.LayoutFlags="All" />
        <StackLayout IsVisible="{TemplateBinding BindingContext.IsBusy}" Padding="6" BackgroundColor="Gray" Orientation="Horizontal" AbsoluteLayout.LayoutBounds="0.5, 0.5, -1, -1" AbsoluteLayout.LayoutFlags="PositionProportional">
            <ActivityIndicator Color="White" IsRunning="{TemplateBinding BindingContext.IsBusy}" VerticalOptions="Center" WidthRequest="20" HeightRequest="20" />
            <Label TextColor="White" Text="Loading..." VerticalOptions="Center" />
        </StackLayout>

    </AbsoluteLayout>
</ControlTemplate>

The template is working correctly for the ContentPage shown below.

<ContentPage ...
             ControlTemplate="{StaticResource LoaderViewTemplate}">

    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
        ...
    </StackLayout>

</ContentPage>

But it doesn't work in the CarouselPage as shown below.

<CarouselPage ...
              ItemsSource="{Binding Tournament.Rounds}">

    <CarouselPage.ItemTemplate>
        <DataTemplate>
            <ContentPage ControlTemplate="{StaticResource LoaderViewTemplate}">
                ...
            </ContentPage>
        </DataTemplate>
    </CarouselPage.ItemTemplate>

</CarouselPage>

The BindingContext in the CarouselPage becomes a TournamentRoundModel from the Tournament.Rounds collection.

Does any one has an idea on how I can reach the root of the ViewModel within the independent ContentPage and the CarouselPage nested ContentPage?

Kind regards, Jop Middelkamp

2 Answers2

1

First of all if you need each ContentPage in CarousalPage to be able to refer to the root view-model, while providing the same to the ControlTemplate's binding(s).

Simplest way to do that would be to extend ContentPage to support a bindable property to hold this reference (to root view-model).

public class ExContentPage : ContentPage
{
    public static readonly BindableProperty RootViewModelProperty =
        BindableProperty.Create(
            "RootViewModel", typeof(object), typeof(ExContentPage),
            defaultValue: default(object));

    public object RootViewModel
    {
        get { return (object)GetValue(RootViewModelProperty); }
        set { SetValue(RootViewModelProperty, value); }
    }
}

Then you can update your shared control-template as:

<!-- Loader view template -->
<ControlTemplate x:Key="LoaderViewTemplate">
    <AbsoluteLayout Padding = "0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">

        <!-- Content -->
        <ContentPresenter .. />

        < !--Loader-- >
        <BoxView IsVisible= "{TemplateBinding RootViewModel.IsBusy}" BackgroundColor= "Green" .. />

        <StackLayout IsVisible= "{TemplateBinding RootViewModel.IsBusy}" .. >
            <ActivityIndicator Color= "White" IsRunning= "{TemplateBinding RootViewModel.IsBusy}"  />
            <Label TextColor= "White" Text= "Loading..." VerticalOptions= "Center" />
        </StackLayout>
    </AbsoluteLayout>
</ControlTemplate>

Sample usage would look like:

<local:ExContentPage ...
        xmlns:local="clr-namespace:CustomNamespace"     
        RootViewModel="{Binding}"
         ControlTemplate="{StaticResource LoaderViewTemplate}">

    <StackLayout HorizontalOptions = "Center" VerticalOptions="Center">
        ...
    </StackLayout>
</local:ExContentPage>

and,

<CarouselPage...
             x:Name="Parent" 
             ItemsSource="{Binding Tournament.Rounds}">
    <CarouselPage.ItemTemplate>
        <DataTemplate>
            <local:ExContentPage
                ControlTemplate = "{StaticResource LoaderViewTemplate}"
                RootViewModel="{Binding BindingContext, Source={x:Reference Parent}}">
                ...
            </ContentPage>
        </DataTemplate>
    </CarouselPage.ItemTemplate>
</CarouselPage>

Furthermore, if IsBusy is the only property that you need to refer in ControlTemplate - you can create an IsBusy bindable property in extended content-page; instead of RootViewModel.

Sharada Gururaj
  • 13,471
  • 1
  • 22
  • 50
0

If you are doing anything carousel related i would suggest you use this nuget package https://github.com/alexrainman/CarouselView instead of the default carousel page.

Fabien Mwamba
  • 91
  • 1
  • 3
  • I've just been switching from the CarouselView to CarouselPage because the CarouselView is to buggy. When pressing an entry on any but the first page the CarouselView control is moving away from the active view... Not workable and confirming the prerelease NuGet status. – Jop Middelkamp Oct 17 '17 at 08:29
  • @JopMiddelkamp yes.i totally agree with you. CarouselView doesnt support the listview when the template created by xaml. it s interesting. And as you told, its moving away from the active view – Fethullah Kaya Oct 06 '20 at 15:19