If you've run into the same problems with layout on buttons that I have where when you either add other controls, or even just do things like set text on labels, and you're finding that the Button text alignment gets corrupted you're going to want to read this...:
This bug appears to occur pretty much whenever you have a number of nested layout controls, and in response to a button click you add controls elsewhere in the page, or even just set content (e.g. set text on a label).
It has been referenced here:
http://forums.xamarin.com/discussion/20501/is-layout-badly-broken-or-am-i-just-not-getting-it
and here:
http://forums.xamarin.com/discussion/18947/changed-button-text-alignment-after-button-clicked
The workaround is to create a Button subclass, and a corresponding ButtonRenderer to force update of the native control's text after the click event has been propagated.
Button class:
using System;
using Xamarin.Forms;
using System.Windows.Input;
namespace TestLayoutProblem
{
public class MyButton : Button
{
// we have to hide the existing event since we cannot raise the event
// (personal opinion - too much of the framework is internal. makes it very difficult to workaround bugs)
public new event EventHandler Clicked;
public void SendClicked()
{
ICommand command = this.Command;
if (command != null)
command.Execute(this.CommandParameter);
EventHandler eventHandler = this.Clicked;
if (eventHandler != null)
eventHandler(this, EventArgs.Empty);
}
}
}
Android ButtonRenderer class:
using System;
using Xamarin.Forms.Platform.Android;
using Android.Content.Res;
using Android.Graphics;
using Android.Widget;
using Android.Graphics.Drawables;
using System.ComponentModel;
using Android.Runtime;
using Android.Views;
using Android.Util;
using TestLayoutProblem;
[assembly: Xamarin.Forms.ExportRenderer (typeof (MyButton), typeof (TestLayoutProblem.Android.MyButtonRenderer))]
namespace TestScrollViewProblem.Android
{
public class MyButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (this.Control != null)
{
Button button = this.Control;
button.SetOnClickListener((IOnClickListener) MyButtonRenderer.ButtonClickListener.Instance.Value);
}
}
private void ForceUpdateText()
{
this.Control.Text = this.Element.Text;
}
private void HandleClick (object sender, EventArgs e)
{
this.ForceUpdateText();
}
private class ButtonClickListener : Java.Lang.Object, IOnClickListener, IJavaObject, IDisposable
{
public static readonly Lazy<MyButtonRenderer.ButtonClickListener> Instance =
new Lazy<MyButtonRenderer.ButtonClickListener>(() => new MyButtonRenderer.ButtonClickListener());
public void OnClick(View v)
{
MyButtonRenderer buttonRenderer = v.Tag as MyButtonRenderer;
if (buttonRenderer != null)
{
// have to cast to MyButton to access the new SendClicked method
// which replaces that of the base class Button
MyButton myButton = (MyButton)buttonRenderer.Element;
myButton.SendClicked();
buttonRenderer.ForceUpdateText();
}
}
}
}
}
Hope this helps someone, because it sure was a lot of work to track down.
Phil