Home > other >  C#, Xamarin Forms: No Custom TextChangedEvent Raised on initialization
C#, Xamarin Forms: No Custom TextChangedEvent Raised on initialization

Time:10-30

I'm creating an Xamarin.Forms MVVM App (only using Android) which needs certain buttons to be outlined red, whenever their text property holds a specific value. (Purpose: alert the user to press the button and select a value, which will change the Button Text Property and therefore remove the red outline)

To achieve this I've create the following documents:

A custom button CButton that extents the default Button:

    public class CButton : Button
    {
        // this Hides the Default .Text-Property
        public string Text 
        {
            get => base.Text;
            set
            {
                base.Text = value;
                TextChangedEvent(this, new EventArgs());
            }

        }
        // The Raised Event
        protected virtual void TextChangedEvent(object sender, EventArgs e)
        {
            EventHandler<EventArgs> handler = TextChanged;
            handler(sender, e);
        }
        public event EventHandler<EventArgs> TextChanged;
    }

A custom behavior makes use of the raised TextChangedEvent

    public class ButtonValBehavior : Behavior<CButton>
    {
        protected override void OnAttachedTo(CButton bindable)
        {
            bindable.TextChanged  = HandleTextChanged;
            base.OnAttachedTo(bindable);
        }
        void HandleTextChanged(object sender, EventArgs e)
        {
            string forbidden = "hh:mm|dd.mm.yyyy";
            if (forbidden.Contains((sender as CButton).Text.ToLower()))
            {
                //Do when Button Text = "hh:mm" || "dd.mm.yyyy"
                (sender as CButton).BorderColor = Color.Gray;
            }
            else
            {
                //Do whenever Button.Text is any other value
                (sender as CButton).BorderColor = Color.FromHex("#d10f32");
            }
        }
        protected override void OnDetachingFrom(CButton bindable)
        {
            bindable.TextChanged -= HandleTextChanged;
            base.OnDetachingFrom(bindable);
        }
    }

The relevant parts of the ViewModel look the following:

    public class VM_DIVI : VM_Base
    {
        public VM_DIVI(O_BasisProtokoll base)
        {
            Base = base;
        }

        private O_BasisProtokoll _base = null;
        public O_BasisProtokoll Base
        {
            get => _base;
            set
            {
                _base = value;
                OnPropertyChanged();
            }
        }

        Command _datePopCommand;
        public Command DatePopCommand
        {
            get
            {
                return _datePopCommand ?? (_datePopCommand = new Command(param => ExecuteDatePopCommand(param)));
            }
        }
        void ExecuteDatePopCommand(object param)
        {
            //launch popup
            var p = new PP_DatePicker(param);
            PopupNavigation.Instance.PushAsync(p);
        }
    }

The .xmal looks the following (b is the xmlns of the Namespace):

    <b:CButton x:Name="BTN_ED_Datum"
               Text="{Binding Base.ED_datum, Mode=TwoWay}" 
               Grid.Column="1"
               Command="{Binding DatePopCommand}" 
               CommandParameter="{x:Reference BTN_ED_Datum}">
        <b:CButton.Behaviors>
            <b:ButtonValBehavior/>
        </b:CButton.Behaviors>
    </b:CButton>

This solution works fine whenever the input is caused by user interaction. However, when a Value is assigned during the initialization of the Page no red outline is created, in fact the TextChangedEvent isn't raised. By using breakpoints I noticed that during initialization the Text Property of CButton is never set, eventhough it actually will be in the view.

Despite fiddling around with my solution I cannot make this work on initialization. I tried to work around this issue by outlining every button by default in their constructor, however this will outline every button red, even when their text value doesn't require them to be.

How can I achieve my initial goal?

Many thanks in advance!

CodePudding user response:

It's been a while but if I recall correctly what I ended up doing was:

  • Changing the new Text-Property of my custom Button to CText and
  • Making sure that I have Mode=TwoWay activated for any Element, that doesn't have it enabled by default. (Look up Binding modes on msdn for more)
  • making CText a bindable property of CButton

My custom button now looks the following:

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;

namespace EORG_Anton.Model
{
    public class CButton : Button
    {
        public static readonly BindableProperty CTextProperty = 
            BindableProperty.Create(nameof(CText), 
            typeof(string), 
            typeof(CButton), 
            default(string), 
            BindingMode.TwoWay,
            propertyChanged: OnTextChanged);
        private static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (CButton)bindable;
            var value = (string)newValue;
            control.CText = value;
        }
        public string CText
        {
            get => base.Text;
            set
            {
                base.Text = value;
                TextChangedEvent(this, new EventArgs());
            }

        }
        protected virtual void TextChangedEvent(object sender, EventArgs e)
        {
            EventHandler<EventArgs> handler = TextChanged;
            handler(sender, e);
        }
        public event EventHandler<EventArgs> TextChanged;
    }
}
  • Related