1
votes

I am creating a platform specific custom button with text and image having custom renderer in Android and iOS. My problem is, Click Command on my button is not getting fired.

I have created custom android renderer which forces use of custom android layout.

Xamarin.Form Button Class:

    public static readonly BindableProperty CommandProperty =
        BindableProperty.Create("Command", typeof(ICommand), typeof(ImageButton), null,
            BindingMode.TwoWay, propertyChanged: OnCommandChanged);
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public EventHandler Clicked;

    private static void OnCommandChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var control = (ImageButton)bindable;

        // this gesture recognizer will inovke the command event whereever it is used
        control.GestureRecognizers.Add(new TapGestureRecognizer
        {
            Command = (Command)newValue,
            CommandParameter = control.CommandParams
        });
    }        
    
    public ImageButton()
    {
        this.GestureRecognizers.Add(new TapGestureRecognizer
        {
            Command = new Command(() =>
            {
                Clicked?.Invoke(this, EventArgs.Empty);

                if (Command != null)
                {
                    if (Command.CanExecute(CommandParams))
                        Command.Execute(CommandParams);
                }
            })
        });
    }

Android Renderer:

[assembly: ExportRenderer(typeof(ImageButton), typeof(ImageButtonRenderer))]
    namespace Droid.Extensions.Renderers
    {
        public class ImageButtonRenderer : ViewRenderer<ImageButton, Android.Views.View>
        {
    private readonly Context context;
    private TextView buttonText;
    private ImageView buttonIcon;

    public ImageButtonRenderer(Context context) : base(context)
    {
        this.context = context;
    }

    protected override void OnElementChanged(ElementChangedEventArgs<Controls.ImageButton> e)
    {
        base.OnElementChanged(e);

        if (Control == null)
        {
            var inflater = context.GetSystemService(Context.LayoutInflaterService) as LayoutInflater;
            var rootLayout = inflater.Inflate(Resource.Layout.ImageButton, null, false);

            buttonText = (TextView) rootLayout.FindViewById(Resource.Id.image_button_text);
            buttonText.Text = Element.Text;
            buttonIcon = (ImageView)rootLayout.FindViewById(Resource.Id.image_button_icon);
            
                    
            SetNativeControl(rootLayout);
            rootLayout.Click += (s, a) => Element.Command?.Execute(a);
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
    }
        }
    }

Consuming Control:

<control:ImageButton
                    Source="ai_tab_menu"
                    Margin="10"
                    Command="{Binding MyCommand}"
                    Text="This is a test!">

View Model:

    public class TabHomeMenuViewModel : INotifyPropertyChanged
{
    string message = string.Empty;
    public string Message
    {
        get { return message; }
        set
        {
            message = value;
            PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs("Message"));
        }
    }

    public Command MyCommand { get; private set; }

    public TabHomeMenuViewModel()
    {
        // configure the TapCommand with a method

        Message = "Waiting";
        MyCommand = new Command(UpdateVersion);
    }

    private void UpdateVersion()
    {
        Message += "Clicked ";
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
2

2 Answers

1
votes

I have pretty much the same problem. I tried to bind a command via BehaviorsPack. It works perfectly with the original element. The custom control does not work. I posted a thread in the xamarin formum but did not get an aswer: https://forums.xamarin.com/discussion/182224/entry-works-with-eventtocommandbehavior-custom-renderner-does-not

0
votes

I was able to find the issue with my code. I was assigning click event to root layout which was not CLICKABLE. So I assigned the Click to inner layout.

SetNativeControl(rootLayout);
rootLayout.FindViewById(Resource.Id.image_button).Click += (s, a) =>
{
   Element.RaiseCommand();
};

Also, we have to Bubble-up the events from Native Control to Forms.

public void RaiseCommand()
{
    Clicked?.Invoke(this, EventArgs.Empty);

    if (Command != null && Command.CanExecute(CommandParams))
        Command.Execute(CommandParams);
}