title: Why won't my Xamarin forms custom editor renderer do anything
body: <p>I am trying to make a custom renderer for an editor that changes the &quot;return&quot; key to a &quot;done&quot; button and fires the Completed event when you tap it instead of typing a newline. The code in OnElementChanged() is being hit, but it's not doing anything. The &quot;return&quot; key is still a &quot;return&quot; key and it still types newlines instead of making the editor go out of focus. What am I doing wrong?</p>
  <p>Here is the class for the custom editor (in the .NET project):</p>
  <pre><code>using Xamarin.Forms;
  
  namespace Partylist.Custom_Controls
  {
      public class ChecklistEditor : Editor
      {
  
      }
  }
  </code></pre>
  <p>Here is the custom renderer for Android:</p>
  <pre><code>using Android.Content;
  using Android.Runtime;
  using Android.Views;
  using Android.Views.InputMethods;
  using Android.Widget;
  
  using Partylist.Custom_Controls;
  using Partylist.Droid.Custom_Renderers;
  
  using Xamarin.Forms;
  using Xamarin.Forms.Platform.Android;
  
  [assembly: ExportRenderer(typeof(ChecklistEditor), typeof(ChecklistEditorRenderer))]
  namespace Partylist.Droid.Custom_Renderers
  {
      class ChecklistEditorRenderer : EditorRenderer
      {
          // Constructor because it needs to exist.
          public ChecklistEditorRenderer(Context context) : base(context)
          {
  
          }
  
          // This gets overridden so I can change what I want to change.
          protected override void OnElementChanged(ElementChangedEventArgs
              &lt;Editor&gt; e)
          {
              // Make it do what is should normally do so it will exist.
              base.OnElementChanged(e);
              // Make the &quot;Return&quot; button on the keyboard be a &quot;Done&quot; button.
              Control.ImeOptions = ImeAction.Done;
              // Make the thing watch for when the user hits the &quot;Return&quot; button.
              Control.EditorAction += OnEditorAction;
          }
  
          // This makes the &quot;Return&quot; button fire the &quot;Completed&quot; event 
          // instead of typing a newline.
          private void OnEditorAction(object sender, TextView
              .EditorActionEventArgs e)
          {
              e.Handled = false;
              if (e.ActionId == ImeAction.Done)
              {
                  Control.ClearFocus();
                  e.Handled = true;
              }
          }
      }
  }
  </code></pre>
  <p>Here is the custom renderer for iOS:</p>
  <pre><code>using Foundation;
  using Partylist.Custom_Controls;
  using Partylist.iOS.Custom_Renderers;
  using UIKit;
  using Xamarin.Forms;
  using Xamarin.Forms.Platform.iOS;
  
  [assembly: ExportRenderer(typeof(ChecklistEditor), typeof(ChecklistEditorRenderer))]
  namespace Partylist.iOS.Custom_Renderers
  {
      class ChecklistEditorRenderer : EditorRenderer
      {
          protected override void OnElementChanged(ElementChangedEventArgs
              &lt;Editor&gt; e)
          {
              base.OnElementChanged(e);
  
              Control.ReturnKeyType = UIReturnKeyType.Done;
          }
  
  
          protected override bool ShouldChangeText(UITextView textView, 
              NSRange range, string text)
          {
              if (text == &quot;\n&quot;)
              {
                  textView.ResignFirstResponder();
                  return false;
              }
  
              return true;
          }
      }
  }
  </code></pre>
  <p>The code-behind for the page where I'm using these custom renderers (there's nothing in the XAML that should conflict with it, I think, but I'll add it to the post if people want to make sure):</p>
  <pre><code>using Partylist.Custom_Controls;
  
  using System;
  using System.Collections.ObjectModel;
  using System.IO;
  using System.Linq;
  using System.Threading.Tasks;
  
  using Xamarin.Forms;
  using Xamarin.Forms.Xaml;
  
  namespace Partylist.Views
  {
      [XamlCompilation(XamlCompilationOptions.Compile)]
      public partial class ChecklistPage : ContentPage
      {
          // Struct for items on the checklist.
          struct Item
          {
              public ChecklistEditor ItemEditor { get; set; }
              public CheckBox ItemCheckbox { get; set; }
          }
          // Create a list of contact structs to populate the ListView.
          ObservableCollection&lt;Item&gt; items;
          // Flag for when an item is added to the list.
          bool itemAdded = false;
  
          // Constructor.
          public ChecklistPage()
          {
              // Whatever setup stuff it was going to do anyway.
              InitializeComponent();
              // Set the label's BindingContext to the 
              // App class so it can update its text.
              tipLabel.BindingContext = (App)App.Current;
          }
  
          // Override for OnAppearing().
          protected override void OnAppearing()
          {
              // Makes the page appear.
              base.OnAppearing();
              // Set the page's title to be the name of the selected list.
              Title = App.selectedList.Name;
              // Make a toolbar item appear to access the Main Checklist
              // unless we are already there.
              if (App.selectedList.ListFile.Name.EndsWith(&quot;.mchec&quot;))
              {
                  ToolbarItems.Remove(MainChecklistButton);
              }
              // Set the binding context of the page to itself.
              BindingContext = this;
              // Start the timer for the tips banner if it is stopped.
              App.tipTimer.Start();
              // Set the banner's text to the current tip's sumamry.
              tipLabel.Text = ((App)App.Current).CurrentTip.Summary;
              OnPropertyChanged(&quot;CurrentTip&quot;);
              // Subscribe the OnTipUpdate function to the tipUpdate event in the app
              // class.
              App.TipUpdate += OnTipUpdate;
  
              // Make the ObservableCOllection reference something.
              items = new ObservableCollection&lt;Item&gt;();
              // Open a stream to the list that we want to display.
              using (StreamReader listReader = new StreamReader(App.selectedList
                  .ListFile.FullName))
              {
                  // Loop through the file and read data into the list.
                  while (!listReader.EndOfStream)
                  {
                      // Create a blank item.
                      Item newItem = new Item()
                      {
                          ItemEditor = new ChecklistEditor()
                          {
                              Text = listReader.ReadLine(),
                              Placeholder = &quot;New Item&quot;,
                              IsTabStop = true,
                              AutoSize = EditorAutoSizeOption.TextChanges,
                              WidthRequest = 310
                          },
                          ItemCheckbox = new CheckBox()
                          {
                              Color = App.selectedList.ListItemColor,
                              IsChecked = bool.Parse(listReader.ReadLine())
                          }
                      };
                      // Subscribe OnCompleted() to the new item's &quot;Completed&quot; 
                      // event.
                      newItem.ItemEditor.Completed += OnCompleted;
                      // Subscribe OnTextChanged() to the new item's 
                      // &quot;TextChanged&quot; event.
                      newItem.ItemEditor.TextChanged += OnTextChanged;
                      // Add the new item to the list.
                      items.Add(newItem);
                      // Make the ListView update.
                      ChecklistView.ItemsSource = items;
                      OnPropertyChanged(&quot;contacts&quot;);
                  }
                  // Once everything is loaded, close the file.
                  listReader.Close();
              }
          }
  
          // Override for OnDisappearing().
          protected override void OnDisappearing()
          {
              // Makes the page disappear.
              base.OnDisappearing();
              // Open a stream to the file for the list.
              StreamWriter listWriter = new StreamWriter(App.selectedList
                  .ListFile.FullName);
              // Loop through the contacts list to write the contacts to the 
              // file.
              for (int i = 0; i &lt; items.Count; i++)
              {
                  // Write each item to the file.
                  listWriter.WriteLine(items.ElementAt(i).ItemEditor.Text);
                  listWriter.WriteLine(items.ElementAt(i).ItemCheckbox.IsChecked);
              }
              // Close the stream.
              listWriter.Close();
          }
  
          // Function for when the &quot;Add New Contact&quot; button is clicked.
          private void OnAddNewItemClicked(object sender, EventArgs e)
          {
              // Create a blank item.
              Item newItem = new Item()
              {
                  ItemEditor = new ChecklistEditor()
                  {
                      Placeholder = &quot;New Item&quot;,
                      IsTabStop = true,
                      AutoSize = EditorAutoSizeOption.TextChanges,
                      WidthRequest = 310
                  },
                  ItemCheckbox = new CheckBox()
                  {
                      Color = App.selectedList.ListItemColor,
                      IsChecked = false
                  }
              };
              // Subscribe OnCompleted() to the new item's &quot;Completed&quot; 
              // event.
              newItem.ItemEditor.Completed += OnCompleted;
              // Subscribe OnTextChanged() to the new item's 
              // &quot;TextChanged&quot; event.
              newItem.ItemEditor.TextChanged += OnTextChanged;
              // Add the new contact to the list.
              items.Add(newItem);
              // Set the &quot;itemAdded&quot; flag to true.
              itemAdded = true;
              // Make the ListView update.
              ChecklistView.ItemsSource = items;
              OnPropertyChanged(&quot;contacts&quot;);
              // Select the new item.
              ChecklistView.SelectedItem = items.ElementAt(items.Count - 1);
          }
  
          // Function for when an item is selected, used to set the focus to
          // a newly added item in the list.
          private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
          {
              // Only runs this if an item was added (as opposed to being 
              // read in from the file).
              if (itemAdded)
              {
                  if (e.SelectedItem == null) return;
                  await Task.Delay(100); // Change the delay time if Focus() doesn't work.
                  ((Item)e.SelectedItem).ItemEditor.Focus();
                  ChecklistView.SelectedItem = null;
                  itemAdded = false;
              }
          }
  
          // Function for when the user presses &quot;Return&quot; on the keyboard in
          // an editor.
          private void OnCompleted(object sender, EventArgs e)
          {
              // We just want to unfocus the editor.
              ((Editor)sender).Unfocus();
          }
  
          // Function for when the user types anything in the editor, used 
          // to make sure it resizes.
          private void OnTextChanged(object sender, TextChangedEventArgs e)
          {
              // Makes the cell resize. The cell is the parent of the 
              // StackLayout which is the parent of the ContentView which is
              // the parent of the Editor that fired the event.
              ((ViewCell)((Editor)sender).Parent.Parent.Parent)
                  .ForceUpdateSize();
          }
      }
  }
  </code></pre>