Allow the Editor control to grow as content lines are added

I have been modifying the Todo app that uses Forms and replaced the note Entry class with the Editor class, so the todo can hold multiline notes. However the editor does not expand as new lines are entered. I have a Forms Editor Control nested inside a stack layout / scroll view - how do I allow the editor to grow each time the user enters another line of data? I had some success by count the '\n' characters in the text each time it changes and making a HeightRequest to the Editor, but I don't know how to calculate the exact extra height to add for each row in a reliable way. This process also feels clunky too. Can the editor control not be told to simply grow as new content is added by the user?

Posts

  • CraigDunnCraigDunn USXamarin Team Xamurai

    No, it can't just automatically grow unfortunately. That sounds like a very web-like user interface (I often see growing text boxes on the web; less frequently in apps, although I know the iOS Messages app for example does).

    There is no way to calculate the required height for an Editor in a cross platform way right now - you need to access the font metrics and calculate specific text wrapping results to know how 'high' a block of text will be. This might be do-able in a custom renderer, but you'll certainly need to calculate the height differently for each platform.

    Not sure that nesting inside a StackLayout/ScrollView is the best UI. What happens when the editor grows to near the height of the screen? The iOS Messages app at least lets the input box push other controls out of the way (until it gets to a certain size, and then it scrolls internally rather than grow too big; having a scrolling editor inside a scrolling view can get very confusing for the user).

  • SimonHewerdineSimonHewerdine GBMember ✭✭

    Thanks for the feedback - very helpful.

    Maybe it is a bit web-like but personally I find it easier than scrolling inside an edit field on a touch screen and ease of use is always my main aim. I take your point about a scrolling control within a scrollview. That's why I want the editor to resize to fit all the text within it. The editor need never scroll. Provided it is limited to a few lines of text (reasonable for a todo) the full screen issue does not arise and if it did it is a rare case, not a common one, so on balance I like it.

    I actually got a reasonable estimation of the line height by inheriting from the Editor like this...

    public class MyEditor : Editor
    {
            bool sized = false;
            public double lineHeight = 0;
    
            protected override void OnSizeAllocated(double width, double height)
            {
                if (!sized)
                {
                    int count = Text.Count(c => c == '\n');
                    lineHeight = (height / (count + 1));
                    sized = true;
                }
                base.OnSizeAllocated(width, height);
            }
    }
    

    and in the page doing this

    notesEntry.TextChanged += (sender, e) =>
                {
                    if (notesEntry.lineHeight != 0)
                    {
                        double newHeight;
                        int count = notesEntry.Text.Count(c => c == '\n');
                        if (count == 0)
                        {
                            newHeight = notesEntry.lineHeight;
                        }
                        else
                        {
                            newHeight = ((notesEntry.lineHeight) * (count + 1));
                        }
                        if (notesEntry.Height != newHeight)
                        {
                            notesEntry.HeightRequest = newHeight;
                        }
                    }
                };
    

    This over estimates the newHeight though, presumably because the lineHeight calculated includes some internal padding over and above the the height of a single line of text. Still even with the extra space it works OK for a demo.

    Thanks again.

  • SameerAlomariSameerAlomari USMember, University

    Hello Simon,

    Did you find a solution? I have the same problem.

    Sameer

  • SimonHewerdineSimonHewerdine GBMember ✭✭

    Hi Sameer,

    I have not looked at this in a while but no I did not :(

    However I did notice that when an edit field contains several lines of text before it is shown then when it is finally displayed it is large enough to fit the contents without scrolling, so the Forms layer is resizing it before showing it for the first time. It should therefore be possible in the notesEntry.TextChanged handler to detect if a new line has been added and when it has then create a new edit control with with to same contents, hide the old one and show the new one.
    I'm just not sure how it would look and function - you may have to force focus to the the new control and it may well flicker...

    Simon.

  • JohnHardmanJohnHardman GBUniversity mod
    edited May 2015

    @SimonHewerdine @SameerAlomari @CraigDunn

    This problem was annoying me on Windows Phone. However, I found that changing orientation a couple of times resulted in the Editor's size changing to the size I wanted. To avoid making the user wave the device around to get it to do the expected behaviour, I instead invalidate the Editor's layout, presumably forcing the layout process to be repeated. This does require creating a class that inherits from Editor. The below code, which originally was part of the Forms Gallery sample before I played with it (please forgive the color choices), shows an example of this. It's not perfect, as this simplistic version does not like other controls being beneath the Editor, and the focus can get hidden behind the keyboard as the user is appending text, but it's an improvement (ok, a slight improvement). Personally, I suspect I will end up replacing the standard Editor in my app, as it is currently fairly nasty to work with.

    using System;

    using Xamarin.Forms;

    namespace FormsGallery
    {
    public class MyEditor : Editor
    {
    public MyEditor() : base()
    {

        }
    
        public void InvalidateLayout()
        {
            this.InvalidateMeasure();
        }
    }
    
    class EditorDemoPage : ContentPage
    {
        private MyEditor editor;
    
        public EditorDemoPage()
        {
            Label header = new Label
            {
                BackgroundColor = Color.Fuchsia,
                TextColor = Color.Navy,
                Text = "My Editor",
                Font = Font.SystemFontOfSize(50, FontAttributes.Bold),
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.Start
            };
    
            editor = new MyEditor
            {
                BackgroundColor = Color.Gray,
                HorizontalOptions = LayoutOptions.Fill,
                VerticalOptions = LayoutOptions.FillAndExpand
            };
    
            // Build the page.
            this.Content = new StackLayout
            {
                BackgroundColor = Color.Lime,
                HorizontalOptions = LayoutOptions.Fill,
                VerticalOptions = LayoutOptions.Fill,
                Children =
                {
                    header,
                    new ScrollView
                    {
                        BackgroundColor = Color.Green,
                        HorizontalOptions = LayoutOptions.FillAndExpand,
                        VerticalOptions = LayoutOptions.FillAndExpand,
                        Orientation = ScrollOrientation.Vertical,
                        Content = editor
                    }
                }
            };
    
            editor.TextChanged += OnTextChanged;
        }
    
        private void OnTextChanged(Object sender, TextChangedEventArgs e)
        {
            editor.InvalidateLayout();
        }
    }
    

    }

  • SimonHewerdineSimonHewerdine GBMember ✭✭

    @JohnHardman thanks for that - it's a good workaround!

    I do agree that the current editor is not very nice to work with...

  • Ondra.7099Ondra.7099 USMember

    It is a common behaviour to expand textbox in mobile apps as text is written.
    On Android you can use EditText and set some basic properties.

    EditText et = ((EditText)findViewById(R.id.editText));
    et.setLayoutParams(new LinearLayout.LayoutParams(
         ViewGroup.LayoutParams.MATCH_PARENT, // layout width
         ViewGroup.LayoutParams.WRAP_CONTENT)); // layout height
    et.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE); 
    

    On iOS you can use TextView and achieve this by calling [textView setScrollEnabled:NO];.
    Isn't there a way how to achieve this with custom renderers? I tried to create a new renderer that uses Android.Widget.EditText as a control. (ExtendedEditor is a class derived from View and is used as an element in renderer.)

    public class ExtendedEditorRenderer : ViewRenderer<ExtendedEditor, EditText>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<ExtendedEditor> e)
        {
            base.OnElementChanged (e);
    
            if (e.OldElement != null || this.Element == null)
                return;
    
            if (Control == null) {
                EditText editText = new EditText (Forms.Context);
                editText.LayoutParameters = new LayoutParams (LayoutParams.MatchParent, LayoutParams.WrapContent);
                editText.InputType = InputTypes.ClassText | InputTypes.TextFlagMultiLine;
                SetNativeControl (editText);
            }
        }
    }
    

    But this does not work, the heigh of EditText is static (one line in this case). I haven't tried iOS renderer yet.

  • Ondra.7099Ondra.7099 USMember

    The best solution is the one described here.

  • PhamThanhTongPhamThanhTong USMember ✭✭
    edited April 2017

    I have the same problem.
    Have anyone found solution that using EditText instead of editor in Xamarin Android?
    I intend to create new editor control via renderer: UITextView for iOS and EditText for Android.
    Thanks!

  • JackPot1995JackPot1995 ILMember ✭✭

    Does someone have a working solution for UWP?

    I tried using InvalidateMeasure on TextChanged event and this is what I got:

    It seems that the editor doesn't expand enough.

  • DaveYDaveY USUniversity ✭✭
    edited May 2018

    This is almost a year old...another old post and it is hard to know if there is recent release that addresses this or is this the best answer with todays' xf 3.x

  • sumitmishrasumitmishra Member ✭✭

    Hi ,
    Use this code.. its worked for me..

    textview.Changed += (object sender, EventArgs e) =>

    {

    //Action

    var NewHeight = txtMsgToSend.ContentSize.Height;

    textview.Frame = new CGRect(x:textview.Frame.X, y: textview.Frame.Y, width:
    textview.Frame.Width, height: NewHeight);
 

    }
 

    };

  • colinedwardscolinedwards USMember ✭✭

    a year old? based on the first post it's nearly 5 years old and still not solved...
    so.. you might have be able to argue.. well, it's for mobile, who want to write that amount of text?

    so, today it's not about mobile, it's about 'forms' everywhere.. so, it needs fixing, not 'codging'..

  • colinedwardscolinedwards USMember ✭✭

    found it... autosize="textchanges" => bang it in a scrollview.. nothing beats compatibility (with existing tech), does it.. (WPF 13 years...)

Sign In or Register to comment.