简体   繁体   中英

Forwarding events/commands when using Xamarin.Forms Custom Renderer

In my Xamarin.Forms app I want to customise the look and feel of buttons to a higher degree than allowed so I am using a custom renderer to replace the default control in Xamarin.Forms on Windows Phone 8.1 with my own button control.

My control that just extends Button, will add extra properties later.

public class ButtonControl : Button {}

My custom renderer on Windows Phone 8.1:

public class ButtonControlRenderer : ViewRenderer<ButtonControl, Button>
  {
    protected override void OnElementChanged(ElementChangedEventArgs<ButtonControl> e)
    {
      base.OnElementChanged(e);

      if (e.OldElement != null || Element == null)
        return;

      var button = new Button
      {
        Style = Application.Current.Resources["ButtonWithTilt"] as Style,
        Content = Element.Text,
        IsEnabled = Element.IsEnabled
      };

      Element.BackgroundColor = Color.Transparent;

      SetNativeControl(button);
    }
  }

And, how I'm using the control in the Xamarin.Forms XAML file:

<StackLayout VerticalOptions="Center"
                         HorizontalOptions="Fill"
                         Margin="24,12,24,0">
              <controls:ButtonControl Text="{res:Translate LoginPageButtonText}"
                      TextColor="{x:Static const:Colours.OverlayColor}"
                      BackgroundColor="{x:Static const:Colours.PrimaryColor}"
                      BorderWidth="0"
                      Margin="0,24,0,0"
                      HeightRequest="50"
                      IsEnabled="{Binding LoginValid}"
                      Command="{Binding LoginCommand}"
                      StyleId="{x:Static const:StyleIds.LoginPageButton}"/>
            </StackLayout>

As I'm replacing the button, my commands don't work out of the box, I have to add the following into OnElementChanged so that when my new button is clicked the command is exeuted:

button.Tapped += delegate
      {
        Element.Command.Execute(null);
      };

This doesn't seem like the cleanest solution, is there a better way of hooking this up?

Also, if there was an event I wanted to trigger on the base Xamarin.Forms control, like Clicked, how would I go about this? Would I override the Clicked event in my ButtonControl than inherits from the Button class and add a method to trigger the event from there?

button.Tapped += handler is the way you do it. But instead of executing the command, you only have to call SendClicked() on your Element. This will execute the command and trigger the Clicked event. The standard renderers are doing the same.

You should transform the anonymous delegate to a method in your class, to be able deregister the event on cleanup, to prevent memory leaks.

public class ButtonControlRenderer : ViewRenderer<ButtonControl, Button>
{
    protected override void OnElementChanged(ElementChangedEventArgs<ButtonControl> e)
    {
        //your creation code ...            
        button.Tapped += OnButtonTapped;
    }

    private void OnButtonTapped(...)
    {
        ((IButtonController)Element)?.SendClicked();
    }

    protected override void Dispose(bool disposing)
    {
        if (Control != null)
            Control.Tapped -= OnButtonTapped;

        base.Dispose(disposing);
    }
}

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