1
votes

I have extendedentry and custom renderer classes

public class ExtendedEntryRenderer : EntryRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {
            Control.Click += (sender, evt) =>
            {
                var nativeEditText = (global::Android.Widget.EditText)Control;
                nativeEditText.SetSelectAllOnFocus(true);

                new Handler().PostDelayed(delegate
                {
                     Control.InputType = 0;
                     try
                     {
                         InputMethodManager inputMethodManager = Control.Context.GetSystemService(Android.Content.Context.InputMethodService) as InputMethodManager;
                         if (inputMethodManager != null)
                         {
                             inputMethodManager.HideSoftInputFromWindow(Control.WindowToken, HideSoftInputFlags.None);
                         }
                     }
                     catch (Exception Ex)
                     {

                     }
                 }, 300L);
            };


            // Hide soft input keyboard
            Control.FocusChange += (sender, evt) =>
            {
                // Select all on entry focus
                var nativeEditText = (global::Android.Widget.EditText)Control;

                nativeEditText.SetSelectAllOnFocus(true);

                new Handler().PostDelayed(delegate
                 {
                     Control.InputType = 0;
                     try
                     {
                         InputMethodManager inputMethodManager = Control.Context.GetSystemService(Android.Content.Context.InputMethodService) as InputMethodManager;
                         if (inputMethodManager != null)
                         {
                            inputMethodManager.HideSoftInputFromWindow(Control.WindowToken, HideSoftInputFlags.None);
                         }
                     }
                     catch (Exception Ex)
                     {

                     }
                 }, 300L);
            };
        }
    }
}

My custom entry:

public class ExtendedEntry : Entry
{

}

And my XAML file is like this:

<local:ExtendedEntry x:Name="BarcodeEntry" Text="{Binding Barcode}" Margin="0,0,0,0" HorizontalOptions="FillAndExpand" VerticalOptions="Start">
    <Entry.Triggers>
        <Trigger TargetType="Entry" Property="IsFocused" Value="True">
            <Setter Property="BackgroundColor" Value="Yellow" />
            <Setter Property="TextColor" Value="Black" />
        </Trigger>
    </Entry.Triggers>       
</local:ExtendedEntry>

Trigger doesn't work.

But when I change to default entry control like this:

<Entry x:Name="BarcodeEntry" Text="{Binding Barcode}" Margin="0,0,0,0"HorizontalOptions="FillAndExpand" VerticalOptions="Start">
    <Entry.Triggers>
        <Trigger TargetType="Entry" Property="IsFocused" Value="True">
            <Setter Property="BackgroundColor" Value="Yellow" />
            <Setter Property="TextColor" Value="Black" />
        </Trigger>
    </Entry.Triggers>       
</Entry>

The Trigger works.

What did i wrong?

Edit

The ExportRenderer row from Xamarin.Forms is in my renderer class.

[assembly: ExportRenderer(typeof(ExtendedEntry), typeof(ExtendedEntryRenderer))]
2

2 Answers

0
votes
  1. You don't have to do below every event. You can do it only once

    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);
    
        if (e.NewElement != null)
        {
            var nativeEditText = (global::Android.Widget.EditText)Control;
            // Select all on entry focus
            nativeEditText.SetSelectAllOnFocus(true);
    
  2. You have identical code in Control.Click and Control.FocusChange. You might want to consider somehow to take it to separate function. Keep in mind that FocusChange is called also when focus is lost, so you might want to check

    if (evt.HasFocus)
    
  3. When Control is created its OnFocusChangeListener is set to ExtendedEntryRenderer. But as soon as you call Control.FocusChange+= it is changing to mono.android.view.View_OnFocusChangeListenerImplementor@a25f36e and apparently that causing problems.

Commment out Control.FocusChange and it will work. When user sets focus to your Entry you will get Click event which should be enough. You will not know when focus is lost thou if it is required.

0
votes

Unfortunately, the Entry / EntryRenderer system in Xamarin is very problematic which makes it inordinately difficult to add effects. Worse, I created a custom renderer which worked, but would sometimes cause a freeze as the underlying entry control was added to a layout. I still have a freeze if I add two different effects to an entry, but I am able to get focus firing and use a single effect successfully.

To avoid the loss of the original focus handler, I grabbed it with:

private Android.Views.View.IOnFocusChangeListener focusListener = null;

private void ConfigureControl()
{
  EditText editText;

  editText = ((EditText)Control);
  this.focusListener = Control.OnFocusChangeListener;
  editText.FocusChange += EditText_FocusChange;

...

private void EditText_FocusChange(object sender, Android.Views.View.FocusChangeEventArgs e)
{
  EditText editText;

  editText = (EditText) sender;
  ...
  if (this.focusListener != null)
  {
    this.focusListener.OnFocusChange(editText,
      e.HasFocus);
  }

This allows the effect to operate and also fires the focus events back to the xamarin form. Unfortunately, doing this in two different effects did not work -- possibly because the mono.android.view.View_OnFocusChangeListenerImplementor@a25f36e handler either doesn't cast to Android.Views.View.IOnFocusChangeListener or it does but some internal bug is going into an endless loop.

I was using an effect to add an 'x' to clear android entry, and I was attempting to also use an effect I created to select all on focus. Both are obvious candidates for inclusion into Xamarin as basic options needed far too frequently to justify leaving them to effects or custom renderers. If you are trying to achieve the same thing, you can do what I did -- add the effect to add the 'x' and then instead of an effect to do a very simple select all, you can achieve that with this less pretty but functional code back in the xamarin view:

  entry.Focused += (s, a) =>
  {
    Device.StartTimer(TimeSpan.FromMilliseconds(1),
      () =>
      {
        entry.CursorPosition = 0;
        entry.SelectionLength = (entry.Text ?? string.Empty).Length;
        return false;
      });
    ...
  };
  entry.Unfocused += (s, a) =>
  {
    ...
  };

I hope that saves someone the hours of wasted custom renderer development and endless experiments with effects until I finally got something so frustratingly basic to work reliably.