Trying to create a control template that inherits parent class and calls?

Hello everyone,
I am trying to make a template for a form on my Xamarin.Forms project.
I want to use a SyncFusion Carousel control, and since I reuse it on a number of forms, I would like to make a xaml view that I can just instantiate on the target form saving from having to recreate the control every time.

Here is the problem, I have TapGestureRecognizers and several other Data objects that get passed to this control.
Here are the samples of how it gets implemented as this time.

  1. <DataTemplate x:Key="CarouselitemTemplate">
  2. <StackLayout Orientation="Vertical">
  3. <StackLayout.GestureRecognizers>
  4. <TapGestureRecognizer
  5. CommandParameter="{Binding Path=.}"
  6. NumberOfTapsRequired="1"
  7. Tapped="OnTapped" />
  8. </StackLayout.GestureRecognizers>
  9. <Image
  10. Aspect="AspectFit"
  11. HeightRequest="350"
  12. Source="{Binding Path=Image, Converter={StaticResource Key=FillImageFromBytes}}"
  13. WidthRequest="325" />
  14. <Label HorizontalTextAlignment="Center" Text="{Binding Path=FileName}" />
  15. </StackLayout>
  16. </DataTemplate>

  17. ....
  18. <Label Text="Images" />
  19. <syncfusion:SfCarousel x:Name="carousel" ItemTemplate="{StaticResource Key=CarouselitemTemplate}" />
  20. ....

What I would like to do is this.
TemplateCarouselView.xaml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <syncfusion:SfCarousel
  3. x:Class="BoomStick.Views.TemplateCarouselView"
  4. xmlns="http://xamarin.com/schemas/2014/forms"
  5. xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  6. xmlns:syncfusion="clr-namespace:Syncfusion.SfCarousel.XForms;assembly=Syncfusion.SfCarousel.XForms">
  7. <syncfusion:SfCarousel.ItemTemplate>
  8. <DataTemplate>
  9. <StackLayout Orientation="Vertical">
  10. <StackLayout.GestureRecognizers>
  11. <TapGestureRecognizer
  12. CommandParameter="{Binding Path=.}"
  13. NumberOfTapsRequired="1"
  14. Tapped="{Binding Path=OnTapped}" />
  15. </StackLayout.GestureRecognizers>
  16. <Image
  17. Aspect="AspectFit"
  18. HeightRequest="350"
  19. Source="{Binding Path=Image, Converter={StaticResource Key=FillImageFromBytes}}"
  20. WidthRequest="325" />
  21. <Label HorizontalTextAlignment="Center" Text="{Binding Path=FileName}" />
  22. </StackLayout>
  23. </DataTemplate>
  24. </syncfusion:SfCarousel.ItemTemplate>
  25. </syncfusion:SfCarousel


TemplateCarouselView.xaml.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Syncfusion.SfCarousel.XForms;
  7. using Xamarin.Forms;
  8. using Xamarin.Forms.Xaml;
  9. namespace BoomStick.Views
  10. {
  11. [XamlCompilation(XamlCompilationOptions.Compile)]
  12. public partial class TemplateCarouselView : SfCarousel
  13. {
  14. public TemplateCarouselView()
  15. {
  16. InitializeComponent();
  17. }
  18. public static readonly BindableProperty OnTappedProperty = BindableProperty.Create(nameof(OnTapped), typeof(Command), typeof(Command));
  19. public Command OnTapped
  20. {
  21. get
  22. {
  23. return (Command)GetValue(OnTappedProperty);
  24. }
  25. set
  26. {
  27. SetValue(OnTappedProperty, value);
  28. }
  29. }
  30. }
  31. }

I figured I could use the BindableProperty to allow the calling instance to add it's own pointer to the Tap Callback function.
I could then implement this view like so.

  1. <myviews:TemplateCarouselView OnTapped="SomeFunction" />

The problems I have are thus.

  1. Can this implementation inherit the ViewModel within the template? or do I need to create BindableProperty elements for each then {Binding Path=xxx}

  2. The implementation for the OnTapped is not handing off to the TapGestureRecognizer I cannot figure out how to overload the incoming property to be accepted by the xaml implementation.

  3. Each instance that implements this View needs to have it's own overloaded OnTapped function, so I cannot just make it an internal call within the TemplateView.

  4. Can I make this template use it's own internal ViewModel while still being able to inherit the parent forms ViewModel?

  5. Can I use this type of a template to make a `CardView`, defining custom cards in this manner then creating an `ObservableCollection<MyControl>()`

Thank you for any ideas or pointers!

Cheers!
Jesse


3 Replies

JK Jesse Knott replied to Jesse Knott January 5, 2021 05:39 AM UTC

Hello everyone,
I am trying to make a template for a form on my Xamarin.Forms project.
I want to use a SyncFusion Carousel control, and since I reuse it on a number of forms, I would like to make a xaml view that I can just instantiate on the target form saving from having to recreate the control every time.

Here is the problem, I have TapGestureRecognizers and several other Data objects that get passed to this control.
Here are the samples of how it gets implemented as this time.

  1. <DataTemplate x:Key="CarouselitemTemplate">
  2. <StackLayout Orientation="Vertical">
  3. <StackLayout.GestureRecognizers>
  4. <TapGestureRecognizer
  5. CommandParameter="{Binding Path=.}"
  6. NumberOfTapsRequired="1"
  7. Tapped="OnTapped" />
  8. </StackLayout.GestureRecognizers>
  9. <Image
  10. Aspect="AspectFit"
  11. HeightRequest="350"
  12. Source="{Binding Path=Image, Converter={StaticResource Key=FillImageFromBytes}}"
  13. WidthRequest="325" />
  14. <Label HorizontalTextAlignment="Center" Text="{Binding Path=FileName}" />
  15. </StackLayout>
  16. </DataTemplate>

  17. ....
  18. <Label Text="Images" />
  19. <syncfusion:SfCarousel x:Name="carousel" ItemTemplate="{StaticResource Key=CarouselitemTemplate}" />
  20. ....

What I would like to do is this.
TemplateCarouselView.xaml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <syncfusion:SfCarousel
  3. x:Class="BoomStick.Views.TemplateCarouselView"
  4. xmlns="http://xamarin.com/schemas/2014/forms"
  5. xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  6. xmlns:syncfusion="clr-namespace:Syncfusion.SfCarousel.XForms;assembly=Syncfusion.SfCarousel.XForms">
  7. <syncfusion:SfCarousel.ItemTemplate>
  8. <DataTemplate>
  9. <StackLayout Orientation="Vertical">
  10. <StackLayout.GestureRecognizers>
  11. <TapGestureRecognizer
  12. CommandParameter="{Binding Path=.}"
  13. NumberOfTapsRequired="1"
  14. Tapped="{Binding Path=OnTapped}" />
  15. </StackLayout.GestureRecognizers>
  16. <Image
  17. Aspect="AspectFit"
  18. HeightRequest="350"
  19. Source="{Binding Path=Image, Converter={StaticResource Key=FillImageFromBytes}}"
  20. WidthRequest="325" />
  21. <Label HorizontalTextAlignment="Center" Text="{Binding Path=FileName}" />
  22. </StackLayout>
  23. </DataTemplate>
  24. </syncfusion:SfCarousel.ItemTemplate>
  25. </syncfusion:SfCarousel


TemplateCarouselView.xaml.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Syncfusion.SfCarousel.XForms;
  7. using Xamarin.Forms;
  8. using Xamarin.Forms.Xaml;
  9. namespace BoomStick.Views
  10. {
  11. [XamlCompilation(XamlCompilationOptions.Compile)]
  12. public partial class TemplateCarouselView : SfCarousel
  13. {
  14. public TemplateCarouselView()
  15. {
  16. InitializeComponent();
  17. }
  18. public static readonly BindableProperty OnTappedProperty = BindableProperty.Create(nameof(OnTapped), typeof(Command), typeof(Command));
  19. public Command OnTapped
  20. {
  21. get
  22. {
  23. return (Command)GetValue(OnTappedProperty);
  24. }
  25. set
  26. {
  27. SetValue(OnTappedProperty, value);
  28. }
  29. }
  30. }
  31. }

I figured I could use the BindableProperty to allow the calling instance to add it's own pointer to the Tap Callback function.
I could then implement this view like so.

  1. <myviews:TemplateCarouselView OnTapped="SomeFunction" />

The problems I have are thus.

  1. Can this implementation inherit the ViewModel within the template? or do I need to create BindableProperty elements for each then {Binding Path=xxx}

  2. The implementation for the OnTapped is not handing off to the TapGestureRecognizer I cannot figure out how to overload the incoming property to be accepted by the xaml implementation.

  3. Each instance that implements this View needs to have it's own overloaded OnTapped function, so I cannot just make it an internal call within the TemplateView.

  4. Can I make this template use it's own internal ViewModel while still being able to inherit the parent forms ViewModel?

  5. Can I use this type of a template to make a `CardView`, defining custom cards in this manner then creating an `ObservableCollection<MyControl>()`

Thank you for any ideas or pointers!

Cheers!
Jesse


Okay, I have gotten the page view to work, but I cannot bind to the Tapped event for the TapGestureRecognizer. How can I overload, or Bind to the event handler so I can pass the command over to it?

Cheers!
Jesse



JK Jesse Knott January 5, 2021 01:36 PM UTC

I got this part done, I was able to do it with creating a bunch of parameters, and creating a base prototype function for the tap handler.

Here is the template class I had to make

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using BoomStick.Models;
using BoomStick.Services;

using Syncfusion.SfCarousel.XForms;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace BoomStick.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class TemplateCarouselView : SfCarousel
    {
        public DataTemplate dt;

        public TemplateCarouselView()
        {
            InitializeComponent();

            dt = new DataTemplate(() =>
            {
                var keyLayout = new StackLayout();

                var bindPhoto = new Binding()
                {
                    Converter = new FillImageFromBytes(),
                    Path = "Image"
                };

                var bindTapped = new Binding()
                {
                    Path = "OnTapped"
                };

                var bindLocalPath = new Binding()
                {
                    Converter = new FillImageFromBytes(),
                    Path = "."
                };

                var tgRec = new TapGestureRecognizer()
                {
                    CommandParameter = bindLocalPath,
                    NumberOfTapsRequired = 1
                };

                tgRec.Tapped += async (s, e) =>
                {
                    var act = await Application.Current.MainPage.DisplayActionSheet(
                                        StringTools.GetStringResource("szPhotoOptions"),
                                        null,
                                        StringTools.GetStringResource("szOK"),
                                        ActionList.ToArray());
                    await TapHandler.ImageTapHandler((Photos)((TappedEventArgs)e).Parameter, act, KeyButton, ParentObject);
                };

                tgRec.SetBinding(TapGestureRecognizer.CommandProperty, bindTapped);
                tgRec.SetBinding(TapGestureRecognizer.CommandParameterProperty, ".");
                keyLayout.GestureRecognizers.Add(tgRec);

                var img = new Image()
                {
                    Aspect = Aspect.AspectFit,
                    HeightRequest = 350,
                    WidthRequest = 250,
                };
                img.SetBinding(Image.SourceProperty, bindPhoto);
                var lblFileName = new Label()
                {
                    HorizontalTextAlignment = TextAlignment.Center,
                };
                lblFileName.SetBinding(Label.TextProperty, "FileName");
                keyLayout.Children.Add(img);
                keyLayout.Children.Add(lblFileName);

                return new ViewCell { View = keyLayout };
            });

            Carousel.ItemTemplate = dt;
        }

        public static readonly BindableProperty ActionListProperty = BindableProperty.Create(nameof(ActionList), typeof(List<string>), typeof(List<string>));

        public List<string> ActionList
        {
            get
            {
                return (List<string>)GetValue(ParentObjectProperty);
            }

            set
            {
                SetValue(ParentObjectProperty, value);
            }
        }

        public static readonly BindableProperty ParentObjectProperty = BindableProperty.Create(nameof(ParentObject), typeof(Object), typeof(Object));

        public Object ParentObject
        {
            get
            {
                return (Object)GetValue(ParentObjectProperty);
            }

            set
            {
                SetValue(ParentObjectProperty, value);
            }
        }

        public static readonly BindableProperty KeyButtonProperty = BindableProperty.Create(nameof(KeyButton), typeof(ImageButton), typeof(ImageButton));

        public ImageButton KeyButton
        {
            get
            {
                return (ImageButton)GetValue(KeyButtonProperty);
            }

            set
            {
                SetValue(KeyButtonProperty, value);
            }
        }
    }
}

I can then instantiate it like this

        <core:TemplateCarouselView
                            x:Name="carousel"
                            ActionList="{Binding Path=ActionList}"
                            KeyButton="{x:Reference KeyImageButton}"
                            ParentObject="{Binding Path=MyClass}" />

It took some time, and I couldn't have done it without some help!

Cheers!



SS Suganya Sethuraman Syncfusion Team January 6, 2021 08:50 AM UTC

Hi Jesse Knott,

Greetings from Syncfusion.

We are glad to that you found the solution, please let us know if you need any other assistance.

Regards,
Suganya Sethuraman.
 


Loader.
Up arrow icon