简体   繁体   中英

Xamarin Forms shared ControlTemplate for ContentPage and CarouselPage

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

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 .

如果你正在做任何与轮播相关的事情,我建议你使用这个 nuget 包https://github.com/alexrainman/CarouselView而不是默认的轮播页面。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM