Xamarin.Forms Button Contents

I am trying to add custom content to a button in Xamarin Forms.

By default, a button is created as follows:

<Button d:DataContext="{d:DesignInstance viewModel:AssessmentItemCategory}" Clicked="Button_OnClicked" Style="{StaticResource CategoryButtonStyle}" Text={Binding Text} /> 

But I would like to create custom content for this button. Normally with WPF I would do it like this:

 <Button d:DataContext="{d:DesignInstance viewModel:AssessmentItemCategory}" Clicked="Button_OnClicked" Style="{StaticResource CategoryButtonStyle}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="20" /> </Grid.ColumnDefinitions> <Label Text="{Binding Text}" Grid.Column="0" TextColor="Black"/> <Label Text="-" Grid.Column="1" TextColor="Black"/> </Grid> </Button> 

But that does not work.

I also searched for the DataTemplate property, but did not find this.

How to do it in Xamarin.Forms?

+5
source share
6 answers

Thanks Paul

I created my own UserControl that handles this

There he is:

 public partial class ContentButton : ContentView { public ContentButton() { InitializeComponent(); } public event EventHandler Tapped; public static readonly BindableProperty CommandProperty = BindableProperty.Create<ContentButton, ICommand>(c => c.Command, null); public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } private void TapGestureRecognizer_OnTapped(object sender, EventArgs e) { if(Tapped != null) Tapped(this,new EventArgs()); } } 

And see the code:

 <?xml version="1.0" encoding="utf-8" ?> <ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="RFA.Wireframes.Controls.ContentButton" x:Name="ContentButtonView"> <ContentView.GestureRecognizers> <TapGestureRecognizer Tapped="TapGestureRecognizer_OnTapped" Command="{Binding Source={x:Reference ContentButtonView}, Path=Command}"></TapGestureRecognizer> </ContentView.GestureRecognizers> </ContentView> 
+4
source

Thanks Tomas, good inspiration. However, for me, your control did not pick up a key event on all platforms and used the obsolete Xamarin.Forms "BindableProperty.Create <" methods as well. So I came up with this.

There he is:

 public class ContentButton:ContentView { private readonly TapGestureRecognizer _tapGestureRecognizer; public ContentButton() { _tapGestureRecognizer = new TapGestureRecognizer(); GestureRecognizers.Add(_tapGestureRecognizer); } protected override void OnChildAdded(Element child) { base.OnChildAdded(child); if (child is View childview) { childview.GestureRecognizers.Add(_tapGestureRecognizer); } } public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ContentButton), null, BindingMode.Default, null, CommandPropertyChanged); private static void CommandPropertyChanged(BindableObject bindable, object oldValue, object newValue) { if (newValue is ICommand command && bindable is ContentButton contentButton) { contentButton._tapGestureRecognizer.Command = command; } } public ICommand Command { get => (ICommand)GetValue(CommandProperty); set => SetValue(CommandProperty, value); } } 
+2
source

Unfortunately, at the moment, Xamarin.Forms does not support the Content property for Button.

You will need to create your own User Control using a combination of other controls to try and recreate the Button control. You can then make part of your User Control ContentView, create a BindableProperty to bind your Grid, and then use the BindingPropertyChangedDelegate to assign the Content property to your ContentView.

+1
source

Thanks to Tomasz for creating ContentButton .

I have successfully used it by adding CommandParameter similar way:

 <ContentView.GestureRecognizers> <TapGestureRecognizer Tapped="TapGestureRecognizer_OnTapped" Command="{Binding Source={x:Reference ContentButtonView}, Path=Command}" CommandParameter="{Binding Source={x:Reference ContentButtonView}, Path=CommandParameter}"/> </ContentView.GestureRecognizers> public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(ContentButton)); public object CommandParameter { get { return (object)GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } 
0
source

I came across a problem that turned out to be a recent bug described here here .

This made me do the next hack, which I think is useful to others. When setting the BackgroundColor directly in my ShoppingCartSummaryView , the TapGestureRecognizer does not work.

  <!--HACK Setting BackgroundColor here because of https://bugzilla.xamarin.com/show_bug.cgi?id=25943 --> <Controls:ContentButton Grid.Row="2" Command="{Binding ShowCartCommand}" BackgroundColor="Yellow"> <Controls:ContentButton.Content> <Views:ShoppingCartSummaryView/> </Controls:ContentButton.Content> </Controls:ContentButton> 
0
source

Here is my implementation of a content button that has some additional things to better mimic the actual button:

  • ICommand Command
  • object CommandParameter
  • Events:
    • Clicked
    • Pressed
    • Released
    • VisuallyPressedChanged (Occurs when the button style needs to be changed so that the user knows that the button is pressed)

It uses the TouchTracking.Forms package to handle touch events; it can be downloaded using NuGet.

 public class ContentButton : ContentView { public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ContentButton), null, BindingMode.Default); public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(ContentButton)); /// <summary> /// Occurs when the Button is clicked. /// </summary> public event EventHandler Clicked; /// <summary> /// Occurs when the Button is pressed. /// </summary> public event EventHandler Pressed; /// <summary> /// Occurs when the Button is released. /// <para>The released event always occur before the clicked event.</para> /// </summary> public event EventHandler Released; /// <summary> /// Occurs when the style of the button should be changed to let the user know that the button has been pressed. /// <para>If the argument is true, it means that the Button was just pressed. /// <para>If the argument is false, it means that the Button was just released or that the user has moved his finger out of the buttons boundaries.</para> /// </summary> public event EventHandler<bool> VisuallyPressedChanged; /// <summary> /// Gets or sets the command to invoke when the button is activated. This is a bindable property. /// </summary> public ICommand Command { get => (ICommand)GetValue(CommandProperty); set => SetValue(CommandProperty, value); } /// <summary> /// Gets or sets the parameter to pass to the Command property. This is a bindable property. /// </summary> public object CommandParameter { get => GetValue(CommandParameterProperty); set => SetValue(CommandParameterProperty, value); } private bool isVisuallyPressed; public ContentButton() { var touchEffect = new TouchEffect { Capture = true }; touchEffect.TouchAction += TouchEffect_TouchAction; Effects.Add(touchEffect); } protected override void OnChildAdded(Element child) { base.OnChildAdded(child); // so that the touch events are ignored and bypassed to this control if(child is VisualElement visualChild) { visualChild.InputTransparent = true; } } private long? currentId; private object touchEffect_lock = new object(); private void TouchEffect_TouchAction(object sender, TouchActionEventArgs e) { // only track one touch if(currentId != e.Id && e.Type!=TouchActionType.Pressed) { return; } lock(touchEffect_lock) { switch(e.Type) { case TouchActionType.Pressed: currentId = e.Id; Pressed?.Invoke(this, EventArgs.Empty); isVisuallyPressed = true; VisuallyPressedChanged?.Invoke(this, true); break; case TouchActionType.Moved: if(isVisuallyPressed) { bool isInside = e.Location.X >= 0 && e.Location.Y >= 0 && e.Location.X <= Bounds.Width && e.Location.Y <= Bounds.Height; if(!isInside) { isVisuallyPressed = false; VisuallyPressedChanged?.Invoke(this, false); } } break; case TouchActionType.Cancelled: currentId = null; isVisuallyPressed = false; VisuallyPressedChanged?.Invoke(this, false); break; case TouchActionType.Released: currentId = null; if(isVisuallyPressed) { Released?.Invoke(this, EventArgs.Empty); Clicked?.Invoke(this, EventArgs.Empty); if(Command != null && Command.CanExecute(CommandParameter)) { Command.Execute(CommandParameter); } isVisuallyPressed = false; VisuallyPressedChanged?.Invoke(this, false); } break; } } } } 
0
source

All Articles