Forum Xamarin.Forms

Extending the Entry class to add bindable properties?

Joshua.R.RussoJoshua.R.Russo USUniversity ✭✭
edited February 2017 in Xamarin.Forms

I would like to add a HasFocus property to the Entry class

public class EntryEnhancedBinding : Entry
{
    public bool HasFocus
    {
        get { return IsFocused; }
        set
        { 
            if (value)
                Focus();
        }
    }
}

When I try to execute the page though I get the following exception:

FreshTinyIoC.TinyIoCResolutionException: Resolve failed: LoginPage - Reason: Position 21:18. Cannot assign property "HasFocus": Property does not exists, or is not assignable, or mismatching type between value and property

I've also tried adding the [ImplementPropertyChanged] from the Frody library to the class but I still get the same error.

How would I extend UI controls to do this type of operation in general, or is this not allowed?

Posts

  • JohnDBJohnDB USMember ✭✭

    If you want to create properties in your user control that you can bind to then you need to declare the property as a BindableProperty. For example:

    public static readonly BindableProperty HasFocusProperty = BindableProperty.Create(nameof(HasFocus), typeof(bool), typeof(EntryEnhancedBinding), false, propertyChanged: (b, o, n)=>
    {
        if ((bool)n)
        {
            ((EntryEnhancedBinding)b).Focus();
        }
    });
    
    public bool HasFocus
    {
        get { return (bool)GetValue(HasFocusProperty); }
        set { SetValue(HasFocusProperty, value); }
    }
    

    have a look here: https://developer.xamarin.com/guides/xamarin-forms/xaml/bindable-properties/

  • Joshua.R.RussoJoshua.R.Russo USUniversity ✭✭

    Of course that's what I was missing. Thank you. I've worked with custom controls before, I just forgot that part

    For anyone who cares, this is what I ended up with so far:

    public class EntryEnhancedBinding : Entry
    {
        public static readonly BindableProperty HasFocusProperty = BindableProperty.Create(
            nameof(HasFocus),
            typeof(bool),
            typeof(EntryEnhancedBinding),
            false,
            propertyChanged: (entry, oldValue, newValue) =>
            {
                var enhancedEntry = (EntryEnhancedBinding)entry;
                if ((bool)newValue && !enhancedEntry.IsFocused)
                    enhancedEntry.Focus();
            });
    
        public bool HasFocus
        {
            get
            {
                var boundValue = (bool)GetValue(HasFocusProperty);
                if (boundValue != IsFocused)
                    SetValue(HasFocusProperty, IsFocused);
                return IsFocused;
            }
            set { SetValue(HasFocusProperty, value); }
        }
    
        public EntryEnhancedBinding()
        {
            Focused += EntryEnhancedBinding_Focused;
        }
    
        private void EntryEnhancedBinding_Focused(object sender, FocusEventArgs e)
        {
            SetValue(HasFocusProperty, e.IsFocused);
        }
    }
    
  • JohnHardmanJohnHardman GBUniversity mod

    @Joshua.R.Russo - Just an observation, so feel free to ignore :-) There is a good reason why Xamarin supply a getter but not a setter for IsFocused. Whilst I can think of a situation where it might be nice to have a setter for this, there are various issues about creating one in a cross-platform world. Unless you are building this as part of a framework that manages multiple views on a page, be aware that the semantics are likely to get confusing for anybody re-using the code, and the behavior unpredictable where multiple views on a page use this (not helped by an outstanding bug in setting focus on Android).

  • Joshua.R.RussoJoshua.R.Russo USUniversity ✭✭

    @JohnHardman I figured there was some reason for the lack of this functionality. I had previously used messages to trigger the Focus() method in the View from the ViewModel. Does my original approach also encounter the challenges you are referring to?

  • JohnHardmanJohnHardman GBUniversity mod

    @Joshua.R.Russo - I would expect triggering using a message to work, although depending on the scenario you might find that you need to do the following on receipt of the message:

    unfocus (remove focus from view that has it currently),
    scroll (to ensure that the view you want to have the focus is visible),
    focus (set the focus where you want it to go).
    
  • Joshua.R.RussoJoshua.R.Russo USUniversity ✭✭

    @JohnHardman Thanks. My scenario isn't so complex at the moment. It's just setting focus on user name / password on login, and a search field on the open of the search page. So no scrolling.

    As for the removing of focus. Is that something you can only do in platform specific code?

  • JohnHardmanJohnHardman GBUniversity mod
    edited February 2017

    @Joshua.R.Russo - Even on something as simple as a logon page, test on an Android device with a physical keyboard (I have a Bluetooth keyboard that I use with pretty much any device). Also, remember to test with the device in landscape orientation.

    The removing of focus is simply a call to Unfocus() on your View, which is not platform-specific.

    For setting the initial focus when a page opens, do it in OnAppearing (or in a message handler) rather than the page constructor.

  • Joshua.R.RussoJoshua.R.Russo USUniversity ✭✭

    @JohnHardman Thanks for all of that! I didn't notice the Unfocus() method before. And I will make sure to try my functionality out on a physical keyboard

  • LucioMSPLucioMSP MXUniversity ✭✭✭
    edited February 2017

    My two cents:

    <Entry x:Name="demoEntry" Focused="Entry_Focused" Unfocused="Entry_Unfocused" Keyboard="Numeric" />

    private void Entry_Focused(object sender, FocusEventArgs e)
            {
                // Your code when focus           
            }
    
    
            private void Entry_Unfocused(object sender, FocusEventArgs e)
            {           
                  // Your code when unfocus
            }
    
  • Joshua.R.RussoJoshua.R.Russo USUniversity ✭✭

    @LucioMSP My goal wasn't to trigger code when the events happen, but to trigger the Entry.Focus() call from the view model

  • MarlonRibeiroMarlonRibeiro USMember ✭✭✭

    @LucioMSP Is it possible to bind an ICommand property from the ViewModel to the Focused event?
    I'm trying to do exactly the same (but on an editor), but that's what I've got:
    " Cannot assign property "Focused": Property does not exists, or is not assignable, or mismatching type between value and property "

  • LucioMSPLucioMSP MXUniversity ✭✭✭
  • MarlonRibeiroMarlonRibeiro USMember ✭✭✭

    Thanks @LucioMSP, but I ended up writing my own FocusableEditor control.

  • LucioMSPLucioMSP MXUniversity ✭✭✭

    In fact is what I would recommend, create your own control, since there are things that even Xamarin does not allow in a native way.

    Regards!

Sign In or Register to comment.