2

I want to use a Stepper in my Xamarin Forms Project, but it won't resize the width properly in Android. The WithRequest won't work at all an when I set the margin it only resize one of the two buttons, the other one is still at its normal size.

enter image description here

Here is my code:

<Grid>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width=".3*"/>
    <ColumnDefinition Width=".5*"/>
    <ColumnDefinition Width=".2*"/>
    <ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
    ...
</Grid.RowDefinitions>
...
<Stepper Grid.Row="7" Grid.Column="1" Margin="20,0,20,0"/>
</Grid>
Anas Alweish
  • 2,818
  • 4
  • 30
  • 44
Optimist19
  • 45
  • 1
  • 7

2 Answers2

2

I want to use a Stepper in my Xamarin Forms Project, but it won't resize the width properly in Android. The WithRequest wont work at all an when I set the margin it only resize one of the two buttons, the other one is still at it's normal size.

The buttons' sizes are not set according to the WidthRequest property. If you want to set the buttons' size, you'll have to make your own Stepper Control.

Make a few changes to the source codes of StepperRenderer's OnElementChange method will make it work for your own custom control. Following is the modified version of StepperRenderer:

[assembly:ExportRenderer(typeof(MyStepper),typeof(MyStepperRenderer))]
namespace StepperDemo.Droid
{
    public class MyStepperRenderer : ViewRenderer<Stepper, LinearLayout>
    {
        AButton _downButton;
        AButton _upButton;

        public MyStepperRenderer()
        {
            AutoPackage = false;
        }

        protected override LinearLayout CreateNativeControl()
        {
            return new LinearLayout(Context) {

                Orientation = Orientation.Horizontal
            };
        }

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

            if (e.OldElement == null)
            {
                _downButton = new AButton(Context) { Text = "-", Gravity = GravityFlags.Center, Tag = this };
                //Set the MinWidth of Button
                _downButton.SetMinWidth(50);

                _downButton.SetOnClickListener(StepperListener.Instance);

                _upButton = new AButton(Context) { Text = "+", Tag = this };
                _upButton.SetOnClickListener(StepperListener.Instance);
                //Set the MinWidth of Button
                _upButton.SetMinWidth(50);

                if (e.NewElement != null)
                {
                    //Set the Width and Height of the button according to the WidthRequest
                    _downButton.LayoutParameters = new LayoutParams((int)e.NewElement.WidthRequest, LayoutParams.MatchParent);
                    _upButton.LayoutParameters = new LayoutParams((int)e.NewElement.WidthRequest, LayoutParams.MatchParent);
                }

                var layout = CreateNativeControl();

                layout.AddView(_downButton);
                layout.AddView(_upButton);

                SetNativeControl(layout);
            }

            UpdateButtonEnabled();
        }

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

            switch (e.PropertyName)
            {
                case "Minimum":
                    UpdateButtonEnabled();
                    break;
               case "Maximum":
                    UpdateButtonEnabled();
                    break;
                case "Value":
                    UpdateButtonEnabled();
                    break;
                case "IsEnabled":
                    UpdateButtonEnabled();
                    break;
            }
        }

        void UpdateButtonEnabled()
        {
            Stepper view = Element;
            _upButton.Enabled = view.IsEnabled ? view.Value < view.Maximum : view.IsEnabled;
            _downButton.Enabled = view.IsEnabled ? view.Value > view.Minimum : view.IsEnabled;
        }

        class StepperListener : Java.Lang.Object, IOnClickListener
        {
            public static readonly StepperListener Instance = new StepperListener();

            public void OnClick(global::Android.Views.View v)
            {
                var renderer = v.Tag as MyStepperRenderer;
                if (renderer == null)
                    return;

                Stepper stepper = renderer.Element;
                if (stepper == null)
                    return;

                if (v == renderer._upButton)
                    ((IElementController)stepper).SetValueFromRenderer(Stepper.ValueProperty, stepper.Value + stepper.Increment);
                else if (v == renderer._downButton)
                    ((IElementController)stepper).SetValueFromRenderer(Stepper.ValueProperty, stepper.Value - stepper.Increment);
        }
    }
}

Then you can use this renderer according to the CustomRenderer Tutorial.

And the Stepper Buttons' width will change properly:

enter image description here

Elvis Xia - MSFT
  • 10,801
  • 1
  • 13
  • 24
0

A simple custom renderer like the following is enough to let the Stepper control honor the WidthRequest and HeightRequest parameters:

using Android.Content;
using Android.Widget;
using MyApp.CustomRenderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using AButton = Android.Widget.Button;

[assembly: ExportRenderer(typeof(Stepper), typeof(ResizableStepperRenderer))]

namespace MyApp.CustomRenderers;

public class ResizableStepperRenderer : StepperRenderer
{
    public ResizableStepperRenderer(Context context)
        : base(context)
    {
    }
        
    protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
    {
        base.OnElementChanged(e);

        if (e.NewElement is not null &&
            Control is { } linearLayout)
        {
            if (linearLayout.GetChildAt(0) is AButton downButton)
            {
                downButton.LayoutParameters = new LinearLayout.LayoutParams(
                    LayoutParams.MatchParent,
                    LayoutParams.MatchParent,
                    0.5f);
            }
                
            if (linearLayout.GetChildAt(1) is AButton upButton)
            {
                upButton.LayoutParameters = new LinearLayout.LayoutParams(
                    LayoutParams.MatchParent,
                    LayoutParams.MatchParent,
                    0.5f);
            }
        }
    }
}

Can be used as follows:

<Stepper
    WidthRequest="100"
    HeightRequest="40"
    Minimum="0"
    Maximum="99"
    Increment="1"
    Value="{Binding ... }" />
lauxjpn
  • 4,749
  • 1
  • 20
  • 40