Is it possible to have overtype functionality in Entry

LapsusLapsus USMember ✭✭

Hi,

I would like my entry to behave in overtype mode. Like when you press insert key in windows, and the type, the letter that you type will replace the letters instead of appending in between of that. Is it possible to achieve something like that with Entry? Maybe by using a custom renderer or something like that?

Thank you for your insights.
BR,
Denis

Best Answers

  • LapsusLapsus US ✭✭
    Accepted Answer

    I have managed to achieve the required functionality by writing a delegate for TextChanged event (note that since I'm using the carret position, this will work only in Xamarin.Forms >= 3.2)

    So this is the method:

    private async void OnOvertypeTextChanged(object sender, TextChangedEventArgs e)
    {
        var entry = sender as Entry;
        try
        {
            entry.TextChanged -= OnOvertypeTextChanged;
            //Update value as in overtype mode
            var oldVal = e.OldTextValue;
            var newVal = e.NewTextValue;
            int? carret = null;
            //If new string is longer, we should go into overtype mode, otherwise ignore it
            if(newVal?.Length > oldVal?.Length)
            {
                for(int i = 0; i < oldVal.Length; i++)
                {
                    //We will assume this is a one character change
                    if(newVal[i] != oldVal[i])
                    {
                        var overtyped = oldVal.Substring(0, i) + newVal[i] + oldVal.Substring(i+1);
                        entry.Text = overtyped;
                        carret = i+1;
                        break;
                    }
                }
            }
            //For some reason, this is needed...
            await Task.Yield();
            if(carret != null)
            {
                entry.CursorPosition = carret.Value;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}", ex);
        }
        finally
        {
            entry.TextChanged += OnOvertypeTextChanged;
        }
    }
    

    And to use it, I created two extension methods on the Entry class, one for enabling overtype and one for disabling it.

    public static void EnableOvertype(this Entry field)
    {
        field.TextChanged += OnOvertypeTextChanged;
    }
    
    public static void DisableOvertype(this Entry field)
    {
        field.TextChanged -= OnOvertypeTextChanged;
    }
    

    So now I can enable/disable overtype mode on any Entry I want :)

    TestEntry.EnableOvertype();
    TestEntry.DisableOvertype();
    
  • LapsusLapsus US ✭✭
    edited December 2018 Accepted Answer

    @Lapsus said:
    I have managed to achieve the required functionality by writing a delegate for TextChanged event (note that since I'm using the carret position, this will work only in Xamarin.Forms >= 3.2)

    So this is the method:

    private async void OnOvertypeTextChanged(object sender, TextChangedEventArgs e)
    {
      var entry = sender as Entry;
      try
      {
          entry.TextChanged -= OnOvertypeTextChanged;
          //Update value as in overtype mode
          var oldVal = e.OldTextValue;
          var newVal = e.NewTextValue;
          int? carret = null;
          //If new string is longer, we should go into overtype mode, otherwise ignore it
          if(newVal?.Length > oldVal?.Length)
          {
              for(int i = 0; i < oldVal.Length; i++)
              {
                  //We will assume this is a one character change
                  if(newVal[i] != oldVal[i])
                  {
                      var overtyped = oldVal.Substring(0, i) + newVal[i] + oldVal.Substring(i+1);
                      entry.Text = overtyped;
                      carret = i+1;
                      break;
                  }
              }
          }
          //For some reason, this is needed...
          await Task.Yield();
          if(carret != null)
          {
              entry.CursorPosition = carret.Value;
          }
      }
      catch (Exception ex)
      {
          Console.WriteLine("Exception caught: {0}", ex);
      }
      finally
      {
          entry.TextChanged += OnOvertypeTextChanged;
      }
    }
    

    And to use it, I created two extension methods on the Entry class, one for enabling overtype and one for disabling it.

    public static void EnableOvertype(this Entry field)
    {
      field.TextChanged += OnOvertypeTextChanged;
    }
    
    public static void DisableOvertype(this Entry field)
    {
      field.TextChanged -= OnOvertypeTextChanged;
    }
    

    So now I can enable/disable overtype mode on any Entry I want :)

    TestEntry.EnableOvertype();
    TestEntry.DisableOvertype();
    

    So I was having some issues with the following code, and I did a revision of the OnOvertypeTextChanged. Please use the provided code instead:

    private static void OnOvertypeTextChanged(object sender, TextChangedEventArgs e)
    {
        Device.BeginInvokeOnMainThread(() => 
        {
            var guid = Guid.NewGuid();
            var entry = sender as Entry;
            try
            {
                entry.TextChanged -= OnOvertypeTextChanged;
                //Update value as in overtype mode
                var oldVal = e.OldTextValue;
                var newVal = e.NewTextValue;
                int? carret = null;
                var i = entry.CursorPosition - 1;
                //If new string is longer, we should go into overtype mode, otherwise ignore it
                //But we shouldn't perform overtype, when inserting at the end of the string
                if (newVal?.Length > oldVal?.Length && i < newVal?.Length)
                {
    
                    var overtyped = oldVal.Substring(0, i) + newVal[i] + ((oldVal.Length > (i + 1)) ? oldVal.Substring(i + 1) : "");
                    entry.Text = overtyped;
                    carret = i + 1;
                }
                //Check if new overtyped value is longer than max allowed size
                if (entry.OvertypeLength != -1 && entry.Text.Length > entry.OvertypeLength)
                {
                    var cut = entry.Text.Substring(0, entry.OvertypeLength);
                    //If new text would be longer, then cut it
                    entry.Text = cut;
                    if (carret >= cut.Length)
                    {
                        carret = cut.Length;
                    }
                }
                if (carret != null)
                {
                    entry.CursorPosition = carret.Value;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"{DateTime.Now.ToString()}: ERROR {guid.ToString()}", ex);
            }
            finally
            {
                entry.TextChanged += OnOvertypeTextChanged;
            }
        });
    }
    

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Maybe by using a custom renderer

    I don't think that about how to render... Its about the data... So you'd have to handle the keybpresses and update the actual value of the data...

    If at position 4 and a key is pressed update the string the Entry is binded to by taking the left substring, add the one character typed, add the right substring.

    You can do that at the shared level which is nice because then all the platforms do the same without platform level code. But you have to work the data through string manipulation.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭
    edited November 2018

    Or... Maybe you can just manipulate the keyboard to include having pressed the Insert key like you mentioned - then have the OS do it all for you.

  • LapsusLapsus USMember ✭✭
    Accepted Answer

    I have managed to achieve the required functionality by writing a delegate for TextChanged event (note that since I'm using the carret position, this will work only in Xamarin.Forms >= 3.2)

    So this is the method:

    private async void OnOvertypeTextChanged(object sender, TextChangedEventArgs e)
    {
        var entry = sender as Entry;
        try
        {
            entry.TextChanged -= OnOvertypeTextChanged;
            //Update value as in overtype mode
            var oldVal = e.OldTextValue;
            var newVal = e.NewTextValue;
            int? carret = null;
            //If new string is longer, we should go into overtype mode, otherwise ignore it
            if(newVal?.Length > oldVal?.Length)
            {
                for(int i = 0; i < oldVal.Length; i++)
                {
                    //We will assume this is a one character change
                    if(newVal[i] != oldVal[i])
                    {
                        var overtyped = oldVal.Substring(0, i) + newVal[i] + oldVal.Substring(i+1);
                        entry.Text = overtyped;
                        carret = i+1;
                        break;
                    }
                }
            }
            //For some reason, this is needed...
            await Task.Yield();
            if(carret != null)
            {
                entry.CursorPosition = carret.Value;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception caught: {0}", ex);
        }
        finally
        {
            entry.TextChanged += OnOvertypeTextChanged;
        }
    }
    

    And to use it, I created two extension methods on the Entry class, one for enabling overtype and one for disabling it.

    public static void EnableOvertype(this Entry field)
    {
        field.TextChanged += OnOvertypeTextChanged;
    }
    
    public static void DisableOvertype(this Entry field)
    {
        field.TextChanged -= OnOvertypeTextChanged;
    }
    

    So now I can enable/disable overtype mode on any Entry I want :)

    TestEntry.EnableOvertype();
    TestEntry.DisableOvertype();
    
  • hydrogenhydrogen Member ✭✭

    Oh, thank you very much for the answer!

  • LapsusLapsus USMember ✭✭
    edited December 2018 Accepted Answer

    @Lapsus said:
    I have managed to achieve the required functionality by writing a delegate for TextChanged event (note that since I'm using the carret position, this will work only in Xamarin.Forms >= 3.2)

    So this is the method:

    private async void OnOvertypeTextChanged(object sender, TextChangedEventArgs e)
    {
      var entry = sender as Entry;
      try
      {
          entry.TextChanged -= OnOvertypeTextChanged;
          //Update value as in overtype mode
          var oldVal = e.OldTextValue;
          var newVal = e.NewTextValue;
          int? carret = null;
          //If new string is longer, we should go into overtype mode, otherwise ignore it
          if(newVal?.Length > oldVal?.Length)
          {
              for(int i = 0; i < oldVal.Length; i++)
              {
                  //We will assume this is a one character change
                  if(newVal[i] != oldVal[i])
                  {
                      var overtyped = oldVal.Substring(0, i) + newVal[i] + oldVal.Substring(i+1);
                      entry.Text = overtyped;
                      carret = i+1;
                      break;
                  }
              }
          }
          //For some reason, this is needed...
          await Task.Yield();
          if(carret != null)
          {
              entry.CursorPosition = carret.Value;
          }
      }
      catch (Exception ex)
      {
          Console.WriteLine("Exception caught: {0}", ex);
      }
      finally
      {
          entry.TextChanged += OnOvertypeTextChanged;
      }
    }
    

    And to use it, I created two extension methods on the Entry class, one for enabling overtype and one for disabling it.

    public static void EnableOvertype(this Entry field)
    {
      field.TextChanged += OnOvertypeTextChanged;
    }
    
    public static void DisableOvertype(this Entry field)
    {
      field.TextChanged -= OnOvertypeTextChanged;
    }
    

    So now I can enable/disable overtype mode on any Entry I want :)

    TestEntry.EnableOvertype();
    TestEntry.DisableOvertype();
    

    So I was having some issues with the following code, and I did a revision of the OnOvertypeTextChanged. Please use the provided code instead:

    private static void OnOvertypeTextChanged(object sender, TextChangedEventArgs e)
    {
        Device.BeginInvokeOnMainThread(() => 
        {
            var guid = Guid.NewGuid();
            var entry = sender as Entry;
            try
            {
                entry.TextChanged -= OnOvertypeTextChanged;
                //Update value as in overtype mode
                var oldVal = e.OldTextValue;
                var newVal = e.NewTextValue;
                int? carret = null;
                var i = entry.CursorPosition - 1;
                //If new string is longer, we should go into overtype mode, otherwise ignore it
                //But we shouldn't perform overtype, when inserting at the end of the string
                if (newVal?.Length > oldVal?.Length && i < newVal?.Length)
                {
    
                    var overtyped = oldVal.Substring(0, i) + newVal[i] + ((oldVal.Length > (i + 1)) ? oldVal.Substring(i + 1) : "");
                    entry.Text = overtyped;
                    carret = i + 1;
                }
                //Check if new overtyped value is longer than max allowed size
                if (entry.OvertypeLength != -1 && entry.Text.Length > entry.OvertypeLength)
                {
                    var cut = entry.Text.Substring(0, entry.OvertypeLength);
                    //If new text would be longer, then cut it
                    entry.Text = cut;
                    if (carret >= cut.Length)
                    {
                        carret = cut.Length;
                    }
                }
                if (carret != null)
                {
                    entry.CursorPosition = carret.Value;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"{DateTime.Now.ToString()}: ERROR {guid.ToString()}", ex);
            }
            finally
            {
                entry.TextChanged += OnOvertypeTextChanged;
            }
        });
    }
    
Sign In or Register to comment.