OnElementPropertyChanged not getting called ?

KentFonagerKentFonager DKBeta
edited April 2015 in Xamarin.Forms

Hi

I'm currently working on some extended Forms controls. I use bindable properties, but somehow the "OnElementPropertyChanged" wont get triggered with my "custom" properties. I just cant seem to figure out if I'm doing anything wrong or ?

I have this forms class called "FormEntry". When using it from code, I create it like this :

    FormEntry fe = new FormEntry () {
            Triggers = {

                new EventTrigger () { Event = "MyEvent", Actions = { new EntryValidation () } }
            }
    };

The code for the for the trigger action class getting called is this :

    public class EntryValidation : TriggerAction<FormEntry>
    {
            protected override void Invoke (FormEntry sender)
            {
                Debug.WriteLine ("3 - EntryValidation called");

                if (sender.Text.Length > 5)
                {
                    sender.Validated = true;
                } else
                {
                    sender.Validated = false;
                }
            }
    }

In my FormEntry class code, I have the "Validated" property defined as follows :

    public static readonly BindableProperty ValidatedProperty = BindableProperty.Create("Validated", typeof(bool), typeof(FormEntry), false, BindingMode.TwoWay);

    public bool Validated {
        get {
            return (bool)base.GetValue(FormEntry.ValidatedProperty);
        }
        set { 
            Debug.WriteLine ("4 - FormEntry : Validated setter called!");
            base.SetValue(FormEntry.ValidatedProperty, value);

        }
    }

When I run the code, I can debug and the above setter IS called as a result from setting the value in the "trigger action class", but unfortunately the "OnElementPropertyChanged" in the renderer class is NOT getting triggered:

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

        Console.WriteLine ("OnElementPropertyChanged called");
        SetProperties (sender);
    }

Am I missing something here? I've read that the notification should be implemented automatically, when i define my bindings as I do.

If someone please could help me figure out what I'm doing wrong, I would really appreciate it. Let me know if you need more info and I will supply that info.

Thanks in advance,
Kent Fonager

Answers

  • MitchMilamMitchMilam USMember ✭✭✭

    @KentFonager can you show us your entire Renderer class?

  • KentFonagerKentFonager DKBeta

    Hi Mitch

    Thanks for your interest in helping me out. Here goes the complete code for the renderer class:

        using System;
        using Xamarin.Forms.Platform.iOS;
        using Xamarin.Forms;
        using CrossLogin;
        using CrossLogin.iOS;
        using UIKit;
        using CoreGraphics;
    
        [assembly: ExportRenderer(typeof(FormEntry), typeof(FormEntryRenderer))]
    
        namespace CrossLogin.iOS
        {
            public class FormEntryRenderer : ViewRenderer<FormEntry, UIFormEntry>
            {
                public FormEntryRenderer ()
                {
                }
    
                protected override void OnElementChanged (ElementChangedEventArgs<FormEntry> e)
                {
                    base.OnElementChanged (e);
    
                    if (e.NewElement == null)
                    {
                        return;
                    }
    
                    if (Control == null)
                    {
                        BackgroundColor = Color.Yellow.ToUIColor();
                        UIFormEntry fe = new UIFormEntry (Bounds);
                        fe.TextChanged += delegate(string obj) {
                            Console.WriteLine("1 - UITextField - TextChanged called");
                            e.NewElement.SendMyEvent();
                        };
                        SetNativeControl(fe);
                    }
    
                    SetProperties (null);
                }
    
                protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
                {
                    base.OnElementPropertyChanged (sender, e);
    
                    Console.WriteLine ("OnElementPropertyChanged called");
                    SetProperties (sender);
                }
    
                private void SetProperties(object sender)
                {
                    Console.WriteLine ("SetProperties called ...");
                    var formEntry = Control;
                    formEntry.Text = Element.Text;
                    formEntry.Validated = Element.Validated;
    
                    if (sender != null)
                    {
                        FormEntry s = (FormEntry)sender;
                        s.SendMyEvent ();
                    }
                }
            }
    
    
            // CONTROL UI CODE
    
            public class UIFormEntry : UIView
            {
                public string Text {
    
                    get {
                        return txtText.Text;
                    }
    
                    set {
                        txtText.Text = value;
    
                        if(TextChanged != null)
                            TextChanged (value);
    
    
                    }
                }
    
                private bool validated = false;
                public bool Validated {
    
                    set {
                        Console.WriteLine ("Setting property Validated");
                        validated = value;
                        if (value == true)
                        {
                            UIView.Animate (0.25f, delegate {
                                imgCancel.Alpha = 0f;
                                imgOk.Alpha = 1f;
                            });
                        }
                        else
                        {
                            UIView.Animate (0.25f, delegate {
                                imgCancel.Alpha = 1f;
                                imgOk.Alpha = 0f;
                            });
                        }
                    }
    
                    get {
                        return validated;
                    }
                }
    
    
    
                UIView leftView;
                UITextField txtText;
                UIImageView imgCancel, imgOk;
    
                public UIFormEntry(CoreGraphics.CGRect bounds) : base(bounds)
                {
                    BackgroundColor = UIColor.Red;
    
                    // we can only set the height here
                    Frame = new CGRect(0, 0, 0, 40);
    
    
                    leftView = new UIView (new CGRect (0, 0, Frame.Height, Frame.Height));
                    leftView.BackgroundColor = Global.ColorLight.ToUIColor();
                    AddSubview (leftView);
    
                    BackgroundColor = Global.ColorDark.ToUIColor().ColorWithAlpha(0.25f);
                    txtText = new UITextField (new CGRect (Frame.Height + 5, 5, Frame.Width - 10 - Frame.Height, Frame.Height - 10));
                    txtText.Font = UIFont.FromName (Global.FontName, 18);
                    AddSubview (txtText);
    
                    txtText.EditingChanged += TxtText_ValueChanged;
    
                    // setup the icons
                    imgCancel = new UIImageView(UIImage.FromFile("cancel.png"));
                    CGRect f = imgCancel.Frame;
                    f.X = (leftView.Frame.Width - f.Width)/2;
                    f.Y = (leftView.Frame.Height - f.Height)/2;
                    imgCancel.Frame = f;
    
                    leftView.AddSubview (imgCancel);
    
                    imgOk = new UIImageView (UIImage.FromFile ("ok.png"));
                    f = imgOk.Frame;
                    f.X = (leftView.Frame.Width - f.Width)/2;
                    f.Y = (leftView.Frame.Height - f.Height)/2;
                    imgOk.Frame = f;
    
                    leftView.AddSubview (imgOk);
    
                    imgOk.Alpha = 0f;
                }
    
                public override void LayoutSubviews ()
                {
                    base.LayoutSubviews ();
    
                    // layout the controls
                    CGRect f = Frame;
    
                    leftView.Frame = new CGRect (0, 0, Frame.Height, Frame.Height);
                    txtText.Frame = new CGRect (Frame.Height + 5, 5, Frame.Width - 10 - Frame.Height, Frame.Height - 10);
    
                    // center the icons in the "leftview" frame
                    CGRect cf = imgCancel.Frame;
                    cf.X = (leftView.Frame.Width - cf.Width)/2;
                    cf.Y = (leftView.Frame.Height - cf.Height)/2;
                    imgCancel.Frame = cf;
    
                    cf = imgOk.Frame;
                    cf.X = (leftView.Frame.Width - cf.Width)/2;
                    cf.Y = (leftView.Frame.Height - cf.Height)/2;
                    imgOk.Frame = cf;
                }
    
                public event Action<string> TextChanged;
                void TxtText_ValueChanged (object sender, EventArgs e)
                {
                    if (TextChanged != null)
                    {
                        TextChanged (((UITextField)sender).Text);
                        //Console.WriteLine ("UIFormEntry - TextChanged event");
                    }
                }
    
    
                public override UIColor BackgroundColor {
                    get {
                        return base.BackgroundColor;
                    }
                    set {
                        base.BackgroundColor = value;
    
                        if(txtText != null)
                            txtText.BackgroundColor = value;
                    }
                }
    
                public string Placeholder
                {
                    set {
                        txtText.Placeholder = value;
                    }
                }
    
                public override bool IsFirstResponder {
                    get {
                        return txtText.IsFirstResponder;
                    }
                }
    
                public override bool ResignFirstResponder ()
                {
                    return txtText.ResignFirstResponder ();
                }
    
            }
        }
    
  • MitchMilamMitchMilam USMember ✭✭✭

    @KentFonager what is FormEntry inherited from?

  • KentFonagerKentFonager DKBeta

    Its inherited from View :

        using System;
        using Xamarin.Forms;
        using System.Diagnostics;
    
        namespace CrossLogin
        {
            public class FormEntry : View
            {
                // bool Validated
                public static readonly BindableProperty ValidatedProperty = BindableProperty.Create("Validated", typeof(bool), typeof(FormEntry), false, BindingMode.TwoWay);
                public bool Validated {
                    get {
                        return (bool)base.GetValue(FormEntry.ValidatedProperty);
                    }
                    set { 
                        Debug.WriteLine ("4 - FormEntry : Validated setter called!");
                        base.SetValue(FormEntry.ValidatedProperty, value);
    
                    }
                }
    
    
    
                public static readonly BindableProperty TestProperty = BindableProperty.Create<FormEntry,bool> (p => p.Test, false);
    
                public bool Test {
                    get { return (bool)GetValue (TestProperty); }
                    set { SetValue (TestProperty, value); }
                }
    
    
    
                // string Text
                public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(FormEntry), "", BindingMode.TwoWay);
                public string Text {
    
                    get { return (string)base.GetValue (TextProperty); }
                    set { SetValue (TextProperty, value); }
                }
    
                public event EventHandler MyEvent;
    
                public void SendMyEvent()
                {
                    var handler = MyEvent;
                    if (handler != null)
                    {
                        Debug.WriteLine ("2 - SendMyEvent called");
                        handler (this, EventArgs.Empty);
                    }
                }
    
                public FormEntry ()
                {
                }
            }
        }
    
  • MitchMilamMitchMilam USMember ✭✭✭

    @KentFonager Make your Validator property setter:

       public bool Validated {
            get {
                return (bool)base.GetValue(FormEntry.ValidatedProperty);
            }
            set { 
                Debug.WriteLine ("4 - FormEntry : Validated setter called!");
                base.SetValue(FormEntry.ValidatedProperty, value);
                NotifyPropertyChanged();
    
            }
        }
    
  • KentFonagerKentFonager DKBeta
    edited April 2015

    Hi again Mitch

    Thanks for holding onto me ;-)

    Unfortunately I cannot insert that code in the code, is it is not recognized. I think the class somehow should use the "INotifyPropertyChanged" interface, right ?

    Maybe I'm not using the correct design/pattern for a scenario like this ?

  • MitchMilamMitchMilam USMember ✭✭✭

    @KentFonager yes. that is entirely possible. BTW, you are going to a lot of work here. What exactly are you trying to accomplish?

  • KentFonagerKentFonager DKBeta

    I want an "Entry component" composed of multiple subcomponents like the actual textfield, some icons and other stuff, where we have an "external" validator (via the EventTrigger), that will change some visual stuff in the component (small fade animations).

    So what i want is, that when the Event Trigger has run, I want to make that stuff in the component happend.

    You follow me ?

  • KentFonagerKentFonager DKBeta

    By doing it with the event trigger, I can reuse the component, and wire up different types of validators like comparing for text length, is it a number only and so on ...

  • MitchMilamMitchMilam USMember ✭✭✭

    @KentFonager that's what I thought, just confirming.

  • KentFonagerKentFonager DKBeta

    But Mitch ... Am I doing it all "wrong" or ? How would one implement a scenario like that ? I cant really see that I should implement the properties like a "bindable model" class ? Seems like kinda overkill ?

  • MitchMilamMitchMilam USMember ✭✭✭

    @KentFonager I am not sure actually.

    Would it be possible to create a sample project that contains the code snippets you have shown and attach the zip here so that I could test it on my machine?

  • KentFonagerKentFonager DKBeta

    Hi Mitch

    I have now attached a sample project with the "component". I hope you maybe could figure out a way to make it work, as expected. What i want is that when the "trigger validator" has run, it calls the Setter on the "Validated" property, which in the end should trigger a "property change" in the platform renderer.

    Thanks for helping out, I really appreciate it, Mitch :-D

    Cheers,
    Kent

  • KentFonagerKentFonager DKBeta

    And here is the file, ups ;-)

  • KentFonagerKentFonager DKBeta

    I changes some stuff and now I see the expected behaviour. I have attached the corrected demo project, if anyone should be interested in trying it out.

Sign In or Register to comment.