How to create ios handler?

Igor Kravchenko 281 Reputation points
2022-05-16T19:56:19.117+00:00

I have created a Card control and handlers for it (CardHandler, CardHandler.Android and CardHandler.iOS). PlatformView in Android handler is Google.Android.Material.Card.MaterialCardView. In iOS handler PlatformView is UIView.
But something is wrong because I get next error in shared handler: Partial declarations of 'CardHandler' must not specify different base classes
In CardHandler.iOS I have next:

202330-ios-handler.png
Full code:

public enum CardType  
    {  
        Elevated, Filled, Outlined  
    }  
  
    public interface ICard : IView, IPadding, IPaddingElement  
    {  
        View Content { get; set; }  
        CardType Type { get; set; }  
    }  
  
    [ContentProperty(nameof(Content))]  
    public class Card : View, ICard  
    {  
        public event EventHandler Clicked;  
        public event EventHandler LongClicked;  
  
        public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;  
  
        public static readonly BindableProperty ContentProperty =  
            BindableProperty.Create(nameof(Content), typeof(View), typeof(Card), default(View), propertyChanged: OnContentChanged);  
  
        public static readonly BindableProperty TypeProperty =  
            BindableProperty.Create(nameof(CardType), typeof(CardType), typeof(ICard), CardType.Elevated);  
  
        public static readonly BindableProperty ClickedCommandProperty =  
            BindableProperty.Create(nameof(ClickedCommand), typeof(ICommand), typeof(ICard), default(ICommand));  
  
        public static readonly BindableProperty LongClickedCommandProperty =  
            BindableProperty.Create(nameof(LongClickedCommand), typeof(ICommand), typeof(ICard), default(ICommand));  
  
        public static readonly BindableProperty ClickedCommandParameterProperty =  
            BindableProperty.Create(nameof(ClickedCommandParameter), typeof(object), typeof(ICard), default);  
  
        public static readonly BindableProperty LongClickedCommandParameterProperty =  
            BindableProperty.Create(nameof(LongClickedCommandParameter), typeof(object), typeof(ICard), default);  
  
        public object ClickedCommandParameter  
        {  
            get => GetValue(ClickedCommandParameterProperty);  
            set => SetValue(ClickedCommandParameterProperty, value);  
        }  
  
        public object LongClickedCommandParameter  
        {  
            get => GetValue(LongClickedCommandParameterProperty);  
            set => SetValue(LongClickedCommandParameterProperty, value);  
        }  
  
        public ICommand ClickedCommand  
        {  
            get => (ICommand)GetValue(ClickedCommandProperty);  
            set => SetValue(ClickedCommandProperty, value);  
        }  
  
        public ICommand LongClickedCommand  
        {  
            get => (ICommand)GetValue(ClickedCommandProperty);  
            set => SetValue(ClickedCommandProperty, value);  
        }  
  
        public Thickness Padding  
        {  
            get => (Thickness)GetValue(PaddingElement.PaddingProperty);  
            set => SetValue(PaddingElement.PaddingProperty, value);  
        }  
  
        public CardType Type  
        {  
            get => (CardType)GetValue(TypeProperty);  
            set => SetValue(TypeProperty, value);  
        }  
  
        public View Content  
        {  
            get => (View)GetValue(ContentProperty);  
            set => SetValue(ContentProperty, value);  
        }  
  
        Thickness IPaddingElement.PaddingDefaultValueCreator() => new Thickness(double.NaN);  
  
        static void OnContentChanged(BindableObject bindable, object oldValue, object newValue)  
        {  
            if (bindable is Card card)  
                card.OnBindingContextChanged();  
        }  
  
        void IPaddingElement.OnPaddingPropertyChanged(Thickness oldValue, Thickness newValue)  
        {  
        }  
  
        public void SendClicked()  
        {  
            Clicked?.Invoke(this, EventArgs.Empty);  
            if (ClickedCommand != null && ClickedCommand.CanExecute(ClickedCommandParameter))  
                ClickedCommand.Execute(ClickedCommandParameter);  
        }  
  
        public void SendLongClicked()  
        {  
            LongClicked?.Invoke(this, EventArgs.Empty);  
            if (LongClickedCommand != null && LongClickedCommand.CanExecute(LongClickedCommandParameter))  
                LongClickedCommand.Execute(LongClickedCommandParameter);  
        }  
    }  
  
    static class PaddingElement  
    {  
        public static readonly BindableProperty PaddingProperty =  
            BindableProperty.Create(nameof(IPaddingElement.Padding), typeof(Thickness), typeof(IPaddingElement), default(Thickness),  
                                    propertyChanged: OnPaddingPropertyChanged,  
                                    defaultValueCreator: PaddingDefaultValueCreator);  
  
        static void OnPaddingPropertyChanged(BindableObject bindable, object oldValue, object newValue)  
        {  
            ((IPaddingElement)bindable).OnPaddingPropertyChanged((Thickness)oldValue, (Thickness)newValue);  
        }  
  
        static object PaddingDefaultValueCreator(BindableObject bindable)  
        {  
            return ((IPaddingElement)bindable).PaddingDefaultValueCreator();  
        }  
  
        public static readonly BindableProperty PaddingLeftProperty =  
            BindableProperty.Create("PaddingLeft", typeof(double), typeof(IPaddingElement), default(double),  
                                    propertyChanged: OnPaddingLeftChanged);  
  
        static void OnPaddingLeftChanged(BindableObject bindable, object oldValue, object newValue)  
        {  
            var padding = (Thickness)bindable.GetValue(PaddingProperty);  
            padding.Left = (double)newValue;  
            bindable.SetValue(PaddingProperty, padding);  
        }  
  
        public static readonly BindableProperty PaddingTopProperty =  
            BindableProperty.Create("PaddingTop", typeof(double), typeof(IPaddingElement), default(double),  
                                    propertyChanged: OnPaddingTopChanged);  
  
        static void OnPaddingTopChanged(BindableObject bindable, object oldValue, object newValue)  
        {  
            var padding = (Thickness)bindable.GetValue(PaddingProperty);  
            padding.Top = (double)newValue;  
            bindable.SetValue(PaddingProperty, padding);  
        }  
  
        public static readonly BindableProperty PaddingRightProperty =  
            BindableProperty.Create("PaddingRight", typeof(double), typeof(IPaddingElement), default(double),  
                                    propertyChanged: OnPaddingRightChanged);  
  
        static void OnPaddingRightChanged(BindableObject bindable, object oldValue, object newValue)  
        {  
            var padding = (Thickness)bindable.GetValue(PaddingProperty);  
            padding.Right = (double)newValue;  
            bindable.SetValue(PaddingProperty, padding);  
        }  
  
        public static readonly BindableProperty PaddingBottomProperty =  
            BindableProperty.Create("PaddingBottom", typeof(double), typeof(IPaddingElement), default(double),  
                                    propertyChanged: OnPaddingBottomChanged);  
  
        static void OnPaddingBottomChanged(BindableObject bindable, object oldValue, object newValue)  
        {  
            var padding = (Thickness)bindable.GetValue(PaddingProperty);  
            padding.Bottom = (double)newValue;  
            bindable.SetValue(PaddingProperty, padding);  
        }  
    }  

Shared handler:

#if IOS || MACCATALYST  
using PlatformView = UIKit.UIView;  
#elif ANDROID  
using PlatformView = Google.Android.Material.Card.MaterialCardView;  
#elif NET || (NET6_0 && !IOS && !ANDROID && !TIZEN)  
using PlatformView = System.Object;  
#endif  
public interface ICardHandler : IViewHandler  
    {  
        new PlatformView PlatformView { get; }  
    }  
  
    public partial class CardHandler : ICardHandler  
    {  
        public static IPropertyMapper<ICard, ICardHandler> Mapper = new PropertyMapper<ICard, ICardHandler>(ViewMapper)  
        {  
            [nameof(ICard.Content)] = MapContent,  
            [nameof(IPadding.Padding)] = MapPadding,  
        };  
  
        public CardHandler(IPropertyMapper mapper, CommandMapper commandMapper = null) : base(mapper, commandMapper)  
        {  
        }  
  
        public CardHandler() : base(Mapper)  
        {  
        }  
    }  

Android handler:

public partial class CardHandler : ViewHandler<ICard, MaterialCardView>  
    {  
        private int GetLayoutResource()  
        {  
            switch (VirtualView.Type)  
            {  
                case CardType.Elevated:  
                    return Resource.Layout.ElevatedCard;  
                case CardType.Filled:  
                    return Resource.Layout.FilledCard;  
                case CardType.Outlined:  
                    return Resource.Layout.OutlinedCard;  
            }  
            return Resource.Layout.ElevatedCard;  
        }  
  
        protected override MaterialCardView CreatePlatformView()  
        {  
            LayoutInflater inflater = LayoutInflater.FromContext(Context);  
            Android.Views.View view = inflater.Inflate(GetLayoutResource(), null);  
            MaterialCardView card = (MaterialCardView)view;  
            card.Clickable = true;  
            card.Focusable = true;  
            return card;  
        }  
  
        protected override void ConnectHandler(MaterialCardView platformView)  
        {  
            base.ConnectHandler(platformView);  
            platformView.Click += OnClicked;  
            platformView.LongClick += OnLongClicked;  
        }  
  
        protected override void DisconnectHandler(MaterialCardView platformView)  
        {  
            base.DisconnectHandler(platformView);  
            platformView.Click -= OnClicked;  
            platformView.LongClick -= OnLongClicked;  
        }  
  
        private void OnClicked(object sender, EventArgs e)  
        {  
            if (VirtualView is Card card)  
                card.SendClicked();  
        }  
  
        private void OnLongClicked(object sender, Android.Views.View.LongClickEventArgs e)  
        {  
            if (VirtualView is Card card)  
                card.SendLongClicked();  
        }  
  
        public static void MapContent(ICardHandler handler, ICard card)  
        {  
            if (card.Content == null)  
                return;  
            handler.PlatformView.AddView(card.Content.ToPlatform(handler.MauiContext));  
        }  
  
        public static void MapPadding(ICardHandler handler, IPadding card)  
        {  
            Thickness padding = handler.MauiContext.Context.ToPixels(card.Padding);  
            handler.PlatformView.SetContentPadding((int)padding.Left, (int)padding.Top, (int)padding.Right, (int)padding.Bottom);  
        }  
    }  

iOS handler:

public partial class CardHandler : ViewHandler<ICard, UIKit.UIView>  
    {  
        protected override UIKit.UIView CreatePlatformView()  
        {  
  
        }  
    }  

What is wrong here? Thanks.

.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,578 questions
0 comments No comments
{count} votes

Accepted answer
  1. Wenyan Zhang (Shanghai Wicresoft Co,.Ltd.) 32,306 Reputation points Microsoft Vendor
    2022-05-18T09:35:48.327+00:00

    Hello,
    I check your project, please try to remove your CardHandler.iOS.cs to Platforms->iOS, remove your CardHandler.android.cs to Platforms->Android and enable change the namespace once you see the popup dialog, then add CardHandler in MauiProgram.

    #if IOS  
                 Handlers.AddHandler(typeof(Card), typeof(CardHandler));  
     #endif  
    

    As you @Igor Kravchenko said, if you prefer to use handlers for one control in one folder, try to combine filename and folder multi-targeting. Thank you!

    Best Regards,
    Wenyan Zhang


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Wenyan Zhang (Shanghai Wicresoft Co,.Ltd.) 32,306 Reputation points Microsoft Vendor
    2022-05-17T08:46:54.587+00:00

    Hi @Igor Kravchenko ,

    I use the following code for testing. I see there are three errors, the last one means that you haven't returned an instance. When I debug the app on my real physical device, the program runs without any problems, although there are still the other two errors in VS.
    It seems a problem of VS, you could try to report it, see: https://learn.microsoft.com/en-us/visualstudio/ide/how-to-report-a-problem-with-visual-studio?view=vs-2022.
    In addition, there is no card view on iOS platform like MaterialCardView, you have to customize the view according to your need.

    public partial class CardHandler : ViewHandler<ICard, UIView>  
        {  
            protected override UIView CreatePlatformView()  
            {  
                UIView view = new UIView();  
                view.Frame = new CoreGraphics.CGRect(0, 0, 300, 300);  
                view.BackgroundColor = UIColor.Yellow;  
                return view;  
            }  
        }  
    

    Add Card in MainPage without any parameters for testing

     <local:Card ></local:Card>  
    

    Register Handler in MauiProgram

    		builder.UseMauiApp<App>().ConfigureMauiHandlers(Handlers =>  
    		{  
    #if IOS  
    			Handlers.AddHandler(typeof(Card), typeof(CardHandler));  
    #endif  
      
    		});  
    

    Best Regards,
    Wenyan Zhang


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.