Hey there!
I'm currently facing a problem trying to update the color of a CustomButton I've made when it's clicked. Have been searching the forums/internet in general for a solution, but no luck yet. Being new to Xamarin Forms, I'm quite unsure of the way I've been doing things, and assume I'm missing something to make it work.
Anyway: I need a different button for each item of a table in my database. To achieve this dynamic creation, I've resorted to creating the custombuttons in code-behind instead of Xaml. Only the color of the clicked button has to change, so to bind the BackgroundColorProperty I'd need a different property for each one in my ViewModel; and, as the number of buttons is dynamic, I don't see a way of doing that.
So I created a command (using Prism) that receives the clicked button as a parameter (which isn't exactly right as far as MVVM goes, I guess) to change just its BackgroundColor. Thing is, it doesn't work if the BackgroundColor has already been set. If it hasn't it works fine, though until the user clicks the button its background is transparent (hasn't been set).
I've only tested with Android so far, but I assume it's a problem with the custom renderer? As in the CustomRenderer might not be getting the event(?) to redraw the background. But if this is the case, why would it work if the color isn't set yet?
Might be obvious, but I'm still quite new to Xamarin Forms, and I fear my limited experience won't be enough to solve this, as I've been trying for longer than I'd like to admit already.
Custom button code:
public class CustomResidueButton : Button
{
public static readonly BindableProperty CustomTextProperty =
BindableProperty.Create("CustomText", typeof(string), typeof(CustomResidueButton), string.Empty);
public string CustomText
{
get { return (string)GetValue(CustomTextProperty); }
set { SetValue(CustomTextProperty, value); }
}
public static readonly BindableProperty CustomImageProperty =
BindableProperty.Create("CustomImage", typeof(string), typeof(CustomResidueButton), string.Empty);
public string CustomImage
{
get { return (string)GetValue(CustomImageProperty); }
set { SetValue(CustomImageProperty, value); }
}
public static readonly BindableProperty ResidueIdProperty =
BindableProperty.Create("ResidueId", typeof(string), typeof(CustomResidueButton), string.Empty);
public string ResidueId
{
get { return (string)GetValue(ResidueIdProperty); }
set { SetValue(ResidueIdProperty, value); }
}
}
Custom buttons creation in the main-page's code behind, called on the constructor:
private void CreateAndLoadResidueButtons(Grid filterGrid)
{
if (APIUtil.ResidueList != null)
{
int row = 1;
int column = 0;
foreach (Residue residue in APIUtil.ResidueList)
{
CustomResidueButton btn = new CustomResidueButton
{
//Style = residueBtnStyle,
CustomText = residue.residueName,
CustomImage = "tire",
ResidueId = residue.residueId.ToString(),
Command = ((MainMapViewModel)BindingContext).ResidueSelectedCommand,
};
Thickness margin = new Thickness();
if (row == 3)
margin.Bottom = 6;
if (column == 0)
margin.Left = 6;
if (column == 2)
margin.Right = 6;
btn.Margin = margin;
btn.CommandParameter = btn;
filterGrid.Children.Add(btn, column, row);
if (column == 2)
{
column = 0;
row++;
}
else
column++;
}
}
}
Command for when a button is clicked:
public DelegateCommand<object> ResidueSelectedCommand { get; set; }
private void CreateCommands()
{
ResidueSelectedCommand = new DelegateCommand<object>(ResidueSelectedExecute);
}
private void ResidueSelectedExecute(object selectedButton)
{
if (CanClick)
{
CanClick = false;
MessagingCenter.Send(new MessageService(), "ChangedResidueType");
CustomResidueButton btn = selectedButton as CustomResidueButton;
int resId = int.Parse(btn.ResidueId);
if (SelectedResidueIdList.Any(i => i == resId))
{
SelectedResidueIdList.Remove(resId);
btn.BackgroundColor = Color.Pink;
} else {
SelectedResidueIdList.Add(resId);
btn.BackgroundColor = Color.Yellow;
}
if (SelectedResidueIdList.Count > 0)
LoadPins(APIUtil.GetSitesOfResidueList(SelectedResidueIdList));
else
LoadPins(APIUtil.GetAllSites());
CanClick = true;
}
}
And finally, the Android custom renderer:
class ResidueButtonRenderer : ViewRenderer<CustomResidueButton, Android.Views.View>
{
private readonly Context _context;
public ResidueButtonRenderer(Context context) : base(context)
{
_context = context;
}
private TextView _residueNameView;
private ImageView _residueIconView;
protected override void OnElementChanged(ElementChangedEventArgs<CustomResidueButton> e)
{
base.OnElementChanged(e);
//e.NewElement.CommandParameter = Element.CommandParameter;
if (Control == null)
{
// Element é o CustomResidueButton
// inflate no layout do botão
var inflater = _context.GetSystemService(Context.LayoutInflaterService) as LayoutInflater;
var rootLayout = inflater.Inflate(Resource.Layout.ResidueButton, null, false);
_residueNameView = rootLayout.FindViewById(Resource.Id.ResidueName) as TextView;
_residueNameView.Text = Element.CustomText;
// usa a string do CustomResidueButton de nome pra pegar imagem do drawable
int imageId = Context.Resources.GetIdentifier(Element.CustomImage, "drawable", Context.PackageName);
_residueIconView = rootLayout.FindViewById(Resource.Id.ResidueIcon) as ImageView;
_residueIconView.SetImageResource(imageId);
SetBackground(rootLayout);
SetNativeControl(rootLayout);
// executa os Commands, o conteudo de Execute() é o CommandParameter
rootLayout.Click += (s, a) => Element.Command?.Execute(Element.CommandParameter);
}
}
private void SetBackground(Android.Views.View rootLayout)
{
var backgroundColor = Element.BackgroundColor.ToAndroid();
var enabledBackground = new GradientDrawable(GradientDrawable.Orientation.LeftRight, new int[] { backgroundColor, backgroundColor });
var stateList = new StateListDrawable();
var rippleItem = new RippleDrawable(ColorStateList.ValueOf(Android.Graphics.Color.White), enabledBackground, null);
stateList.AddState(new[] { Android.Resource.Attribute.StateEnabled }, rippleItem);
rootLayout.Background = stateList;
}
}
At this point any kind of help would be appreciated, even if it's just telling me I'm doing things completely wrong.
Thanks in advance!